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 {
1551 return (flags & Flags.AddressTaken) != 0;
1555 public Block Block {
1561 public Constant ConstantValue {
1566 const_value = value;
1571 // Hoisted local variable variant
1573 public HoistedVariable HoistedVariant {
1575 return hoisted_variant;
1578 hoisted_variant = value;
1582 public bool IsDeclared {
1584 return type != null;
1588 public bool IsConstant {
1590 return (flags & Flags.Constant) != 0;
1594 public bool IsLocked {
1596 return (flags & Flags.IsLocked) != 0;
1599 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1603 public bool IsThis {
1605 return (flags & Flags.IsThis) != 0;
1609 public bool IsFixed {
1611 return (flags & Flags.FixedVariable) != 0;
1615 public bool IsReadonly {
1617 return (flags & Flags.ReadonlyMask) != 0;
1621 public Location Location {
1627 public string Name {
1633 public TypeSpec Type {
1644 public void CreateBuilder (EmitContext ec)
1646 if ((flags & Flags.Used) == 0) {
1647 if (VariableInfo == null) {
1648 // Missing flow analysis or wrong variable flags
1649 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1652 if (VariableInfo.IsEverAssigned)
1653 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1655 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1658 if (HoistedVariant != null)
1661 if (builder != null) {
1662 if ((flags & Flags.CompilerGenerated) != 0)
1665 // To avoid Used warning duplicates
1666 throw new InternalErrorException ("Already created variable `{0}'", name);
1670 // All fixed variabled are pinned, a slot has to be alocated
1672 builder = ec.DeclareLocal (Type, IsFixed);
1673 if (SymbolWriter.HasSymbolWriter)
1674 ec.DefineLocalVariable (name, builder);
1677 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1679 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1684 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1686 if (IsConstant && const_value != null)
1687 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1689 return new LocalVariableReference (this, loc);
1692 public void Emit (EmitContext ec)
1694 // TODO: Need something better for temporary variables
1695 if ((flags & Flags.CompilerGenerated) != 0)
1698 ec.Emit (OpCodes.Ldloc, builder);
1701 public void EmitAssign (EmitContext ec)
1703 // TODO: Need something better for temporary variables
1704 if ((flags & Flags.CompilerGenerated) != 0)
1707 ec.Emit (OpCodes.Stloc, builder);
1710 public void EmitAddressOf (EmitContext ec)
1712 ec.Emit (OpCodes.Ldloca, builder);
1715 public static string GetCompilerGeneratedName (Block block)
1717 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1720 public string GetReadOnlyContext ()
1722 switch (flags & Flags.ReadonlyMask) {
1723 case Flags.FixedVariable:
1724 return "fixed variable";
1725 case Flags.ForeachVariable:
1726 return "foreach iteration variable";
1727 case Flags.UsingVariable:
1728 return "using variable";
1731 throw new InternalErrorException ("Variable is not readonly");
1734 public bool IsThisAssigned (BlockContext ec, Block block)
1736 if (VariableInfo == null)
1737 throw new Exception ();
1739 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1742 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1745 public bool IsAssigned (BlockContext ec)
1747 if (VariableInfo == null)
1748 throw new Exception ();
1750 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1753 public void PrepareForFlowAnalysis (BlockContext bc)
1756 // No need for definitely assigned check for these guys
1758 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1761 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1762 bc.FlowOffset += VariableInfo.Length;
1766 // Mark the variables as referenced in the user code
1768 public void SetIsUsed ()
1770 flags |= Flags.Used;
1773 public void SetHasAddressTaken ()
1775 flags |= (Flags.AddressTaken | Flags.Used);
1778 public override string ToString ()
1780 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
1785 /// Block represents a C# block.
1789 /// This class is used in a number of places: either to represent
1790 /// explicit blocks that the programmer places or implicit blocks.
1792 /// Implicit blocks are used as labels or to introduce variable
1795 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1796 /// they contain extra information that is not necessary on normal blocks.
1798 public class Block : Statement {
1806 HasCapturedVariable = 64,
1807 HasCapturedThis = 1 << 7,
1808 IsExpressionTree = 1 << 8,
1809 CompilerGenerated = 1 << 9,
1813 public Block Parent;
1814 public Location StartLocation;
1815 public Location EndLocation;
1817 public ExplicitBlock Explicit;
1818 public ParametersBlock ParametersBlock;
1820 protected Flags flags;
1823 // The statements in this block
1825 protected List<Statement> statements;
1827 protected List<Statement> scope_initializers;
1829 int? resolving_init_idx;
1831 protected Block original;
1835 public int ID = id++;
1837 static int clone_id_counter;
1841 // int assignable_slots;
1842 bool unreachable_shown;
1845 public Block (Block parent, Location start, Location end)
1846 : this (parent, 0, start, end)
1850 public Block (Block parent, Flags flags, Location start, Location end)
1852 if (parent != null) {
1853 // the appropriate constructors will fixup these fields
1854 ParametersBlock = parent.ParametersBlock;
1855 Explicit = parent.Explicit;
1858 this.Parent = parent;
1860 this.StartLocation = start;
1861 this.EndLocation = end;
1863 statements = new List<Statement> (4);
1865 this.original = this;
1870 public bool HasRet {
1871 get { return (flags & Flags.HasRet) != 0; }
1874 public Block Original {
1880 public bool IsCompilerGenerated {
1881 get { return (flags & Flags.CompilerGenerated) != 0; }
1882 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
1885 public bool Unchecked {
1886 get { return (flags & Flags.Unchecked) != 0; }
1887 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1890 public bool Unsafe {
1891 get { return (flags & Flags.Unsafe) != 0; }
1892 set { flags |= Flags.Unsafe; }
1897 public Block CreateSwitchBlock (Location start)
1899 // FIXME: Only explicit block should be created
1900 var new_block = new Block (this, start, start);
1901 new_block.IsCompilerGenerated = true;
1905 public void SetEndLocation (Location loc)
1910 public void AddLabel (LabeledStatement target)
1912 ParametersBlock.TopBlock.AddLabel (target.Name, target);
1915 public void AddLocalName (LocalVariable li)
1917 AddLocalName (li.Name, li);
1920 public virtual void AddLocalName (string name, INamedBlockVariable li)
1922 ParametersBlock.TopBlock.AddLocalName (name, li);
1925 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
1927 if (reason == null) {
1928 Error_AlreadyDeclared (name, variable);
1932 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
1933 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
1934 "to `{0}', which is already used in a `{1}' scope to denote something else",
1938 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
1940 var pi = variable as ParametersBlock.ParameterInfo;
1942 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
1944 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
1945 "A local variable named `{0}' is already defined in this scope", name);
1949 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
1951 ParametersBlock.TopBlock.Report.Error (412, loc,
1952 "The type parameter name `{0}' is the same as local variable or parameter name",
1957 // It should be used by expressions which require to
1958 // register a statement during resolve process.
1960 public void AddScopeStatement (Statement s)
1962 if (scope_initializers == null)
1963 scope_initializers = new List<Statement> ();
1966 // Simple recursive helper, when resolve scope initializer another
1967 // new scope initializer can be added, this ensures it's initialized
1968 // before existing one. For now this can happen with expression trees
1969 // in base ctor initializer only
1971 if (resolving_init_idx.HasValue) {
1972 scope_initializers.Insert (resolving_init_idx.Value, s);
1973 ++resolving_init_idx;
1975 scope_initializers.Add (s);
1979 public void AddStatement (Statement s)
1984 public int AssignableSlots {
1986 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
1988 // return assignable_slots;
1992 public LabeledStatement LookupLabel (string name)
1994 return ParametersBlock.TopBlock.GetLabel (name, this);
1997 public override bool Resolve (BlockContext ec)
1999 Block prev_block = ec.CurrentBlock;
2002 ec.CurrentBlock = this;
2003 ec.StartFlowBranching (this);
2006 // Compiler generated scope statements
2008 if (scope_initializers != null) {
2009 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2010 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2013 resolving_init_idx = null;
2017 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2018 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2019 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2020 // responsible for handling the situation.
2022 int statement_count = statements.Count;
2023 for (int ix = 0; ix < statement_count; ix++){
2024 Statement s = statements [ix];
2027 // Warn if we detect unreachable code.
2030 if (s is EmptyStatement)
2033 if (!unreachable_shown && !(s is LabeledStatement)) {
2034 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2035 unreachable_shown = true;
2038 Block c_block = s as Block;
2039 if (c_block != null)
2040 c_block.unreachable = c_block.unreachable_shown = true;
2044 // Note that we're not using ResolveUnreachable() for unreachable
2045 // statements here. ResolveUnreachable() creates a temporary
2046 // flow branching and kills it afterwards. This leads to problems
2047 // if you have two unreachable statements where the first one
2048 // assigns a variable and the second one tries to access it.
2051 if (!s.Resolve (ec)) {
2053 if (ec.IsInProbingMode)
2056 statements [ix] = new EmptyStatement (s.loc);
2060 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2061 statements [ix] = new EmptyStatement (s.loc);
2063 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2064 if (unreachable && s is LabeledStatement)
2065 throw new InternalErrorException ("should not happen");
2068 while (ec.CurrentBranching is FlowBranchingLabeled)
2069 ec.EndFlowBranching ();
2071 bool flow_unreachable = ec.EndFlowBranching ();
2073 ec.CurrentBlock = prev_block;
2075 if (flow_unreachable)
2076 flags |= Flags.HasRet;
2078 // If we're a non-static `struct' constructor which doesn't have an
2079 // initializer, then we must initialize all of the struct's fields.
2080 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2086 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2088 unreachable_shown = true;
2092 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2094 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2095 bool ok = Resolve (ec);
2096 ec.KillFlowBranching ();
2101 protected override void DoEmit (EmitContext ec)
2103 for (int ix = 0; ix < statements.Count; ix++){
2104 statements [ix].Emit (ec);
2108 public override void Emit (EmitContext ec)
2110 if (scope_initializers != null)
2111 EmitScopeInitializers (ec);
2113 ec.Mark (StartLocation);
2116 if (SymbolWriter.HasSymbolWriter)
2117 EmitSymbolInfo (ec);
2120 protected void EmitScopeInitializers (EmitContext ec)
2122 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2124 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2125 foreach (Statement s in scope_initializers)
2129 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2132 protected virtual void EmitSymbolInfo (EmitContext ec)
2137 public override string ToString ()
2139 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2143 protected override void CloneTo (CloneContext clonectx, Statement t)
2145 Block target = (Block) t;
2147 target.clone_id = clone_id_counter++;
2150 clonectx.AddBlockMap (this, target);
2151 if (original != this)
2152 clonectx.AddBlockMap (original, target);
2154 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2155 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2158 target.Parent = clonectx.RemapBlockCopy (Parent);
2160 target.statements = new List<Statement> (statements.Count);
2161 foreach (Statement s in statements)
2162 target.statements.Add (s.Clone (clonectx));
2166 public class ExplicitBlock : Block
2168 protected AnonymousMethodStorey am_storey;
2170 public ExplicitBlock (Block parent, Location start, Location end)
2171 : this (parent, (Flags) 0, start, end)
2175 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2176 : base (parent, flags, start, end)
2178 this.Explicit = this;
2183 public AnonymousMethodStorey AnonymousMethodStorey {
2189 public bool HasCapturedThis {
2190 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2192 return (flags & Flags.HasCapturedThis) != 0;
2196 public bool HasCapturedVariable {
2197 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2199 return (flags & Flags.HasCapturedVariable) != 0;
2206 // Creates anonymous method storey in current block
2208 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2211 // An iterator has only 1 storey block
2213 if (ec.CurrentAnonymousMethod.IsIterator)
2214 return ec.CurrentAnonymousMethod.Storey;
2217 // When referencing a variable in iterator storey from children anonymous method
2219 if (ParametersBlock.am_storey is IteratorStorey) {
2220 return ParametersBlock.am_storey;
2223 if (am_storey == null) {
2224 MemberBase mc = ec.MemberContext as MemberBase;
2227 // Creates anonymous method storey for this block
2229 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2235 public override void Emit (EmitContext ec)
2237 if (am_storey != null) {
2238 DefineAnonymousStorey (ec);
2239 am_storey.EmitStoreyInstantiation (ec, this);
2242 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2243 if (emit_debug_info)
2248 if (emit_debug_info)
2252 void DefineAnonymousStorey (EmitContext ec)
2255 // Creates anonymous method storey
2257 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2259 // Creates parent storey reference when hoisted this is accessible
2261 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2262 ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2265 // Hoisted this exists in top-level parent storey only
2267 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2268 parent = parent.Parent.Explicit;
2270 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2273 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2275 // TODO MemberCache: Review
2276 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2279 am_storey.CreateType ();
2280 am_storey.DefineType ();
2281 am_storey.ResolveTypeParameters ();
2283 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2284 if (ref_blocks != null) {
2285 foreach (ExplicitBlock ref_block in ref_blocks) {
2286 for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2287 if (b.am_storey != null) {
2288 b.am_storey.AddParentStoreyReference (ec, am_storey);
2290 // Stop propagation inside same top block
2291 if (b.ParametersBlock.Original == ParametersBlock.Original)
2294 b = b.ParametersBlock;
2297 b.HasCapturedVariable = true;
2302 am_storey.Define ();
2303 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2306 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2308 tryBlock.statements = statements;
2309 statements = new List<Statement> (1);
2310 statements.Add (tf);
2315 // ParametersBlock was introduced to support anonymous methods
2316 // and lambda expressions
2318 public class ParametersBlock : ExplicitBlock
2320 public class ParameterInfo : INamedBlockVariable
2322 readonly ParametersBlock block;
2324 public VariableInfo VariableInfo;
2327 public ParameterInfo (ParametersBlock block, int index)
2335 public Block Block {
2341 public bool IsDeclared {
2347 public bool IsLocked {
2356 public Location Location {
2358 return Parameter.Location;
2362 public Parameter Parameter {
2364 return block.Parameters [index];
2368 public TypeSpec ParameterType {
2370 return Parameter.Type;
2376 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2378 return new ParameterReference (this, loc);
2383 // Block is converted into an expression
2385 sealed class BlockScopeExpression : Expression
2388 readonly ParametersBlock block;
2390 public BlockScopeExpression (Expression child, ParametersBlock block)
2396 public override bool ContainsEmitWithAwait ()
2398 return child.ContainsEmitWithAwait ();
2401 public override Expression CreateExpressionTree (ResolveContext ec)
2403 throw new NotSupportedException ();
2406 protected override Expression DoResolve (ResolveContext ec)
2411 child = child.Resolve (ec);
2415 eclass = child.eclass;
2420 public override void Emit (EmitContext ec)
2422 block.EmitScopeInitializers (ec);
2427 protected ParametersCompiled parameters;
2428 protected ParameterInfo[] parameter_info;
2430 protected bool unreachable;
2431 protected ToplevelBlock top_block;
2433 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2434 : base (parent, 0, start, start)
2436 if (parameters == null)
2437 throw new ArgumentNullException ("parameters");
2439 this.parameters = parameters;
2440 ParametersBlock = this;
2442 this.top_block = parent.ParametersBlock.top_block;
2443 ProcessParameters ();
2446 protected ParametersBlock (ParametersCompiled parameters, Location start)
2447 : base (null, 0, start, start)
2449 if (parameters == null)
2450 throw new ArgumentNullException ("parameters");
2452 this.parameters = parameters;
2453 ParametersBlock = this;
2457 // It's supposed to be used by method body implementation of anonymous methods
2459 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2460 : base (null, 0, source.StartLocation, source.EndLocation)
2462 this.parameters = parameters;
2463 this.statements = source.statements;
2464 this.scope_initializers = source.scope_initializers;
2466 this.resolved = true;
2467 this.unreachable = source.unreachable;
2468 this.am_storey = source.am_storey;
2470 ParametersBlock = this;
2473 // Overwrite original for comparison purposes when linking cross references
2474 // between anonymous methods
2484 return (flags & Flags.IsAsync) != 0;
2487 flags = value ? flags | Flags.IsAsync : flags & ~Flags.IsAsync;
2492 // Block has been converted to expression tree
2494 public bool IsExpressionTree {
2496 return (flags & Flags.IsExpressionTree) != 0;
2501 // The parameters for the block.
2503 public ParametersCompiled Parameters {
2509 public ToplevelBlock TopBlock {
2515 public bool Resolved {
2521 public int TemporaryLocalsCount { get; set; }
2526 // Check whether all `out' parameters have been assigned.
2528 public void CheckOutParameters (FlowBranching.UsageVector vector)
2530 if (vector.IsUnreachable)
2533 int n = parameter_info == null ? 0 : parameter_info.Length;
2535 for (int i = 0; i < n; i++) {
2536 VariableInfo var = parameter_info[i].VariableInfo;
2541 if (vector.IsAssigned (var, false))
2544 var p = parameter_info[i].Parameter;
2545 TopBlock.Report.Error (177, p.Location,
2546 "The out parameter `{0}' must be assigned to before control leaves the current method",
2551 public override Expression CreateExpressionTree (ResolveContext ec)
2553 if (statements.Count == 1) {
2554 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2555 if (scope_initializers != null)
2556 expr = new BlockScopeExpression (expr, this);
2561 return base.CreateExpressionTree (ec);
2564 public ParameterInfo GetParameterInfo (Parameter p)
2566 for (int i = 0; i < parameters.Count; ++i) {
2567 if (parameters[i] == p)
2568 return parameter_info[i];
2571 throw new ArgumentException ("Invalid parameter");
2574 public Expression GetParameterReference (int index, Location loc)
2576 return new ParameterReference (parameter_info[index], loc);
2579 public Statement PerformClone ()
2581 CloneContext clonectx = new CloneContext ();
2582 return Clone (clonectx);
2585 protected void ProcessParameters ()
2587 if (parameters.Count == 0)
2590 parameter_info = new ParameterInfo[parameters.Count];
2591 for (int i = 0; i < parameter_info.Length; ++i) {
2592 var p = parameters.FixedParameters[i];
2596 // TODO: Should use Parameter only and more block there
2597 parameter_info[i] = new ParameterInfo (this, i);
2599 AddLocalName (p.Name, parameter_info[i]);
2603 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2610 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2611 flags |= Flags.IsExpressionTree;
2616 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2617 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2622 unreachable = top_level.End ();
2624 } catch (Exception e) {
2625 if (e is CompletionResult || rc.Report.IsDisabled)
2628 if (rc.CurrentBlock != null) {
2629 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2631 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2634 if (rc.Module.Compiler.Settings.DebugFlags > 0)
2638 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
2639 if (rc.CurrentAnonymousMethod == null) {
2640 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2641 if (md is StateMachineMethod) {
2644 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2649 // If an asynchronous body of F is either an expression classified as nothing, or a
2650 // statement block where no return statements have expressions, the inferred return type is Task
2653 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
2654 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
2655 am.ReturnTypeInference = null;
2656 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
2661 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2662 rc.CurrentAnonymousMethod.GetSignatureForError ());
2670 void ResolveMeta (BlockContext ec)
2672 int orig_count = parameters.Count;
2674 for (int i = 0; i < orig_count; ++i) {
2675 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2677 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2680 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2681 parameter_info[i].VariableInfo = vi;
2682 ec.FlowOffset += vi.Length;
2686 public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
2688 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2689 pb.EndLocation = EndLocation;
2690 pb.statements = statements;
2692 var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2693 am_storey = new IteratorStorey (iterator);
2695 statements = new List<Statement> (1);
2696 AddStatement (new Return (iterator, iterator.Location));
2699 public void WrapIntoAsyncTask (IMemberContext context, TypeContainer host, TypeSpec returnType)
2701 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2702 pb.EndLocation = EndLocation;
2703 pb.statements = statements;
2705 var block_type = host.Module.Compiler.BuiltinTypes.Void;
2706 var initializer = new AsyncInitializer (pb, host, block_type);
2707 initializer.Type = block_type;
2709 am_storey = new AsyncTaskStorey (context, initializer, returnType);
2711 statements = new List<Statement> (1);
2712 AddStatement (new StatementExpression (initializer));
2719 public class ToplevelBlock : ParametersBlock
2721 LocalVariable this_variable;
2722 CompilerContext compiler;
2723 Dictionary<string, object> names;
2724 Dictionary<string, object> labels;
2726 public HoistedVariable HoistedThisVariable;
2728 public Report Report {
2729 get { return compiler.Report; }
2732 public ToplevelBlock (CompilerContext ctx, Location loc)
2733 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2737 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2738 : base (parameters, start)
2740 this.compiler = ctx;
2743 ProcessParameters ();
2747 // Recreates a top level block from parameters block. Used for
2748 // compiler generated methods where the original block comes from
2749 // explicit child block. This works for already resolved blocks
2750 // only to ensure we resolve them in the correct flow order
2752 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
2753 : base (source, parameters)
2755 this.compiler = source.TopBlock.compiler;
2759 public bool IsIterator
2762 return (flags & Flags.IsIterator) != 0;
2765 flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator;
2769 public override void AddLocalName (string name, INamedBlockVariable li)
2772 names = new Dictionary<string, object> ();
2775 if (!names.TryGetValue (name, out value)) {
2776 names.Add (name, li);
2780 INamedBlockVariable existing = value as INamedBlockVariable;
2781 List<INamedBlockVariable> existing_list;
2782 if (existing != null) {
2783 existing_list = new List<INamedBlockVariable> ();
2784 existing_list.Add (existing);
2785 names[name] = existing_list;
2787 existing_list = (List<INamedBlockVariable>) value;
2791 // A collision checking between local names
2793 for (int i = 0; i < existing_list.Count; ++i) {
2794 existing = existing_list[i];
2795 Block b = existing.Block;
2797 // Collision at same level
2798 if (li.Block == b) {
2799 li.Block.Error_AlreadyDeclared (name, li);
2803 // Collision with parent
2805 while ((b = b.Parent) != null) {
2806 if (existing.Block == b) {
2807 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
2808 i = existing_list.Count;
2813 // Collision with with children
2815 while ((b = b.Parent) != null) {
2816 if (li.Block == b) {
2817 li.Block.Error_AlreadyDeclared (name, li, "child");
2818 i = existing_list.Count;
2824 existing_list.Add (li);
2827 public void AddLabel (string name, LabeledStatement label)
2830 labels = new Dictionary<string, object> ();
2833 if (!labels.TryGetValue (name, out value)) {
2834 labels.Add (name, label);
2838 LabeledStatement existing = value as LabeledStatement;
2839 List<LabeledStatement> existing_list;
2840 if (existing != null) {
2841 existing_list = new List<LabeledStatement> ();
2842 existing_list.Add (existing);
2843 labels[name] = existing_list;
2845 existing_list = (List<LabeledStatement>) value;
2849 // A collision checking between labels
2851 for (int i = 0; i < existing_list.Count; ++i) {
2852 existing = existing_list[i];
2853 Block b = existing.Block;
2855 // Collision at same level
2856 if (label.Block == b) {
2857 Report.SymbolRelatedToPreviousError (existing.loc, name);
2858 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
2862 // Collision with parent
2864 while ((b = b.Parent) != null) {
2865 if (existing.Block == b) {
2866 Report.Error (158, label.loc,
2867 "The label `{0}' shadows another label by the same name in a contained scope", name);
2868 i = existing_list.Count;
2873 // Collision with with children
2875 while ((b = b.Parent) != null) {
2876 if (label.Block == b) {
2877 Report.Error (158, label.loc,
2878 "The label `{0}' shadows another label by the same name in a contained scope", name);
2879 i = existing_list.Count;
2885 existing_list.Add (label);
2889 // Creates an arguments set from all parameters, useful for method proxy calls
2891 public Arguments GetAllParametersArguments ()
2893 int count = parameters.Count;
2894 Arguments args = new Arguments (count);
2895 for (int i = 0; i < count; ++i) {
2896 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
2897 args.Add (new Argument (arg_expr));
2904 // Lookup inside a block, the returned value can represent 3 states
2906 // true+variable: A local name was found and it's valid
2907 // false+variable: A local name was found in a child block only
2908 // false+null: No local name was found
2910 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
2916 if (!names.TryGetValue (name, out value))
2919 variable = value as INamedBlockVariable;
2921 if (variable != null) {
2923 if (variable.Block == b.Original)
2927 } while (b != null);
2935 } while (b != null);
2937 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
2938 for (int i = 0; i < list.Count; ++i) {
2941 if (variable.Block == b.Original)
2945 } while (b != null);
2953 } while (b != null);
2963 public LabeledStatement GetLabel (string name, Block block)
2969 if (!labels.TryGetValue (name, out value)) {
2973 var label = value as LabeledStatement;
2975 if (label != null) {
2976 if (label.Block == b.Original)
2979 // TODO: Temporary workaround for the switch block implicit label block
2980 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2983 List<LabeledStatement> list = (List<LabeledStatement>) value;
2984 for (int i = 0; i < list.Count; ++i) {
2986 if (label.Block == b.Original)
2989 // TODO: Temporary workaround for the switch block implicit label block
2990 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2999 // Returns the "this" instance variable of this block.
3000 // See AddThisVariable() for more information.
3002 public LocalVariable ThisVariable {
3003 get { return this_variable; }
3007 // This is used by non-static `struct' constructors which do not have an
3008 // initializer - in this case, the constructor must initialize all of the
3009 // struct's fields. To do this, we add a "this" variable and use the flow
3010 // analysis code to ensure that it's been fully initialized before control
3011 // leaves the constructor.
3013 public void AddThisVariable (BlockContext bc)
3015 if (this_variable != null)
3016 throw new InternalErrorException (StartLocation.ToString ());
3018 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3019 this_variable.Type = bc.CurrentType;
3020 this_variable.PrepareForFlowAnalysis (bc);
3023 public bool IsThisAssigned (BlockContext ec)
3025 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3028 public override void Emit (EmitContext ec)
3030 if (Report.Errors > 0)
3039 ec.Mark (EndLocation);
3041 if (ec.HasReturnLabel)
3042 ec.MarkLabel (ec.ReturnLabel);
3044 if (ec.return_value != null) {
3045 ec.Emit (OpCodes.Ldloc, ec.return_value);
3046 ec.Emit (OpCodes.Ret);
3049 // If `HasReturnLabel' is set, then we already emitted a
3050 // jump to the end of the method, so we must emit a `ret'
3053 // Unfortunately, System.Reflection.Emit automatically emits
3054 // a leave to the end of a finally block. This is a problem
3055 // if no code is following the try/finally block since we may
3056 // jump to a point after the end of the method.
3057 // As a workaround, we're always creating a return label in
3061 if (ec.HasReturnLabel || !unreachable) {
3062 if (ec.ReturnType.Kind != MemberKind.Void)
3063 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3064 ec.Emit (OpCodes.Ret);
3069 } catch (Exception e){
3070 Console.WriteLine ("Exception caught by the compiler while emitting:");
3071 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3073 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3079 protected override void EmitSymbolInfo (EmitContext ec)
3081 AnonymousExpression ae = ec.CurrentAnonymousMethod;
3082 if ((ae != null) && (ae.Storey != null))
3083 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
3085 base.EmitSymbolInfo (ec);
3089 public class SwitchLabel {
3092 readonly Location loc;
3097 // if expr == null, then it is the default case.
3099 public SwitchLabel (Expression expr, Location l)
3105 public bool IsDefault {
3107 return label == null;
3111 public Expression Label {
3117 public Location Location {
3123 public Constant Converted {
3132 public Label GetILLabel (EmitContext ec)
3134 if (il_label == null){
3135 il_label = ec.DefineLabel ();
3138 return il_label.Value;
3142 // Resolves the expression, reduces it to a literal if possible
3143 // and then converts it to the requested type.
3145 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3147 Expression e = label.Resolve (ec);
3152 Constant c = e as Constant;
3154 ec.Report.Error (150, loc, "A constant value is expected");
3158 if (allow_nullable && c is NullLiteral) {
3163 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3164 return converted != null;
3167 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3170 if (converted == null)
3173 label = converted.GetValueAsLiteral ();
3175 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3176 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3179 public SwitchLabel Clone (CloneContext clonectx)
3184 return new SwitchLabel (label.Clone (clonectx), loc);
3188 public class SwitchSection {
3189 public readonly List<SwitchLabel> Labels;
3190 public readonly Block Block;
3192 public SwitchSection (List<SwitchLabel> labels, Block block)
3198 public SwitchSection Clone (CloneContext clonectx)
3200 var cloned_labels = new List<SwitchLabel> ();
3202 foreach (SwitchLabel sl in Labels)
3203 cloned_labels.Add (sl.Clone (clonectx));
3205 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3209 public class Switch : Statement
3211 // structure used to hold blocks of keys while calculating table switch
3212 sealed class LabelsRange : IComparable<LabelsRange>
3214 public readonly long min;
3216 public readonly List<long> label_values;
3218 public LabelsRange (long value)
3221 label_values = new List<long> ();
3222 label_values.Add (value);
3225 public LabelsRange (long min, long max, ICollection<long> values)
3229 this.label_values = new List<long> (values);
3234 return max - min + 1;
3238 public bool AddValue (long value)
3240 var gap = value - min + 1;
3241 // Ensure the range has > 50% occupancy
3242 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3246 label_values.Add (value);
3250 public int CompareTo (LabelsRange other)
3252 int nLength = label_values.Count;
3253 int nLengthOther = other.label_values.Count;
3254 if (nLengthOther == nLength)
3255 return (int) (other.min - min);
3257 return nLength - nLengthOther;
3261 public List<SwitchSection> Sections;
3262 public Expression Expr;
3265 // Mapping of all labels to their SwitchLabels
3267 Dictionary<long, SwitchLabel> labels;
3268 Dictionary<string, SwitchLabel> string_labels;
3271 /// The governing switch type
3273 public TypeSpec SwitchType;
3278 Label default_target;
3280 Expression new_expr;
3283 SwitchSection constant_section;
3284 SwitchSection default_section;
3285 SwitchLabel null_section;
3287 ExpressionStatement string_dictionary;
3288 FieldExpr switch_cache_field;
3289 static int unique_counter;
3290 ExplicitBlock block;
3293 // Nullable Types support
3295 Nullable.Unwrap unwrap;
3297 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3305 public ExplicitBlock Block {
3311 public Label DefaultLabel {
3313 return default_target;
3317 public bool GotDefault {
3319 return default_section != null;
3323 public bool IsNullable {
3325 return unwrap != null;
3330 // Determines the governing type for a switch. The returned
3331 // expression might be the expression from the switch, or an
3332 // expression that includes any potential conversions to
3334 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3336 switch (expr.Type.BuiltinType) {
3337 case BuiltinTypeSpec.Type.Byte:
3338 case BuiltinTypeSpec.Type.SByte:
3339 case BuiltinTypeSpec.Type.UShort:
3340 case BuiltinTypeSpec.Type.Short:
3341 case BuiltinTypeSpec.Type.UInt:
3342 case BuiltinTypeSpec.Type.Int:
3343 case BuiltinTypeSpec.Type.ULong:
3344 case BuiltinTypeSpec.Type.Long:
3345 case BuiltinTypeSpec.Type.Char:
3346 case BuiltinTypeSpec.Type.String:
3347 case BuiltinTypeSpec.Type.Bool:
3351 if (expr.Type.IsEnum)
3355 // Try to find a *user* defined implicit conversion.
3357 // If there is no implicit conversion, or if there are multiple
3358 // conversions, we have to report an error
3360 Expression converted = null;
3361 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3364 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3369 // Ignore over-worked ImplicitUserConversions that do
3370 // an implicit conversion in addition to the user conversion.
3372 if (!(e is UserCast))
3375 if (converted != null){
3376 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3385 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3387 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3403 // Performs the basic sanity checks on the switch statement
3404 // (looks for duplicate keys and non-constant expressions).
3406 // It also returns a hashtable with the keys that we will later
3407 // use to compute the switch tables
3409 bool CheckSwitch (ResolveContext ec)
3412 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3413 string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3415 labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3417 foreach (SwitchSection ss in Sections){
3418 foreach (SwitchLabel sl in ss.Labels){
3420 if (default_section != null){
3421 sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3424 default_section = ss;
3428 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3434 if (string_labels != null) {
3435 string s = sl.Converted.GetValue () as string;
3439 string_labels.Add (s, sl);
3441 if (sl.Converted is NullLiteral) {
3444 labels.Add (sl.Converted.GetValueAsLong (), sl);
3447 } catch (ArgumentException) {
3448 if (string_labels != null)
3449 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3451 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3461 // This method emits code for a lookup-based switch statement (non-string)
3462 // Basically it groups the cases into blocks that are at least half full,
3463 // and then spits out individual lookup opcodes for each block.
3464 // It emits the longest blocks first, and short blocks are just
3465 // handled with direct compares.
3467 void EmitTableSwitch (EmitContext ec, Expression val)
3469 Label lbl_default = default_target;
3471 if (labels.Count > 0) {
3472 List<LabelsRange> ranges;
3473 if (string_labels != null) {
3474 // We have done all hard work for string already
3475 // setup single range only
3476 ranges = new List<LabelsRange> (1);
3477 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3479 var element_keys = new long[labels.Count];
3480 labels.Keys.CopyTo (element_keys, 0);
3481 Array.Sort (element_keys);
3484 // Build possible ranges of switch labes to reduce number
3487 ranges = new List<LabelsRange> (element_keys.Length);
3488 var range = new LabelsRange (element_keys[0]);
3490 for (int i = 1; i < element_keys.Length; ++i) {
3491 var l = element_keys[i];
3492 if (range.AddValue (l))
3495 range = new LabelsRange (l);
3499 // sort the blocks so we can tackle the largest ones first
3503 TypeSpec compare_type = TypeManager.IsEnumType (SwitchType) ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3505 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3506 LabelsRange kb = ranges[range_index];
3507 lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3509 // Optimize small ranges using simple equality check
3510 if (kb.Range <= 2) {
3511 foreach (var key in kb.label_values) {
3512 SwitchLabel sl = labels[key];
3513 if (sl.Converted.IsDefaultValue) {
3514 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3517 sl.Converted.Emit (ec);
3518 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3522 // TODO: if all the keys in the block are the same and there are
3523 // no gaps/defaults then just use a range-check.
3524 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3525 // TODO: optimize constant/I4 cases
3527 // check block range (could be > 2^31)
3529 ec.EmitLong (kb.min);
3530 ec.Emit (OpCodes.Blt, lbl_default);
3533 ec.EmitLong (kb.max);
3534 ec.Emit (OpCodes.Bgt, lbl_default);
3539 ec.EmitLong (kb.min);
3540 ec.Emit (OpCodes.Sub);
3543 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3547 int first = (int) kb.min;
3550 ec.Emit (OpCodes.Sub);
3551 } else if (first < 0) {
3552 ec.EmitInt (-first);
3553 ec.Emit (OpCodes.Add);
3557 // first, build the list of labels for the switch
3559 long cJumps = kb.Range;
3560 Label[] switch_labels = new Label[cJumps];
3561 for (int iJump = 0; iJump < cJumps; iJump++) {
3562 var key = kb.label_values[iKey];
3563 if (key == kb.min + iJump) {
3564 switch_labels[iJump] = labels[key].GetILLabel (ec);
3567 switch_labels[iJump] = lbl_default;
3571 // emit the switch opcode
3572 ec.Emit (OpCodes.Switch, switch_labels);
3575 // mark the default for this block
3576 if (range_index != 0)
3577 ec.MarkLabel (lbl_default);
3580 // the last default just goes to the end
3581 if (ranges.Count > 0)
3582 ec.Emit (OpCodes.Br, lbl_default);
3585 // now emit the code for the sections
3586 bool found_default = false;
3588 foreach (SwitchSection ss in Sections) {
3589 foreach (SwitchLabel sl in ss.Labels) {
3591 ec.MarkLabel (lbl_default);
3592 found_default = true;
3593 if (null_section == null)
3594 ec.MarkLabel (null_target);
3595 } else if (sl.Converted.IsNull) {
3596 ec.MarkLabel (null_target);
3599 ec.MarkLabel (sl.GetILLabel (ec));
3605 if (!found_default) {
3606 ec.MarkLabel (lbl_default);
3607 if (null_section == null) {
3608 ec.MarkLabel (null_target);
3613 SwitchLabel FindLabel (Constant value)
3615 SwitchLabel sl = null;
3617 if (string_labels != null) {
3618 string s = value.GetValue () as string;
3620 if (null_section != null)
3622 else if (default_section != null)
3623 sl = default_section.Labels[0];
3625 string_labels.TryGetValue (s, out sl);
3628 if (value is NullLiteral) {
3631 labels.TryGetValue (value.GetValueAsLong (), out sl);
3638 SwitchSection FindSection (SwitchLabel label)
3640 foreach (SwitchSection ss in Sections){
3641 foreach (SwitchLabel sl in ss.Labels){
3650 public static void Reset ()
3655 public override bool Resolve (BlockContext ec)
3657 Expr = Expr.Resolve (ec);
3661 new_expr = SwitchGoverningType (ec, Expr);
3663 if (new_expr == null && Expr.Type.IsNullableType) {
3664 unwrap = Nullable.Unwrap.Create (Expr, false);
3668 new_expr = SwitchGoverningType (ec, unwrap);
3671 if (new_expr == null){
3672 ec.Report.Error (151, loc,
3673 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3674 TypeManager.CSharpName (Expr.Type));
3679 SwitchType = new_expr.Type;
3681 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
3682 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
3686 if (!CheckSwitch (ec))
3689 Switch old_switch = ec.Switch;
3691 ec.Switch.SwitchType = SwitchType;
3693 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3695 var constant = new_expr as Constant;
3696 if (constant != null) {
3698 SwitchLabel label = FindLabel (constant);
3700 constant_section = FindSection (label);
3702 if (constant_section == null)
3703 constant_section = default_section;
3708 foreach (SwitchSection ss in Sections){
3710 ec.CurrentBranching.CreateSibling (
3711 null, FlowBranching.SiblingType.SwitchSection);
3715 if (is_constant && (ss != constant_section)) {
3716 // If we're a constant switch, we're only emitting
3717 // one single section - mark all the others as
3719 ec.CurrentBranching.CurrentUsageVector.Goto ();
3720 if (!ss.Block.ResolveUnreachable (ec, true)) {
3724 if (!ss.Block.Resolve (ec))
3729 if (default_section == null)
3730 ec.CurrentBranching.CreateSibling (
3731 null, FlowBranching.SiblingType.SwitchSection);
3733 ec.EndFlowBranching ();
3734 ec.Switch = old_switch;
3739 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && !is_constant) {
3740 // TODO: Optimize single case, and single+default case
3741 ResolveStringSwitchMap (ec);
3747 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
3749 var sl = FindLabel (value);
3752 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
3758 void ResolveStringSwitchMap (ResolveContext ec)
3760 FullNamedExpression string_dictionary_type;
3761 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
3762 string_dictionary_type = new TypeExpression (
3763 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
3764 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
3766 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
3767 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
3769 ec.Module.PredefinedTypes.Dictionary.Resolve ();
3773 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3774 Field field = new Field (ctype, string_dictionary_type,
3775 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3776 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3777 if (!field.Define ())
3779 ctype.AddField (field);
3781 var init = new List<Expression> ();
3783 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
3784 string value = null;
3785 foreach (SwitchSection section in Sections) {
3786 bool contains_label = false;
3787 foreach (SwitchLabel sl in section.Labels) {
3788 if (sl.IsDefault || sl.Converted.IsNull)
3791 if (!contains_label) {
3792 labels.Add (counter, sl);
3793 contains_label = true;
3796 value = (string) sl.Converted.GetValue ();
3797 var init_args = new List<Expression> (2);
3798 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
3800 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
3801 init_args.Add (sl.Converted);
3803 init.Add (new CollectionElementInitializer (init_args, loc));
3807 // Don't add empty sections
3813 Arguments args = new Arguments (1);
3814 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
3815 Expression initializer = new NewInitialize (string_dictionary_type, args,
3816 new CollectionOrObjectInitializers (init, loc), loc);
3818 switch_cache_field = new FieldExpr (field, loc);
3819 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3822 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3824 Label l_initialized = ec.DefineLabel ();
3827 // Skip initialization when value is null
3829 value.EmitBranchable (ec, null_target, false);
3832 // Check if string dictionary is initialized and initialize
3834 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3835 string_dictionary.EmitStatement (ec);
3836 ec.MarkLabel (l_initialized);
3838 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
3840 ResolveContext rc = new ResolveContext (ec.MemberContext);
3842 if (switch_cache_field.Type.IsGeneric) {
3843 Arguments get_value_args = new Arguments (2);
3844 get_value_args.Add (new Argument (value));
3845 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3846 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3847 if (get_item == null)
3851 // A value was not found, go to default case
3853 get_item.EmitBranchable (ec, default_target, false);
3855 Arguments get_value_args = new Arguments (1);
3856 get_value_args.Add (new Argument (value));
3858 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3859 if (get_item == null)
3862 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
3863 get_item_object.EmitAssign (ec, get_item, true, false);
3864 ec.Emit (OpCodes.Brfalse, default_target);
3866 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3867 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
3869 get_item_int.EmitStatement (ec);
3870 get_item_object.Release (ec);
3873 EmitTableSwitch (ec, string_switch_variable);
3874 string_switch_variable.Release (ec);
3877 protected override void DoEmit (EmitContext ec)
3880 // Needed to emit anonymous storey initialization
3881 // Otherwise it does not contain any statements for now
3885 default_target = ec.DefineLabel ();
3886 null_target = ec.DefineLabel ();
3888 // Store variable for comparission purposes
3889 // TODO: Don't duplicate non-captured VariableReference
3890 LocalTemporary value;
3892 value = new LocalTemporary (SwitchType);
3893 unwrap.EmitCheck (ec);
3894 ec.Emit (OpCodes.Brfalse, null_target);
3897 } else if (!is_constant) {
3898 value = new LocalTemporary (SwitchType);
3905 // Setup the codegen context
3907 Label old_end = ec.LoopEnd;
3908 Switch old_switch = ec.Switch;
3910 ec.LoopEnd = ec.DefineLabel ();
3915 if (constant_section != null)
3916 constant_section.Block.Emit (ec);
3917 } else if (string_dictionary != null) {
3918 DoEmitStringSwitch (value, ec);
3920 EmitTableSwitch (ec, value);
3926 // Restore context state.
3927 ec.MarkLabel (ec.LoopEnd);
3930 // Restore the previous context
3932 ec.LoopEnd = old_end;
3933 ec.Switch = old_switch;
3936 protected override void CloneTo (CloneContext clonectx, Statement t)
3938 Switch target = (Switch) t;
3940 target.Expr = Expr.Clone (clonectx);
3941 target.Sections = new List<SwitchSection> ();
3942 foreach (SwitchSection ss in Sections){
3943 target.Sections.Add (ss.Clone (clonectx));
3948 // A place where execution can restart in an iterator
3949 public abstract class ResumableStatement : Statement
3952 protected Label resume_point;
3954 public Label PrepareForEmit (EmitContext ec)
3958 resume_point = ec.DefineLabel ();
3960 return resume_point;
3963 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3968 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
3973 public abstract class TryFinallyBlock : ExceptionStatement
3975 protected Statement stmt;
3976 Label dispose_try_block;
3977 bool prepared_for_dispose, emitted_dispose;
3979 protected TryFinallyBlock (Statement stmt, Location loc)
3987 public Statement Statement {
3995 protected abstract void EmitTryBody (EmitContext ec);
3996 protected abstract void EmitFinallyBody (EmitContext ec);
3998 public override Label PrepareForDispose (EmitContext ec, Label end)
4000 if (!prepared_for_dispose) {
4001 prepared_for_dispose = true;
4002 dispose_try_block = ec.DefineLabel ();
4004 return dispose_try_block;
4007 protected sealed override void DoEmit (EmitContext ec)
4009 EmitTryBodyPrepare (ec);
4012 ec.BeginFinallyBlock ();
4014 Label start_finally = ec.DefineLabel ();
4015 if (resume_points != null) {
4016 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4018 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4019 ec.Emit (OpCodes.Brfalse_S, start_finally);
4020 ec.Emit (OpCodes.Endfinally);
4023 ec.MarkLabel (start_finally);
4024 EmitFinallyBody (ec);
4026 ec.EndExceptionBlock ();
4029 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4031 if (emitted_dispose)
4034 emitted_dispose = true;
4036 Label end_of_try = ec.DefineLabel ();
4038 // Ensure that the only way we can get into this code is through a dispatcher
4039 if (have_dispatcher)
4040 ec.Emit (OpCodes.Br, end);
4042 ec.BeginExceptionBlock ();
4044 ec.MarkLabel (dispose_try_block);
4046 Label[] labels = null;
4047 for (int i = 0; i < resume_points.Count; ++i) {
4048 ResumableStatement s = resume_points[i];
4049 Label ret = s.PrepareForDispose (ec, end_of_try);
4050 if (ret.Equals (end_of_try) && labels == null)
4052 if (labels == null) {
4053 labels = new Label[resume_points.Count];
4054 for (int j = 0; j < i; ++j)
4055 labels[j] = end_of_try;
4060 if (labels != null) {
4062 for (j = 1; j < labels.Length; ++j)
4063 if (!labels[0].Equals (labels[j]))
4065 bool emit_dispatcher = j < labels.Length;
4067 if (emit_dispatcher) {
4068 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4069 ec.Emit (OpCodes.Ldloc, pc);
4070 ec.EmitInt (first_resume_pc);
4071 ec.Emit (OpCodes.Sub);
4072 ec.Emit (OpCodes.Switch, labels);
4073 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4076 foreach (ResumableStatement s in resume_points)
4077 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4080 ec.MarkLabel (end_of_try);
4082 ec.BeginFinallyBlock ();
4084 EmitFinallyBody (ec);
4086 ec.EndExceptionBlock ();
4091 // Base class for blocks using exception handling
4093 public abstract class ExceptionStatement : ResumableStatement
4098 protected List<ResumableStatement> resume_points;
4099 protected int first_resume_pc;
4101 protected ExceptionStatement (Location loc)
4106 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4108 StateMachineInitializer state_machine = null;
4109 if (resume_points != null) {
4110 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4112 ec.EmitInt ((int) IteratorStorey.State.Running);
4113 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4116 ec.BeginExceptionBlock ();
4118 if (resume_points != null) {
4119 ec.MarkLabel (resume_point);
4121 // For normal control flow, we want to fall-through the Switch
4122 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4123 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4124 ec.EmitInt (first_resume_pc);
4125 ec.Emit (OpCodes.Sub);
4127 Label[] labels = new Label[resume_points.Count];
4128 for (int i = 0; i < resume_points.Count; ++i)
4129 labels[i] = resume_points[i].PrepareForEmit (ec);
4130 ec.Emit (OpCodes.Switch, labels);
4134 public void SomeCodeFollows ()
4137 code_follows = true;
4141 public override bool Resolve (BlockContext ec)
4144 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4145 // So, ensure there's some IL code after this statement.
4146 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4147 ec.NeedReturnLabel ();
4152 public void AddResumePoint (ResumableStatement stmt, int pc)
4154 if (resume_points == null) {
4155 resume_points = new List<ResumableStatement> ();
4156 first_resume_pc = pc;
4159 if (pc != first_resume_pc + resume_points.Count)
4160 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4162 resume_points.Add (stmt);
4167 public class Lock : TryFinallyBlock
4170 TemporaryVariableReference expr_copy;
4171 TemporaryVariableReference lock_taken;
4173 public Lock (Expression expr, Statement stmt, Location loc)
4179 public override bool Resolve (BlockContext ec)
4181 expr = expr.Resolve (ec);
4185 if (!TypeSpec.IsReferenceType (expr.Type)) {
4186 ec.Report.Error (185, loc,
4187 "`{0}' is not a reference type as required by the lock statement",
4188 expr.Type.GetSignatureForError ());
4191 if (expr.Type.IsGenericParameter) {
4192 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4195 VariableReference lv = expr as VariableReference;
4198 locked = lv.IsLockedByStatement;
4199 lv.IsLockedByStatement = true;
4205 using (ec.Set (ResolveContext.Options.LockScope)) {
4206 ec.StartFlowBranching (this);
4207 Statement.Resolve (ec);
4208 ec.EndFlowBranching ();
4212 lv.IsLockedByStatement = locked;
4218 // Have to keep original lock value around to unlock same location
4219 // in the case the original has changed or is null
4221 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4222 expr_copy.Resolve (ec);
4225 // Ensure Monitor methods are available
4227 if (ResolvePredefinedMethods (ec) > 1) {
4228 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4229 lock_taken.Resolve (ec);
4235 protected override void EmitTryBodyPrepare (EmitContext ec)
4237 expr_copy.EmitAssign (ec, expr);
4239 if (lock_taken != null) {
4241 // Initialize ref variable
4243 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4246 // Monitor.Enter (expr_copy)
4248 expr_copy.Emit (ec);
4249 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4252 base.EmitTryBodyPrepare (ec);
4255 protected override void EmitTryBody (EmitContext ec)
4258 // Monitor.Enter (expr_copy, ref lock_taken)
4260 if (lock_taken != null) {
4261 expr_copy.Emit (ec);
4262 lock_taken.LocalInfo.CreateBuilder (ec);
4263 lock_taken.AddressOf (ec, AddressOp.Load);
4264 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4267 Statement.Emit (ec);
4270 protected override void EmitFinallyBody (EmitContext ec)
4273 // if (lock_taken) Monitor.Exit (expr_copy)
4275 Label skip = ec.DefineLabel ();
4277 if (lock_taken != null) {
4278 lock_taken.Emit (ec);
4279 ec.Emit (OpCodes.Brfalse_S, skip);
4282 expr_copy.Emit (ec);
4283 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4285 ec.Emit (OpCodes.Call, m);
4287 ec.MarkLabel (skip);
4290 int ResolvePredefinedMethods (ResolveContext rc)
4292 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4293 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4297 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4301 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4305 protected override void CloneTo (CloneContext clonectx, Statement t)
4307 Lock target = (Lock) t;
4309 target.expr = expr.Clone (clonectx);
4310 target.stmt = Statement.Clone (clonectx);
4314 public class Unchecked : Statement {
4317 public Unchecked (Block b, Location loc)
4324 public override bool Resolve (BlockContext ec)
4326 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4327 return Block.Resolve (ec);
4330 protected override void DoEmit (EmitContext ec)
4332 using (ec.With (EmitContext.Options.CheckedScope, false))
4336 protected override void CloneTo (CloneContext clonectx, Statement t)
4338 Unchecked target = (Unchecked) t;
4340 target.Block = clonectx.LookupBlock (Block);
4344 public class Checked : Statement {
4347 public Checked (Block b, Location loc)
4350 b.Unchecked = false;
4354 public override bool Resolve (BlockContext ec)
4356 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4357 return Block.Resolve (ec);
4360 protected override void DoEmit (EmitContext ec)
4362 using (ec.With (EmitContext.Options.CheckedScope, true))
4366 protected override void CloneTo (CloneContext clonectx, Statement t)
4368 Checked target = (Checked) t;
4370 target.Block = clonectx.LookupBlock (Block);
4374 public class Unsafe : Statement {
4377 public Unsafe (Block b, Location loc)
4380 Block.Unsafe = true;
4384 public override bool Resolve (BlockContext ec)
4386 if (ec.CurrentIterator != null)
4387 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4389 using (ec.Set (ResolveContext.Options.UnsafeScope))
4390 return Block.Resolve (ec);
4393 protected override void DoEmit (EmitContext ec)
4398 protected override void CloneTo (CloneContext clonectx, Statement t)
4400 Unsafe target = (Unsafe) t;
4402 target.Block = clonectx.LookupBlock (Block);
4409 public class Fixed : Statement
4411 abstract class Emitter : ShimExpression
4413 protected LocalVariable vi;
4415 protected Emitter (Expression expr, LocalVariable li)
4421 public abstract void EmitExit (EmitContext ec);
4424 class ExpressionEmitter : Emitter {
4425 public ExpressionEmitter (Expression converted, LocalVariable li) :
4426 base (converted, li)
4430 protected override Expression DoResolve (ResolveContext rc)
4432 throw new NotImplementedException ();
4435 public override void Emit (EmitContext ec) {
4437 // Store pointer in pinned location
4443 public override void EmitExit (EmitContext ec)
4446 ec.Emit (OpCodes.Conv_U);
4451 class StringEmitter : Emitter
4453 LocalVariable pinned_string;
4455 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4460 protected override Expression DoResolve (ResolveContext rc)
4462 pinned_string = new LocalVariable (vi.Block, "$pinned",
4463 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4465 pinned_string.Type = rc.BuiltinTypes.String;
4467 eclass = ExprClass.Variable;
4468 type = rc.BuiltinTypes.Int;
4472 public override void Emit (EmitContext ec)
4474 pinned_string.CreateBuilder (ec);
4477 pinned_string.EmitAssign (ec);
4479 // TODO: Should use Binary::Add
4480 pinned_string.Emit (ec);
4481 ec.Emit (OpCodes.Conv_I);
4483 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
4487 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
4488 //pe.InstanceExpression = pinned_string;
4489 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4491 ec.Emit (OpCodes.Add);
4495 public override void EmitExit (EmitContext ec)
4498 pinned_string.EmitAssign (ec);
4502 public class VariableDeclaration : BlockVariableDeclaration
4504 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4509 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4511 if (!Variable.Type.IsPointer && li == Variable) {
4512 bc.Report.Error (209, TypeExpression.Location,
4513 "The type of locals declared in a fixed statement must be a pointer type");
4518 // The rules for the possible declarators are pretty wise,
4519 // but the production on the grammar is more concise.
4521 // So we have to enforce these rules here.
4523 // We do not resolve before doing the case 1 test,
4524 // because the grammar is explicit in that the token &
4525 // is present, so we need to test for this particular case.
4528 if (initializer is Cast) {
4529 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4533 initializer = initializer.Resolve (bc);
4535 if (initializer == null)
4541 if (initializer.Type.IsArray) {
4542 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4545 // Provided that array_type is unmanaged,
4547 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
4551 // and T* is implicitly convertible to the
4552 // pointer type given in the fixed statement.
4554 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4556 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
4557 if (converted == null)
4561 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4563 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4564 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4565 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
4566 new NullLiteral (loc),
4569 converted = converted.Resolve (bc);
4571 return new ExpressionEmitter (converted, li);
4577 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
4578 return new StringEmitter (initializer, li, loc).Resolve (bc);
4581 // Case 3: fixed buffer
4582 if (initializer is FixedBufferPtr) {
4583 return new ExpressionEmitter (initializer, li);
4587 // Case 4: & object.
4589 bool already_fixed = true;
4590 Unary u = initializer as Unary;
4591 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4592 IVariableReference vr = u.Expr as IVariableReference;
4593 if (vr == null || !vr.IsFixed) {
4594 already_fixed = false;
4598 if (already_fixed) {
4599 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4602 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4603 return new ExpressionEmitter (initializer, li);
4608 VariableDeclaration decl;
4609 Statement statement;
4612 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4621 public Statement Statement {
4627 public BlockVariableDeclaration Variables {
4635 public override bool Resolve (BlockContext ec)
4637 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4638 if (!decl.Resolve (ec))
4642 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4643 bool ok = statement.Resolve (ec);
4644 bool flow_unreachable = ec.EndFlowBranching ();
4645 has_ret = flow_unreachable;
4650 protected override void DoEmit (EmitContext ec)
4652 decl.Variable.CreateBuilder (ec);
4653 decl.Initializer.Emit (ec);
4654 if (decl.Declarators != null) {
4655 foreach (var d in decl.Declarators) {
4656 d.Variable.CreateBuilder (ec);
4657 d.Initializer.Emit (ec);
4661 statement.Emit (ec);
4667 // Clear the pinned variable
4669 ((Emitter) decl.Initializer).EmitExit (ec);
4670 if (decl.Declarators != null) {
4671 foreach (var d in decl.Declarators) {
4672 ((Emitter)d.Initializer).EmitExit (ec);
4677 protected override void CloneTo (CloneContext clonectx, Statement t)
4679 Fixed target = (Fixed) t;
4681 target.decl = (VariableDeclaration) decl.Clone (clonectx);
4682 target.statement = statement.Clone (clonectx);
4686 public class Catch : Statement
4690 FullNamedExpression type_expr;
4691 CompilerAssign assign;
4694 public Catch (Block block, Location loc)
4702 public Block Block {
4708 public TypeSpec CatchType {
4714 public bool IsGeneral {
4716 return type_expr == null;
4720 public FullNamedExpression TypeExpression {
4729 public LocalVariable Variable {
4740 protected override void DoEmit (EmitContext ec)
4743 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
4745 ec.BeginCatchBlock (CatchType);
4748 li.CreateBuilder (ec);
4751 // Special case hoisted catch variable, we have to use a temporary variable
4752 // to pass via anonymous storey initialization with the value still on top
4755 if (li.HoistedVariant != null) {
4756 LocalTemporary lt = new LocalTemporary (li.Type);
4757 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4759 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4761 // switch to assigning from the temporary variable and not from top of the stack
4762 assign.UpdateSource (lt);
4765 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4766 ec.Emit (OpCodes.Pop);
4767 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4773 public override bool Resolve (BlockContext ec)
4775 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4776 if (type_expr != null) {
4777 type = type_expr.ResolveAsType (ec);
4781 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
4782 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4783 } else if (li != null) {
4785 li.PrepareForFlowAnalysis (ec);
4787 // source variable is at the top of the stack
4788 Expression source = new EmptyExpression (li.Type);
4789 if (li.Type.IsGenericParameter)
4790 source = new UnboxCast (source, li.Type);
4792 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
4793 Block.AddScopeStatement (new StatementExpression (assign));
4797 return Block.Resolve (ec);
4801 protected override void CloneTo (CloneContext clonectx, Statement t)
4803 Catch target = (Catch) t;
4805 if (type_expr != null)
4806 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
4808 target.block = clonectx.LookupBlock (block);
4812 public class TryFinally : TryFinallyBlock
4816 public TryFinally (Statement stmt, Block fini, Location loc)
4822 public override bool Resolve (BlockContext ec)
4826 ec.StartFlowBranching (this);
4828 if (!stmt.Resolve (ec))
4832 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4833 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4834 if (!fini.Resolve (ec))
4838 ec.EndFlowBranching ();
4840 ok &= base.Resolve (ec);
4845 protected override void EmitTryBody (EmitContext ec)
4850 protected override void EmitFinallyBody (EmitContext ec)
4855 protected override void CloneTo (CloneContext clonectx, Statement t)
4857 TryFinally target = (TryFinally) t;
4859 target.stmt = (Statement) stmt.Clone (clonectx);
4861 target.fini = clonectx.LookupBlock (fini);
4865 public class TryCatch : ExceptionStatement
4868 public List<Catch> Specific;
4869 public Catch General;
4870 readonly bool inside_try_finally;
4872 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4876 this.Specific = catch_clauses;
4877 this.inside_try_finally = inside_try_finally;
4879 Catch c = catch_clauses [0];
4882 catch_clauses.RemoveAt (0);
4886 public bool IsTryCatchFinally {
4888 return inside_try_finally;
4892 public override bool Resolve (BlockContext ec)
4896 ec.StartFlowBranching (this);
4898 if (!Block.Resolve (ec))
4901 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4903 foreach (Catch c in Specific){
4904 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4906 if (!c.Resolve (ec)) {
4911 TypeSpec resolved_type = c.CatchType;
4912 for (int ii = 0; ii < last_index; ++ii) {
4913 if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4914 ec.Report.Error (160, c.loc,
4915 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4916 TypeManager.CSharpName (prev_catches [ii]));
4921 prev_catches [last_index++] = resolved_type;
4924 if (General != null) {
4925 foreach (Catch c in Specific) {
4926 if (c.CatchType.BuiltinType != BuiltinTypeSpec.Type.Exception)
4929 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
4932 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
4935 ec.Report.Warning (1058, 1, c.loc,
4936 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4939 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4941 if (!General.Resolve (ec))
4945 ec.EndFlowBranching ();
4947 return base.Resolve (ec) && ok;
4950 protected sealed override void DoEmit (EmitContext ec)
4952 if (!inside_try_finally)
4953 EmitTryBodyPrepare (ec);
4957 foreach (Catch c in Specific)
4960 if (General != null)
4963 if (!inside_try_finally)
4964 ec.EndExceptionBlock ();
4967 protected override void CloneTo (CloneContext clonectx, Statement t)
4969 TryCatch target = (TryCatch) t;
4971 target.Block = clonectx.LookupBlock (Block);
4972 if (General != null)
4973 target.General = (Catch) General.Clone (clonectx);
4974 if (Specific != null){
4975 target.Specific = new List<Catch> ();
4976 foreach (Catch c in Specific)
4977 target.Specific.Add ((Catch) c.Clone (clonectx));
4982 public class Using : TryFinallyBlock
4984 public class VariableDeclaration : BlockVariableDeclaration
4986 Statement dispose_call;
4988 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4993 public VariableDeclaration (LocalVariable li, Location loc)
4999 public VariableDeclaration (Expression expr)
5002 loc = expr.Location;
5008 public bool IsNested { get; private set; }
5012 public void EmitDispose (EmitContext ec)
5014 dispose_call.Emit (ec);
5017 public override bool Resolve (BlockContext bc)
5022 return base.Resolve (bc, false);
5025 public Expression ResolveExpression (BlockContext bc)
5027 var e = Initializer.Resolve (bc);
5031 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5032 Initializer = ResolveInitializer (bc, Variable, e);
5036 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5038 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5039 initializer = initializer.Resolve (bc);
5040 if (initializer == null)
5043 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5044 Arguments args = new Arguments (1);
5045 args.Add (new Argument (initializer));
5046 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5047 if (initializer == null)
5050 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5051 dispose_call = CreateDisposeCall (bc, var);
5052 dispose_call.Resolve (bc);
5054 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5057 if (li == Variable) {
5058 CheckIDiposableConversion (bc, li, initializer);
5059 dispose_call = CreateDisposeCall (bc, li);
5060 dispose_call.Resolve (bc);
5063 return base.ResolveInitializer (bc, li, initializer);
5066 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5070 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5071 if (type.IsNullableType) {
5072 // it's handled in CreateDisposeCall
5076 bc.Report.SymbolRelatedToPreviousError (type);
5077 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5078 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5079 type.GetSignatureForError ());
5085 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5087 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5089 var loc = lv.Location;
5091 var idt = bc.BuiltinTypes.IDisposable;
5092 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5094 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5095 dispose_mg.InstanceExpression = type.IsNullableType ?
5096 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5099 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5101 // Add conditional call when disposing possible null variable
5102 if (!type.IsStruct || type.IsNullableType)
5103 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
5108 public void ResolveDeclaratorInitializer (BlockContext bc)
5110 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5113 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5115 for (int i = declarators.Count - 1; i >= 0; --i) {
5116 var d = declarators [i];
5117 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5118 vd.Initializer = d.Initializer;
5120 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5121 vd.dispose_call.Resolve (bc);
5123 stmt = new Using (vd, stmt, d.Variable.Location);
5131 VariableDeclaration decl;
5133 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5139 public Using (Expression expr, Statement stmt, Location loc)
5142 this.decl = new VariableDeclaration (expr);
5147 public Expression Expression {
5149 return decl.Variable == null ? decl.Initializer : null;
5153 public BlockVariableDeclaration Variables {
5161 protected override void EmitTryBodyPrepare (EmitContext ec)
5164 base.EmitTryBodyPrepare (ec);
5167 protected override void EmitTryBody (EmitContext ec)
5172 protected override void EmitFinallyBody (EmitContext ec)
5174 decl.EmitDispose (ec);
5177 public override bool Resolve (BlockContext ec)
5179 VariableReference vr;
5180 bool vr_locked = false;
5182 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5183 if (decl.Variable == null) {
5184 vr = decl.ResolveExpression (ec) as VariableReference;
5186 vr_locked = vr.IsLockedByStatement;
5187 vr.IsLockedByStatement = true;
5190 if (decl.IsNested) {
5191 decl.ResolveDeclaratorInitializer (ec);
5193 if (!decl.Resolve (ec))
5196 if (decl.Declarators != null) {
5197 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5205 ec.StartFlowBranching (this);
5209 ec.EndFlowBranching ();
5212 vr.IsLockedByStatement = vr_locked;
5219 protected override void CloneTo (CloneContext clonectx, Statement t)
5221 Using target = (Using) t;
5223 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5224 target.stmt = stmt.Clone (clonectx);
5229 /// Implementation of the foreach C# statement
5231 public class Foreach : Statement
5233 sealed class ArrayForeach : Statement
5235 readonly Foreach for_each;
5236 readonly Statement statement;
5239 TemporaryVariableReference[] lengths;
5240 Expression [] length_exprs;
5241 StatementExpression[] counter;
5242 TemporaryVariableReference[] variables;
5244 TemporaryVariableReference copy;
5246 LocalVariableReference variable;
5248 public ArrayForeach (Foreach @foreach, int rank)
5250 for_each = @foreach;
5251 statement = for_each.statement;
5254 counter = new StatementExpression[rank];
5255 variables = new TemporaryVariableReference[rank];
5256 length_exprs = new Expression [rank];
5259 // Only use temporary length variables when dealing with
5260 // multi-dimensional arrays
5263 lengths = new TemporaryVariableReference [rank];
5266 protected override void CloneTo (CloneContext clonectx, Statement target)
5268 throw new NotImplementedException ();
5271 public override bool Resolve (BlockContext ec)
5273 Block variables_block = for_each.variable.Block;
5274 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5277 int rank = length_exprs.Length;
5278 Arguments list = new Arguments (rank);
5279 for (int i = 0; i < rank; i++) {
5280 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5282 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5283 counter[i].Resolve (ec);
5286 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5288 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5289 lengths[i].Resolve (ec);
5291 Arguments args = new Arguments (1);
5292 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5293 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5296 list.Add (new Argument (v));
5299 access = new ElementAccess (copy, list, loc).Resolve (ec);
5304 if (for_each.type is VarExpr) {
5305 // Infer implicitly typed local variable from foreach array type
5306 var_type = access.Type;
5308 var_type = for_each.type.ResolveAsType (ec);
5311 if (var_type == null)
5314 conv = Convert.ExplicitConversion (ec, access, var_type, loc);
5320 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5321 ec.CurrentBranching.CreateSibling ();
5323 for_each.variable.Type = conv.Type;
5324 variable = new LocalVariableReference (for_each.variable, loc);
5325 variable.Resolve (ec);
5327 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5328 if (!statement.Resolve (ec))
5330 ec.EndFlowBranching ();
5332 // There's no direct control flow from the end of the embedded statement to the end of the loop
5333 ec.CurrentBranching.CurrentUsageVector.Goto ();
5335 ec.EndFlowBranching ();
5340 protected override void DoEmit (EmitContext ec)
5342 copy.EmitAssign (ec, for_each.expr);
5344 int rank = length_exprs.Length;
5345 Label[] test = new Label [rank];
5346 Label[] loop = new Label [rank];
5348 for (int i = 0; i < rank; i++) {
5349 test [i] = ec.DefineLabel ();
5350 loop [i] = ec.DefineLabel ();
5352 if (lengths != null)
5353 lengths [i].EmitAssign (ec, length_exprs [i]);
5356 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5357 for (int i = 0; i < rank; i++) {
5358 variables [i].EmitAssign (ec, zero);
5360 ec.Emit (OpCodes.Br, test [i]);
5361 ec.MarkLabel (loop [i]);
5364 variable.local_info.CreateBuilder (ec);
5365 variable.EmitAssign (ec, conv, false, false);
5367 statement.Emit (ec);
5369 ec.MarkLabel (ec.LoopBegin);
5371 for (int i = rank - 1; i >= 0; i--){
5372 counter [i].Emit (ec);
5374 ec.MarkLabel (test [i]);
5375 variables [i].Emit (ec);
5377 if (lengths != null)
5378 lengths [i].Emit (ec);
5380 length_exprs [i].Emit (ec);
5382 ec.Emit (OpCodes.Blt, loop [i]);
5385 ec.MarkLabel (ec.LoopEnd);
5389 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5391 class Body : Statement
5394 LocalVariableReference variable;
5395 Expression current, conv;
5396 Statement statement;
5398 public Body (TypeSpec type, LocalVariable variable,
5399 Expression current, Statement statement,
5403 this.variable = new LocalVariableReference (variable, loc);
5404 this.current = current;
5405 this.statement = statement;
5409 protected override void CloneTo (CloneContext clonectx, Statement target)
5411 throw new NotImplementedException ();
5414 public override bool Resolve (BlockContext ec)
5416 current = current.Resolve (ec);
5417 if (current == null)
5420 conv = Convert.ExplicitConversion (ec, current, type, loc);
5424 variable.local_info.Type = conv.Type;
5425 variable.Resolve (ec);
5427 if (!statement.Resolve (ec))
5433 protected override void DoEmit (EmitContext ec)
5435 variable.local_info.CreateBuilder (ec);
5436 variable.EmitAssign (ec, conv, false, false);
5438 statement.Emit (ec);
5442 class RuntimeDispose : Using.VariableDeclaration
5444 public RuntimeDispose (LocalVariable lv, Location loc)
5449 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5451 // Defered to runtime check
5454 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5456 var idt = bc.BuiltinTypes.IDisposable;
5459 // Fabricates code like
5461 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5464 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
5466 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5467 dispose_variable.CreateReferenceExpression (bc, loc),
5468 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5469 loc), new NullLiteral (loc), loc);
5471 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5473 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5474 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5476 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5477 return new If (idisaposable_test, dispose, loc);
5481 LocalVariable variable;
5483 Statement statement;
5484 Expression var_type;
5485 ExpressionStatement init;
5486 TemporaryVariableReference enumerator_variable;
5487 bool ambiguous_getenumerator_name;
5489 public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5491 this.var_type = var_type;
5492 this.variable = var;
5498 protected override void CloneTo (CloneContext clonectx, Statement target)
5500 throw new NotImplementedException ();
5503 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5505 rc.Report.SymbolRelatedToPreviousError (enumerator);
5506 rc.Report.Error (202, loc,
5507 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5508 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5511 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5514 // Option 1: Try to match by name GetEnumerator first
5516 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
5517 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5519 var mg = mexpr as MethodGroupExpr;
5521 mg.InstanceExpression = expr;
5522 Arguments args = new Arguments (0);
5523 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5525 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5526 if (ambiguous_getenumerator_name)
5529 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5535 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5538 PredefinedMember<MethodSpec> iface_candidate = null;
5539 var ptypes = rc.Module.PredefinedTypes;
5540 var gen_ienumerable = ptypes.IEnumerableGeneric;
5541 if (!gen_ienumerable.Define ())
5542 gen_ienumerable = null;
5545 var ifaces = t.Interfaces;
5546 if (ifaces != null) {
5547 foreach (var iface in ifaces) {
5548 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
5549 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
5550 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5551 rc.Report.Error (1640, loc,
5552 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5553 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
5558 // TODO: Cache this somehow
5559 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
5560 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
5565 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
5566 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
5571 if (t.IsGenericParameter)
5576 } while (t != null);
5578 if (iface_candidate == null) {
5579 rc.Report.Error (1579, loc,
5580 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5581 expr.Type.GetSignatureForError (), "GetEnumerator");
5586 var method = iface_candidate.Resolve (loc);
5590 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5591 mg.InstanceExpression = expr;
5595 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5597 var ms = MemberCache.FindMember (enumerator.ReturnType,
5598 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
5599 BindingRestriction.InstanceOnly) as MethodSpec;
5601 if (ms == null || !ms.IsPublic) {
5602 Error_WrongEnumerator (rc, enumerator);
5606 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5609 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5611 var ps = MemberCache.FindMember (enumerator.ReturnType,
5612 MemberFilter.Property ("Current", null),
5613 BindingRestriction.InstanceOnly) as PropertySpec;
5615 if (ps == null || !ps.IsPublic) {
5616 Error_WrongEnumerator (rc, enumerator);
5623 public override bool Resolve (BlockContext ec)
5625 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
5628 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
5629 } else if (expr.Type.IsNullableType) {
5630 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
5633 var get_enumerator_mg = ResolveGetEnumerator (ec);
5634 if (get_enumerator_mg == null) {
5638 var get_enumerator = get_enumerator_mg.BestCandidate;
5639 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
5640 enumerator_variable.Resolve (ec);
5642 // Prepare bool MoveNext ()
5643 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5644 if (move_next_mg == null) {
5648 move_next_mg.InstanceExpression = enumerator_variable;
5650 // Prepare ~T~ Current { get; }
5651 var current_prop = ResolveCurrent (ec, get_enumerator);
5652 if (current_prop == null) {
5656 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
5657 if (current_pe == null)
5660 VarExpr ve = var_type as VarExpr;
5664 // Source type is dynamic, set element type to dynamic too
5665 variable.Type = ec.BuiltinTypes.Dynamic;
5667 // Infer implicitly typed local variable from foreach enumerable type
5668 variable.Type = current_pe.Type;
5672 // Explicit cast of dynamic collection elements has to be done at runtime
5673 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
5676 variable.Type = var_type.ResolveAsType (ec);
5679 if (variable.Type == null)
5682 var init = new Invocation (get_enumerator_mg, null);
5684 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5685 new Body (variable.Type, variable, current_pe, statement, loc), loc);
5687 var enum_type = enumerator_variable.Type;
5690 // Add Dispose method call when enumerator can be IDisposable
5692 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
5693 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
5695 // Runtime Dispose check
5697 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
5698 vd.Initializer = init;
5699 statement = new Using (vd, statement, loc);
5702 // No Dispose call needed
5704 this.init = new SimpleAssign (enumerator_variable, init);
5705 this.init.Resolve (ec);
5709 // Static Dispose check
5711 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
5712 vd.Initializer = init;
5713 statement = new Using (vd, statement, loc);
5716 return statement.Resolve (ec);
5719 protected override void DoEmit (EmitContext ec)
5721 enumerator_variable.LocalInfo.CreateBuilder (ec);
5724 init.EmitStatement (ec);
5726 statement.Emit (ec);
5729 #region IErrorHandler Members
5731 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5733 ec.Report.SymbolRelatedToPreviousError (best);
5734 ec.Report.Warning (278, 2, loc,
5735 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5736 expr.Type.GetSignatureForError (), "enumerable",
5737 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5739 ambiguous_getenumerator_name = true;
5743 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5748 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5753 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5762 LocalVariable variable;
5764 Statement statement;
5766 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
5769 this.variable = var;
5775 public Statement Statement {
5776 get { return statement; }
5779 public override bool Resolve (BlockContext ec)
5781 expr = expr.Resolve (ec);
5786 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5790 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5791 statement = new ArrayForeach (this, 1);
5792 } else if (expr.Type is ArrayContainer) {
5793 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5795 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5796 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5797 expr.ExprClassName);
5801 statement = new CollectionForeach (type, variable, expr, statement, loc);
5804 return statement.Resolve (ec);
5807 protected override void DoEmit (EmitContext ec)
5809 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5810 ec.LoopBegin = ec.DefineLabel ();
5811 ec.LoopEnd = ec.DefineLabel ();
5813 statement.Emit (ec);
5815 ec.LoopBegin = old_begin;
5816 ec.LoopEnd = old_end;
5819 protected override void CloneTo (CloneContext clonectx, Statement t)
5821 Foreach target = (Foreach) t;
5823 target.type = type.Clone (clonectx);
5824 target.expr = expr.Clone (clonectx);
5825 target.statement = statement.Clone (clonectx);