2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@seznam.cz)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
14 using System.Collections.Generic;
17 using IKVM.Reflection.Emit;
19 using System.Reflection.Emit;
22 namespace Mono.CSharp {
24 public abstract class Statement {
28 /// Resolves the statement, true means that all sub-statements
31 public virtual bool Resolve (BlockContext bc)
37 /// We already know that the statement is unreachable, but we still
38 /// need to resolve it to catch errors.
40 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
43 // This conflicts with csc's way of doing this, but IMHO it's
44 // the right thing to do.
46 // If something is unreachable, we still check whether it's
47 // correct. This means that you cannot use unassigned variables
48 // in unreachable code, for instance.
52 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
54 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
55 bool ok = Resolve (ec);
56 ec.KillFlowBranching ();
62 /// Return value indicates whether all code paths emitted return.
64 protected abstract void DoEmit (EmitContext ec);
66 public virtual void Emit (EmitContext ec)
73 // This routine must be overrided in derived classes and make copies
74 // of all the data that might be modified if resolved
76 protected abstract void CloneTo (CloneContext clonectx, Statement target);
78 public Statement Clone (CloneContext clonectx)
80 Statement s = (Statement) this.MemberwiseClone ();
81 CloneTo (clonectx, s);
85 public virtual Expression CreateExpressionTree (ResolveContext ec)
87 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
92 public sealed class EmptyStatement : Statement
94 public EmptyStatement (Location loc)
99 public override bool Resolve (BlockContext ec)
104 public override bool ResolveUnreachable (BlockContext ec, bool warn)
109 public override void Emit (EmitContext ec)
113 protected override void DoEmit (EmitContext ec)
115 throw new NotSupportedException ();
118 protected override void CloneTo (CloneContext clonectx, Statement target)
124 public class If : Statement {
126 public Statement TrueStatement;
127 public Statement FalseStatement;
131 public If (Expression bool_expr, Statement true_statement, Location l)
132 : this (bool_expr, true_statement, null, l)
136 public If (Expression bool_expr,
137 Statement true_statement,
138 Statement false_statement,
141 this.expr = bool_expr;
142 TrueStatement = true_statement;
143 FalseStatement = false_statement;
147 public override bool Resolve (BlockContext ec)
151 expr = expr.Resolve (ec);
156 // Dead code elimination
158 if (expr is Constant) {
159 bool take = !((Constant) expr).IsDefaultValue;
162 if (!TrueStatement.Resolve (ec))
165 if ((FalseStatement != null) &&
166 !FalseStatement.ResolveUnreachable (ec, true))
168 FalseStatement = null;
170 if (!TrueStatement.ResolveUnreachable (ec, true))
172 TrueStatement = null;
174 if ((FalseStatement != null) &&
175 !FalseStatement.Resolve (ec))
183 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
185 ok &= TrueStatement.Resolve (ec);
187 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
189 ec.CurrentBranching.CreateSibling ();
191 if (FalseStatement != null)
192 ok &= FalseStatement.Resolve (ec);
194 ec.EndFlowBranching ();
199 protected override void DoEmit (EmitContext ec)
201 Label false_target = ec.DefineLabel ();
205 // If we're a boolean constant, Resolve() already
206 // eliminated dead code for us.
208 Constant c = expr as Constant;
210 c.EmitSideEffect (ec);
212 if (!c.IsDefaultValue)
213 TrueStatement.Emit (ec);
214 else if (FalseStatement != null)
215 FalseStatement.Emit (ec);
220 expr.EmitBranchable (ec, false_target, false);
222 TrueStatement.Emit (ec);
224 if (FalseStatement != null){
225 bool branch_emitted = false;
227 end = ec.DefineLabel ();
229 ec.Emit (OpCodes.Br, end);
230 branch_emitted = true;
233 ec.MarkLabel (false_target);
234 FalseStatement.Emit (ec);
239 ec.MarkLabel (false_target);
243 protected override void CloneTo (CloneContext clonectx, Statement t)
247 target.expr = expr.Clone (clonectx);
248 target.TrueStatement = TrueStatement.Clone (clonectx);
249 if (FalseStatement != null)
250 target.FalseStatement = FalseStatement.Clone (clonectx);
254 public class Do : Statement {
255 public Expression expr;
256 public Statement EmbeddedStatement;
258 public Do (Statement statement, BooleanExpression bool_expr, Location l)
261 EmbeddedStatement = statement;
265 public override bool Resolve (BlockContext ec)
269 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
271 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
273 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
274 if (!EmbeddedStatement.Resolve (ec))
276 ec.EndFlowBranching ();
278 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
279 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
281 expr = expr.Resolve (ec);
284 else if (expr is Constant){
285 bool infinite = !((Constant) expr).IsDefaultValue;
287 ec.CurrentBranching.CurrentUsageVector.Goto ();
290 ec.EndFlowBranching ();
295 protected override void DoEmit (EmitContext ec)
297 Label loop = ec.DefineLabel ();
298 Label old_begin = ec.LoopBegin;
299 Label old_end = ec.LoopEnd;
301 ec.LoopBegin = ec.DefineLabel ();
302 ec.LoopEnd = ec.DefineLabel ();
305 EmbeddedStatement.Emit (ec);
306 ec.MarkLabel (ec.LoopBegin);
309 // Dead code elimination
311 if (expr is Constant){
312 bool res = !((Constant) expr).IsDefaultValue;
314 expr.EmitSideEffect (ec);
316 ec.Emit (OpCodes.Br, loop);
318 expr.EmitBranchable (ec, loop, true);
320 ec.MarkLabel (ec.LoopEnd);
322 ec.LoopBegin = old_begin;
323 ec.LoopEnd = old_end;
326 protected override void CloneTo (CloneContext clonectx, Statement t)
330 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
331 target.expr = expr.Clone (clonectx);
335 public class While : Statement {
336 public Expression expr;
337 public Statement Statement;
338 bool infinite, empty;
340 public While (BooleanExpression bool_expr, Statement statement, Location l)
342 this.expr = bool_expr;
343 Statement = statement;
347 public override bool Resolve (BlockContext ec)
351 expr = expr.Resolve (ec);
356 // Inform whether we are infinite or not
358 if (expr is Constant){
359 bool value = !((Constant) expr).IsDefaultValue;
362 if (!Statement.ResolveUnreachable (ec, true))
370 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
372 ec.CurrentBranching.CreateSibling ();
374 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
375 if (!Statement.Resolve (ec))
377 ec.EndFlowBranching ();
379 // There's no direct control flow from the end of the embedded statement to the end of the loop
380 ec.CurrentBranching.CurrentUsageVector.Goto ();
382 ec.EndFlowBranching ();
387 protected override void DoEmit (EmitContext ec)
390 expr.EmitSideEffect (ec);
394 Label old_begin = ec.LoopBegin;
395 Label old_end = ec.LoopEnd;
397 ec.LoopBegin = ec.DefineLabel ();
398 ec.LoopEnd = ec.DefineLabel ();
401 // Inform whether we are infinite or not
403 if (expr is Constant){
404 // expr is 'true', since the 'empty' case above handles the 'false' case
405 ec.MarkLabel (ec.LoopBegin);
406 expr.EmitSideEffect (ec);
408 ec.Emit (OpCodes.Br, ec.LoopBegin);
411 // Inform that we are infinite (ie, `we return'), only
412 // if we do not `break' inside the code.
414 ec.MarkLabel (ec.LoopEnd);
416 Label while_loop = ec.DefineLabel ();
418 ec.Emit (OpCodes.Br, ec.LoopBegin);
419 ec.MarkLabel (while_loop);
423 ec.MarkLabel (ec.LoopBegin);
426 expr.EmitBranchable (ec, while_loop, true);
428 ec.MarkLabel (ec.LoopEnd);
431 ec.LoopBegin = old_begin;
432 ec.LoopEnd = old_end;
435 public override void Emit (EmitContext ec)
440 protected override void CloneTo (CloneContext clonectx, Statement t)
442 While target = (While) t;
444 target.expr = expr.Clone (clonectx);
445 target.Statement = Statement.Clone (clonectx);
449 public class For : Statement {
451 Statement InitStatement;
453 public Statement Statement;
454 bool infinite, empty;
456 public For (Statement init_statement,
457 BooleanExpression test,
462 InitStatement = init_statement;
464 Increment = increment;
465 Statement = statement;
469 public override bool Resolve (BlockContext ec)
473 if (InitStatement != null){
474 if (!InitStatement.Resolve (ec))
479 Test = Test.Resolve (ec);
482 else if (Test is Constant){
483 bool value = !((Constant) Test).IsDefaultValue;
486 if (!Statement.ResolveUnreachable (ec, true))
488 if ((Increment != null) &&
489 !Increment.ResolveUnreachable (ec, false))
499 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
501 ec.CurrentBranching.CreateSibling ();
503 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
505 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
506 if (!Statement.Resolve (ec))
508 ec.EndFlowBranching ();
510 if (Increment != null){
511 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
512 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
515 if (!Increment.Resolve (ec))
520 // There's no direct control flow from the end of the embedded statement to the end of the loop
521 ec.CurrentBranching.CurrentUsageVector.Goto ();
523 ec.EndFlowBranching ();
528 protected override void DoEmit (EmitContext ec)
530 if (InitStatement != null)
531 InitStatement.Emit (ec);
534 Test.EmitSideEffect (ec);
538 Label old_begin = ec.LoopBegin;
539 Label old_end = ec.LoopEnd;
540 Label loop = ec.DefineLabel ();
541 Label test = ec.DefineLabel ();
543 ec.LoopBegin = ec.DefineLabel ();
544 ec.LoopEnd = ec.DefineLabel ();
546 ec.Emit (OpCodes.Br, test);
550 ec.MarkLabel (ec.LoopBegin);
555 // If test is null, there is no test, and we are just
560 // The Resolve code already catches the case for
561 // Test == Constant (false) so we know that
564 if (Test is Constant) {
565 Test.EmitSideEffect (ec);
566 ec.Emit (OpCodes.Br, loop);
568 Test.EmitBranchable (ec, loop, true);
572 ec.Emit (OpCodes.Br, loop);
573 ec.MarkLabel (ec.LoopEnd);
575 ec.LoopBegin = old_begin;
576 ec.LoopEnd = old_end;
579 protected override void CloneTo (CloneContext clonectx, Statement t)
581 For target = (For) t;
583 if (InitStatement != null)
584 target.InitStatement = InitStatement.Clone (clonectx);
586 target.Test = Test.Clone (clonectx);
587 if (Increment != null)
588 target.Increment = Increment.Clone (clonectx);
589 target.Statement = Statement.Clone (clonectx);
593 public class StatementExpression : Statement
595 ExpressionStatement expr;
597 public StatementExpression (ExpressionStatement expr)
603 protected override void CloneTo (CloneContext clonectx, Statement t)
605 StatementExpression target = (StatementExpression) t;
606 target.expr = (ExpressionStatement) expr.Clone (clonectx);
609 protected override void DoEmit (EmitContext ec)
611 expr.EmitStatement (ec);
614 public override bool Resolve (BlockContext ec)
616 expr = expr.ResolveStatement (ec);
622 // Simple version of statement list not requiring a block
624 public class StatementList : Statement
626 List<Statement> statements;
628 public StatementList (Statement first, Statement second)
630 statements = new List<Statement> () { first, second };
634 public IList<Statement> Statements {
641 public void Add (Statement statement)
643 statements.Add (statement);
646 public override bool Resolve (BlockContext ec)
648 foreach (var s in statements)
654 protected override void DoEmit (EmitContext ec)
656 foreach (var s in statements)
660 protected override void CloneTo (CloneContext clonectx, Statement target)
662 StatementList t = (StatementList) target;
664 t.statements = new List<Statement> (statements.Count);
665 foreach (Statement s in statements)
666 t.statements.Add (s.Clone (clonectx));
670 // A 'return' or a 'yield break'
671 public abstract class ExitStatement : Statement
673 protected bool unwind_protect;
674 protected abstract bool DoResolve (BlockContext ec);
676 public virtual void Error_FinallyClause (Report Report)
678 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
681 public sealed override bool Resolve (BlockContext ec)
686 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
687 ec.CurrentBranching.CurrentUsageVector.Goto ();
693 /// Implements the return statement
695 public class Return : ExitStatement
697 protected Expression Expr;
699 public Return (Expression expr, Location l)
706 public Expression Expression {
713 protected override bool DoResolve (BlockContext ec)
716 if (ec.ReturnType.Kind == MemberKind.Void)
720 // Return must not be followed by an expression when
721 // the method return type is Task
723 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
724 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
725 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
727 // Extra trick not to emit ret/leave inside awaiter body
729 Expr = EmptyExpression.Null;
734 if (ec.CurrentIterator != null) {
735 Error_ReturnFromIterator (ec);
737 ec.Report.Error (126, loc,
738 "An object of a type convertible to `{0}' is required for the return statement",
739 ec.ReturnType.GetSignatureForError ());
745 Expr = Expr.Resolve (ec);
746 TypeSpec block_return_type = ec.ReturnType;
748 AnonymousExpression am = ec.CurrentAnonymousMethod;
750 if (block_return_type.Kind == MemberKind.Void) {
751 ec.Report.Error (127, loc,
752 "`{0}': A return keyword must not be followed by any expression when method returns void",
753 ec.GetSignatureForError ());
757 Error_ReturnFromIterator (ec);
761 var async_block = am as AsyncInitializer;
762 if (async_block != null) {
764 var storey = (AsyncTaskStorey) am.Storey;
765 var async_type = storey.ReturnType;
767 if (async_type == null && async_block.ReturnTypeInference != null) {
768 async_block.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
772 if (!async_type.IsGenericTask) {
773 if (this is ContextualReturn)
776 ec.Report.Error (1997, loc,
777 "`{0}': A return keyword must not be followed by an expression when async method returns Task. Consider using Task<T>",
778 ec.GetSignatureForError ());
783 // The return type is actually Task<T> type argument
785 block_return_type = async_type.TypeArguments[0];
788 var l = am as AnonymousMethodBody;
789 if (l != null && l.ReturnTypeInference != null && Expr != null) {
790 l.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
799 if (Expr.Type != block_return_type) {
800 Expr = Convert.ImplicitConversionRequired (ec, Expr, block_return_type, loc);
803 if (am != null && block_return_type == ec.ReturnType) {
804 ec.Report.Error (1662, loc,
805 "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",
806 am.ContainerType, am.GetSignatureForError ());
815 protected override void DoEmit (EmitContext ec)
820 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
821 if (async_body != null) {
822 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
824 // It's null for await without async
825 if (async_return != null) {
826 async_return.EmitAssign (ec);
828 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
835 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
839 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
841 ec.Emit (OpCodes.Ret);
844 void Error_ReturnFromIterator (ResolveContext rc)
846 rc.Report.Error (1622, loc,
847 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
850 protected override void CloneTo (CloneContext clonectx, Statement t)
852 Return target = (Return) t;
853 // It's null for simple return;
855 target.Expr = Expr.Clone (clonectx);
859 public class Goto : Statement {
861 LabeledStatement label;
864 public override bool Resolve (BlockContext ec)
866 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
867 ec.CurrentBranching.CurrentUsageVector.Goto ();
871 public Goto (string label, Location l)
877 public string Target {
878 get { return target; }
881 public void SetResolvedTarget (LabeledStatement label)
884 label.AddReference ();
887 protected override void CloneTo (CloneContext clonectx, Statement target)
892 protected override void DoEmit (EmitContext ec)
895 throw new InternalErrorException ("goto emitted before target resolved");
896 Label l = label.LabelTarget (ec);
897 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
901 public class LabeledStatement : Statement {
908 FlowBranching.UsageVector vectors;
910 public LabeledStatement (string name, Block block, Location l)
917 public Label LabelTarget (EmitContext ec)
922 label = ec.DefineLabel ();
937 public bool IsDefined {
938 get { return defined; }
941 public bool HasBeenReferenced {
942 get { return referenced; }
945 public FlowBranching.UsageVector JumpOrigins {
946 get { return vectors; }
949 public void AddUsageVector (FlowBranching.UsageVector vector)
951 vector = vector.Clone ();
952 vector.Next = vectors;
956 protected override void CloneTo (CloneContext clonectx, Statement target)
961 public override bool Resolve (BlockContext ec)
963 // this flow-branching will be terminated when the surrounding block ends
964 ec.StartFlowBranching (this);
968 protected override void DoEmit (EmitContext ec)
970 if (!HasBeenReferenced)
971 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
974 ec.MarkLabel (label);
977 public void AddReference ()
985 /// `goto default' statement
987 public class GotoDefault : Statement {
989 public GotoDefault (Location l)
994 protected override void CloneTo (CloneContext clonectx, Statement target)
999 public override bool Resolve (BlockContext ec)
1001 ec.CurrentBranching.CurrentUsageVector.Goto ();
1003 if (ec.Switch == null) {
1004 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1008 if (!ec.Switch.GotDefault) {
1009 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1016 protected override void DoEmit (EmitContext ec)
1018 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
1023 /// `goto case' statement
1025 public class GotoCase : Statement {
1029 public GotoCase (Expression e, Location l)
1035 public override bool Resolve (BlockContext ec)
1037 if (ec.Switch == null){
1038 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1042 ec.CurrentBranching.CurrentUsageVector.Goto ();
1044 expr = expr.Resolve (ec);
1048 Constant c = expr as Constant;
1050 ec.Report.Error (150, expr.Location, "A constant value is expected");
1055 if (ec.Switch.IsNullable && c is NullLiteral) {
1058 TypeSpec type = ec.Switch.SwitchType;
1059 res = c.TryReduce (ec, type, c.Location);
1061 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1065 if (!Convert.ImplicitStandardConversionExists (c, type))
1066 ec.Report.Warning (469, 2, loc,
1067 "The `goto case' value is not implicitly convertible to type `{0}'",
1068 TypeManager.CSharpName (type));
1072 sl = ec.Switch.ResolveGotoCase (ec, res);
1076 protected override void DoEmit (EmitContext ec)
1078 ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1081 protected override void CloneTo (CloneContext clonectx, Statement t)
1083 GotoCase target = (GotoCase) t;
1085 target.expr = expr.Clone (clonectx);
1089 public class Throw : Statement {
1092 public Throw (Expression expr, Location l)
1098 public override bool Resolve (BlockContext ec)
1101 ec.CurrentBranching.CurrentUsageVector.Goto ();
1102 return ec.CurrentBranching.CheckRethrow (loc);
1105 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1106 ec.CurrentBranching.CurrentUsageVector.Goto ();
1111 var et = ec.BuiltinTypes.Exception;
1112 if (Convert.ImplicitConversionExists (ec, expr, et))
1113 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1115 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1120 protected override void DoEmit (EmitContext ec)
1123 ec.Emit (OpCodes.Rethrow);
1127 ec.Emit (OpCodes.Throw);
1131 protected override void CloneTo (CloneContext clonectx, Statement t)
1133 Throw target = (Throw) t;
1136 target.expr = expr.Clone (clonectx);
1140 public class Break : Statement {
1142 public Break (Location l)
1147 bool unwind_protect;
1149 public override bool Resolve (BlockContext ec)
1151 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1152 ec.CurrentBranching.CurrentUsageVector.Goto ();
1156 protected override void DoEmit (EmitContext ec)
1158 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1161 protected override void CloneTo (CloneContext clonectx, Statement t)
1167 public class Continue : Statement {
1169 public Continue (Location l)
1174 bool unwind_protect;
1176 public override bool Resolve (BlockContext ec)
1178 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1179 ec.CurrentBranching.CurrentUsageVector.Goto ();
1183 protected override void DoEmit (EmitContext ec)
1185 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1188 protected override void CloneTo (CloneContext clonectx, Statement t)
1194 public interface ILocalVariable
1196 void Emit (EmitContext ec);
1197 void EmitAssign (EmitContext ec);
1198 void EmitAddressOf (EmitContext ec);
1201 public interface INamedBlockVariable
1203 Block Block { get; }
1204 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1205 bool IsDeclared { get; }
1206 Location Location { get; }
1209 public class BlockVariableDeclaration : Statement
1211 public class Declarator
1214 Expression initializer;
1216 public Declarator (LocalVariable li, Expression initializer)
1218 if (li.Type != null)
1219 throw new ArgumentException ("Expected null variable type");
1222 this.initializer = initializer;
1225 public Declarator (Declarator clone, Expression initializer)
1228 this.initializer = initializer;
1233 public LocalVariable Variable {
1239 public Expression Initializer {
1244 initializer = value;
1251 Expression initializer;
1252 protected FullNamedExpression type_expr;
1253 protected LocalVariable li;
1254 protected List<Declarator> declarators;
1256 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1258 this.type_expr = type;
1260 this.loc = type_expr.Location;
1263 protected BlockVariableDeclaration (LocalVariable li)
1270 public List<Declarator> Declarators {
1276 public Expression Initializer {
1281 initializer = value;
1285 public FullNamedExpression TypeExpression {
1291 public LocalVariable Variable {
1299 public void AddDeclarator (Declarator decl)
1301 if (declarators == null)
1302 declarators = new List<Declarator> ();
1304 declarators.Add (decl);
1307 void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1309 var container = bc.CurrentMemberDefinition.Parent;
1311 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1312 new MemberName (li.Name, li.Location), null);
1314 container.AddField (f);
1317 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1321 public override bool Resolve (BlockContext bc)
1323 return Resolve (bc, true);
1326 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1328 if (li.Type == null) {
1329 TypeSpec type = null;
1330 var vexpr = type_expr as VarExpr;
1333 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1334 // same name exists or as a keyword when no type was found
1336 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1337 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1338 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1341 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1345 if (li.IsConstant) {
1346 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1350 if (Initializer == null) {
1351 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1355 if (declarators != null) {
1356 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1360 Initializer = Initializer.Resolve (bc);
1361 if (Initializer != null) {
1362 ((VarExpr) type_expr).InferType (bc, Initializer);
1363 type = type_expr.Type;
1368 type = type_expr.ResolveAsType (bc);
1372 if (li.IsConstant && !type.IsConstantCompatible) {
1373 Const.Error_InvalidConstantType (type, loc, bc.Report);
1378 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1383 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1385 CreateEvaluatorVariable (bc, li);
1387 li.PrepareForFlowAnalysis (bc);
1390 if (initializer != null) {
1391 initializer = ResolveInitializer (bc, li, initializer);
1392 // li.Variable.DefinitelyAssigned
1395 if (declarators != null) {
1396 foreach (var d in declarators) {
1397 d.Variable.Type = li.Type;
1399 CreateEvaluatorVariable (bc, d.Variable);
1401 d.Variable.PrepareForFlowAnalysis (bc);
1404 if (d.Initializer != null && resolveDeclaratorInitializers) {
1405 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1406 // d.Variable.DefinitelyAssigned
1414 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1416 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1417 return a.ResolveStatement (bc);
1420 protected override void DoEmit (EmitContext ec)
1425 li.CreateBuilder (ec);
1427 if (Initializer != null)
1428 ((ExpressionStatement) Initializer).EmitStatement (ec);
1430 if (declarators != null) {
1431 foreach (var d in declarators) {
1432 d.Variable.CreateBuilder (ec);
1433 if (d.Initializer != null)
1434 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1439 protected override void CloneTo (CloneContext clonectx, Statement target)
1441 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1443 if (type_expr != null)
1444 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1446 if (initializer != null)
1447 t.initializer = initializer.Clone (clonectx);
1449 if (declarators != null) {
1450 t.declarators = null;
1451 foreach (var d in declarators)
1452 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1457 public class BlockConstantDeclaration : BlockVariableDeclaration
1459 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1464 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1466 initializer = initializer.Resolve (bc);
1467 if (initializer == null)
1470 var c = initializer as Constant;
1472 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1476 c = c.ConvertImplicitly (li.Type);
1478 if (TypeSpec.IsReferenceType (li.Type))
1479 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1481 initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
1486 li.ConstantValue = c;
1492 // The information about a user-perceived local variable
1494 public class LocalVariable : INamedBlockVariable, ILocalVariable
1501 AddressTaken = 1 << 2,
1502 CompilerGenerated = 1 << 3,
1504 ForeachVariable = 1 << 5,
1505 FixedVariable = 1 << 6,
1506 UsingVariable = 1 << 7,
1507 // DefinitelyAssigned = 1 << 8,
1510 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1514 readonly string name;
1515 readonly Location loc;
1516 readonly Block block;
1518 Constant const_value;
1520 public VariableInfo VariableInfo;
1521 HoistedVariable hoisted_variant;
1523 LocalBuilder builder;
1525 public LocalVariable (Block block, string name, Location loc)
1532 public LocalVariable (Block block, string name, Flags flags, Location loc)
1533 : this (block, name, loc)
1539 // Used by variable declarators
1541 public LocalVariable (LocalVariable li, string name, Location loc)
1542 : this (li.block, name, li.flags, loc)
1548 public bool AddressTaken {
1549 get { return (flags & Flags.AddressTaken) != 0; }
1550 set { flags |= Flags.AddressTaken; }
1553 public Block Block {
1559 public Constant ConstantValue {
1564 const_value = value;
1569 // Hoisted local variable variant
1571 public HoistedVariable HoistedVariant {
1573 return hoisted_variant;
1576 hoisted_variant = value;
1580 public bool IsDeclared {
1582 return type != null;
1586 public bool IsConstant {
1588 return (flags & Flags.Constant) != 0;
1592 public bool IsLocked {
1594 return (flags & Flags.IsLocked) != 0;
1597 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1601 public bool IsThis {
1603 return (flags & Flags.IsThis) != 0;
1607 public bool IsFixed {
1609 return (flags & Flags.FixedVariable) != 0;
1613 public bool IsReadonly {
1615 return (flags & Flags.ReadonlyMask) != 0;
1619 public Location Location {
1625 public string Name {
1631 public TypeSpec Type {
1642 public void CreateBuilder (EmitContext ec)
1644 if ((flags & Flags.Used) == 0) {
1645 if (VariableInfo == null) {
1646 // Missing flow analysis or wrong variable flags
1647 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1650 if (VariableInfo.IsEverAssigned)
1651 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1653 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1656 if (HoistedVariant != null)
1659 if (builder != null) {
1660 if ((flags & Flags.CompilerGenerated) != 0)
1663 // To avoid Used warning duplicates
1664 throw new InternalErrorException ("Already created variable `{0}'", name);
1668 // All fixed variabled are pinned, a slot has to be alocated
1670 builder = ec.DeclareLocal (Type, IsFixed);
1671 if (SymbolWriter.HasSymbolWriter)
1672 ec.DefineLocalVariable (name, builder);
1675 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1677 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1682 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1684 if (IsConstant && const_value != null)
1685 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1687 return new LocalVariableReference (this, loc);
1690 public void Emit (EmitContext ec)
1692 // TODO: Need something better for temporary variables
1693 if ((flags & Flags.CompilerGenerated) != 0)
1696 ec.Emit (OpCodes.Ldloc, builder);
1699 public void EmitAssign (EmitContext ec)
1701 // TODO: Need something better for temporary variables
1702 if ((flags & Flags.CompilerGenerated) != 0)
1705 ec.Emit (OpCodes.Stloc, builder);
1708 public void EmitAddressOf (EmitContext ec)
1710 ec.Emit (OpCodes.Ldloca, builder);
1713 public static string GetCompilerGeneratedName (Block block)
1715 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1718 public string GetReadOnlyContext ()
1720 switch (flags & Flags.ReadonlyMask) {
1721 case Flags.FixedVariable:
1722 return "fixed variable";
1723 case Flags.ForeachVariable:
1724 return "foreach iteration variable";
1725 case Flags.UsingVariable:
1726 return "using variable";
1729 throw new InternalErrorException ("Variable is not readonly");
1732 public bool IsThisAssigned (BlockContext ec, Block block)
1734 if (VariableInfo == null)
1735 throw new Exception ();
1737 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1740 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1743 public bool IsAssigned (BlockContext ec)
1745 if (VariableInfo == null)
1746 throw new Exception ();
1748 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1751 public void PrepareForFlowAnalysis (BlockContext bc)
1754 // No need for definitely assigned check for these guys
1756 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1759 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1760 bc.FlowOffset += VariableInfo.Length;
1764 // Mark the variables as referenced in the user code
1766 public void SetIsUsed ()
1768 flags |= Flags.Used;
1771 public override string ToString ()
1773 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
1778 /// Block represents a C# block.
1782 /// This class is used in a number of places: either to represent
1783 /// explicit blocks that the programmer places or implicit blocks.
1785 /// Implicit blocks are used as labels or to introduce variable
1788 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1789 /// they contain extra information that is not necessary on normal blocks.
1791 public class Block : Statement {
1799 HasCapturedVariable = 64,
1800 HasCapturedThis = 1 << 7,
1801 IsExpressionTree = 1 << 8,
1802 CompilerGenerated = 1 << 9,
1806 public Block Parent;
1807 public Location StartLocation;
1808 public Location EndLocation;
1810 public ExplicitBlock Explicit;
1811 public ParametersBlock ParametersBlock;
1813 protected Flags flags;
1816 // The statements in this block
1818 protected List<Statement> statements;
1820 protected List<Statement> scope_initializers;
1822 int? resolving_init_idx;
1824 protected Block original;
1828 public int ID = id++;
1830 static int clone_id_counter;
1834 // int assignable_slots;
1835 bool unreachable_shown;
1838 public Block (Block parent, Location start, Location end)
1839 : this (parent, 0, start, end)
1843 public Block (Block parent, Flags flags, Location start, Location end)
1845 if (parent != null) {
1846 // the appropriate constructors will fixup these fields
1847 ParametersBlock = parent.ParametersBlock;
1848 Explicit = parent.Explicit;
1851 this.Parent = parent;
1853 this.StartLocation = start;
1854 this.EndLocation = end;
1856 statements = new List<Statement> (4);
1858 this.original = this;
1863 public bool HasRet {
1864 get { return (flags & Flags.HasRet) != 0; }
1867 public Block Original {
1873 public bool IsCompilerGenerated {
1874 get { return (flags & Flags.CompilerGenerated) != 0; }
1875 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
1878 public bool Unchecked {
1879 get { return (flags & Flags.Unchecked) != 0; }
1880 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1883 public bool Unsafe {
1884 get { return (flags & Flags.Unsafe) != 0; }
1885 set { flags |= Flags.Unsafe; }
1890 public Block CreateSwitchBlock (Location start)
1892 // FIXME: Only explicit block should be created
1893 var new_block = new Block (this, start, start);
1894 new_block.IsCompilerGenerated = true;
1898 public void SetEndLocation (Location loc)
1903 public void AddLabel (LabeledStatement target)
1905 ParametersBlock.TopBlock.AddLabel (target.Name, target);
1908 public void AddLocalName (LocalVariable li)
1910 AddLocalName (li.Name, li);
1913 public virtual void AddLocalName (string name, INamedBlockVariable li)
1915 ParametersBlock.TopBlock.AddLocalName (name, li);
1918 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
1920 if (reason == null) {
1921 Error_AlreadyDeclared (name, variable);
1925 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
1926 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
1927 "to `{0}', which is already used in a `{1}' scope to denote something else",
1931 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
1933 var pi = variable as ParametersBlock.ParameterInfo;
1935 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
1937 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
1938 "A local variable named `{0}' is already defined in this scope", name);
1942 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
1944 ParametersBlock.TopBlock.Report.Error (412, loc,
1945 "The type parameter name `{0}' is the same as local variable or parameter name",
1950 // It should be used by expressions which require to
1951 // register a statement during resolve process.
1953 public void AddScopeStatement (Statement s)
1955 if (scope_initializers == null)
1956 scope_initializers = new List<Statement> ();
1959 // Simple recursive helper, when resolve scope initializer another
1960 // new scope initializer can be added, this ensures it's initialized
1961 // before existing one. For now this can happen with expression trees
1962 // in base ctor initializer only
1964 if (resolving_init_idx.HasValue) {
1965 scope_initializers.Insert (resolving_init_idx.Value, s);
1966 ++resolving_init_idx;
1968 scope_initializers.Add (s);
1972 public void AddStatement (Statement s)
1977 public int AssignableSlots {
1979 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
1981 // return assignable_slots;
1985 public LabeledStatement LookupLabel (string name)
1987 return ParametersBlock.TopBlock.GetLabel (name, this);
1990 public override bool Resolve (BlockContext ec)
1992 Block prev_block = ec.CurrentBlock;
1995 ec.CurrentBlock = this;
1996 ec.StartFlowBranching (this);
1999 // Compiler generated scope statements
2001 if (scope_initializers != null) {
2002 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2003 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2006 resolving_init_idx = null;
2010 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2011 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2012 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2013 // responsible for handling the situation.
2015 int statement_count = statements.Count;
2016 for (int ix = 0; ix < statement_count; ix++){
2017 Statement s = statements [ix];
2020 // Warn if we detect unreachable code.
2023 if (s is EmptyStatement)
2026 if (!unreachable_shown && !(s is LabeledStatement)) {
2027 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2028 unreachable_shown = true;
2031 Block c_block = s as Block;
2032 if (c_block != null)
2033 c_block.unreachable = c_block.unreachable_shown = true;
2037 // Note that we're not using ResolveUnreachable() for unreachable
2038 // statements here. ResolveUnreachable() creates a temporary
2039 // flow branching and kills it afterwards. This leads to problems
2040 // if you have two unreachable statements where the first one
2041 // assigns a variable and the second one tries to access it.
2044 if (!s.Resolve (ec)) {
2046 if (ec.IsInProbingMode)
2049 statements [ix] = new EmptyStatement (s.loc);
2053 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2054 statements [ix] = new EmptyStatement (s.loc);
2056 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2057 if (unreachable && s is LabeledStatement)
2058 throw new InternalErrorException ("should not happen");
2061 while (ec.CurrentBranching is FlowBranchingLabeled)
2062 ec.EndFlowBranching ();
2064 bool flow_unreachable = ec.EndFlowBranching ();
2066 ec.CurrentBlock = prev_block;
2068 if (flow_unreachable)
2069 flags |= Flags.HasRet;
2071 // If we're a non-static `struct' constructor which doesn't have an
2072 // initializer, then we must initialize all of the struct's fields.
2073 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2079 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2081 unreachable_shown = true;
2085 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2087 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2088 bool ok = Resolve (ec);
2089 ec.KillFlowBranching ();
2094 protected override void DoEmit (EmitContext ec)
2096 for (int ix = 0; ix < statements.Count; ix++){
2097 statements [ix].Emit (ec);
2101 public override void Emit (EmitContext ec)
2103 if (scope_initializers != null)
2104 EmitScopeInitializers (ec);
2106 ec.Mark (StartLocation);
2109 if (SymbolWriter.HasSymbolWriter)
2110 EmitSymbolInfo (ec);
2113 protected void EmitScopeInitializers (EmitContext ec)
2115 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2117 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2118 foreach (Statement s in scope_initializers)
2122 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2125 protected virtual void EmitSymbolInfo (EmitContext ec)
2130 public override string ToString ()
2132 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2136 protected override void CloneTo (CloneContext clonectx, Statement t)
2138 Block target = (Block) t;
2140 target.clone_id = clone_id_counter++;
2143 clonectx.AddBlockMap (this, target);
2144 if (original != this)
2145 clonectx.AddBlockMap (original, target);
2147 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2148 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2151 target.Parent = clonectx.RemapBlockCopy (Parent);
2153 target.statements = new List<Statement> (statements.Count);
2154 foreach (Statement s in statements)
2155 target.statements.Add (s.Clone (clonectx));
2159 public class ExplicitBlock : Block
2161 protected AnonymousMethodStorey am_storey;
2163 public ExplicitBlock (Block parent, Location start, Location end)
2164 : this (parent, (Flags) 0, start, end)
2168 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2169 : base (parent, flags, start, end)
2171 this.Explicit = this;
2176 public AnonymousMethodStorey AnonymousMethodStorey {
2182 public bool HasCapturedThis {
2183 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2185 return (flags & Flags.HasCapturedThis) != 0;
2189 public bool HasCapturedVariable {
2190 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2192 return (flags & Flags.HasCapturedVariable) != 0;
2199 // Creates anonymous method storey in current block
2201 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2204 // An iterator has only 1 storey block
2206 if (ec.CurrentAnonymousMethod.IsIterator)
2207 return ec.CurrentAnonymousMethod.Storey;
2210 // When referencing a variable in iterator storey from children anonymous method
2212 if (ParametersBlock.am_storey is IteratorStorey) {
2213 return ParametersBlock.am_storey;
2216 if (am_storey == null) {
2217 MemberBase mc = ec.MemberContext as MemberBase;
2220 // Creates anonymous method storey for this block
2222 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2228 public override void Emit (EmitContext ec)
2230 if (am_storey != null) {
2231 DefineAnonymousStorey (ec);
2232 am_storey.EmitStoreyInstantiation (ec, this);
2235 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2236 if (emit_debug_info)
2241 if (emit_debug_info)
2245 void DefineAnonymousStorey (EmitContext ec)
2248 // Creates anonymous method storey
2250 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2252 // Creates parent storey reference when hoisted this is accessible
2254 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2255 ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2258 // Hoisted this exists in top-level parent storey only
2260 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2261 parent = parent.Parent.Explicit;
2263 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2266 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2268 // TODO MemberCache: Review
2269 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2272 am_storey.CreateType ();
2273 am_storey.DefineType ();
2274 am_storey.ResolveTypeParameters ();
2276 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2277 if (ref_blocks != null) {
2278 foreach (ExplicitBlock ref_block in ref_blocks) {
2279 for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2280 if (b.am_storey != null) {
2281 b.am_storey.AddParentStoreyReference (ec, am_storey);
2283 // Stop propagation inside same top block
2284 if (b.ParametersBlock.Original == ParametersBlock.Original)
2287 b = b.ParametersBlock;
2290 b.HasCapturedVariable = true;
2295 am_storey.Define ();
2296 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2299 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2301 tryBlock.statements = statements;
2302 statements = new List<Statement> (1);
2303 statements.Add (tf);
2308 // ParametersBlock was introduced to support anonymous methods
2309 // and lambda expressions
2311 public class ParametersBlock : ExplicitBlock
2313 public class ParameterInfo : INamedBlockVariable
2315 readonly ParametersBlock block;
2317 public VariableInfo VariableInfo;
2320 public ParameterInfo (ParametersBlock block, int index)
2328 public Block Block {
2334 public bool IsDeclared {
2340 public bool IsLocked {
2349 public Location Location {
2351 return Parameter.Location;
2355 public Parameter Parameter {
2357 return block.Parameters [index];
2361 public TypeSpec ParameterType {
2363 return Parameter.Type;
2369 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2371 return new ParameterReference (this, loc);
2376 // Block is converted into an expression
2378 sealed class BlockScopeExpression : Expression
2381 readonly ParametersBlock block;
2383 public BlockScopeExpression (Expression child, ParametersBlock block)
2389 public override bool ContainsEmitWithAwait ()
2391 return child.ContainsEmitWithAwait ();
2394 public override Expression CreateExpressionTree (ResolveContext ec)
2396 throw new NotSupportedException ();
2399 protected override Expression DoResolve (ResolveContext ec)
2404 child = child.Resolve (ec);
2408 eclass = child.eclass;
2413 public override void Emit (EmitContext ec)
2415 block.EmitScopeInitializers (ec);
2420 protected ParametersCompiled parameters;
2421 protected ParameterInfo[] parameter_info;
2423 protected bool unreachable;
2424 protected ToplevelBlock top_block;
2426 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2427 : base (parent, 0, start, start)
2429 if (parameters == null)
2430 throw new ArgumentNullException ("parameters");
2432 this.parameters = parameters;
2433 ParametersBlock = this;
2435 this.top_block = parent.ParametersBlock.top_block;
2436 ProcessParameters ();
2439 protected ParametersBlock (ParametersCompiled parameters, Location start)
2440 : base (null, 0, start, start)
2442 if (parameters == null)
2443 throw new ArgumentNullException ("parameters");
2445 this.parameters = parameters;
2446 ParametersBlock = this;
2450 // It's supposed to be used by method body implementation of anonymous methods
2452 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2453 : base (null, 0, source.StartLocation, source.EndLocation)
2455 this.parameters = parameters;
2456 this.statements = source.statements;
2457 this.scope_initializers = source.scope_initializers;
2459 this.resolved = true;
2460 this.unreachable = source.unreachable;
2461 this.am_storey = source.am_storey;
2463 ParametersBlock = this;
2466 // Overwrite original for comparison purposes when linking cross references
2467 // between anonymous methods
2477 return (flags & Flags.IsAsync) != 0;
2480 flags = value ? flags | Flags.IsAsync : flags & ~Flags.IsAsync;
2485 // Block has been converted to expression tree
2487 public bool IsExpressionTree {
2489 return (flags & Flags.IsExpressionTree) != 0;
2494 // The parameters for the block.
2496 public ParametersCompiled Parameters {
2502 public ToplevelBlock TopBlock {
2508 public bool Resolved {
2514 public int TemporaryLocalsCount { get; set; }
2519 // Check whether all `out' parameters have been assigned.
2521 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2523 if (vector.IsUnreachable)
2526 int n = parameter_info == null ? 0 : parameter_info.Length;
2528 for (int i = 0; i < n; i++) {
2529 VariableInfo var = parameter_info[i].VariableInfo;
2534 if (vector.IsAssigned (var, false))
2537 TopBlock.Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2542 public override Expression CreateExpressionTree (ResolveContext ec)
2544 if (statements.Count == 1) {
2545 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2546 if (scope_initializers != null)
2547 expr = new BlockScopeExpression (expr, this);
2552 return base.CreateExpressionTree (ec);
2555 public ParameterInfo GetParameterInfo (Parameter p)
2557 for (int i = 0; i < parameters.Count; ++i) {
2558 if (parameters[i] == p)
2559 return parameter_info[i];
2562 throw new ArgumentException ("Invalid parameter");
2565 public Expression GetParameterReference (int index, Location loc)
2567 return new ParameterReference (parameter_info[index], loc);
2570 public Statement PerformClone ()
2572 CloneContext clonectx = new CloneContext ();
2573 return Clone (clonectx);
2576 protected void ProcessParameters ()
2578 if (parameters.Count == 0)
2581 parameter_info = new ParameterInfo[parameters.Count];
2582 for (int i = 0; i < parameter_info.Length; ++i) {
2583 var p = parameters.FixedParameters[i];
2587 // TODO: Should use Parameter only and more block there
2588 parameter_info[i] = new ParameterInfo (this, i);
2590 AddLocalName (p.Name, parameter_info[i]);
2594 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2601 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2602 flags |= Flags.IsExpressionTree;
2607 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2608 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2613 unreachable = top_level.End ();
2615 } catch (Exception e) {
2616 if (e is CompletionResult || rc.Report.IsDisabled)
2619 if (rc.CurrentBlock != null) {
2620 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2622 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2625 if (rc.Module.Compiler.Settings.DebugFlags > 0)
2629 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
2630 if (rc.CurrentAnonymousMethod == null) {
2631 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2632 if (md is StateMachineMethod) {
2635 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2640 // If an asynchronous body of F is either an expression classified as nothing, or a
2641 // statement block where no return statements have expressions, the inferred return type is Task
2644 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
2645 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
2646 am.ReturnTypeInference = null;
2647 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
2652 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2653 rc.CurrentAnonymousMethod.GetSignatureForError ());
2661 void ResolveMeta (BlockContext ec)
2663 int orig_count = parameters.Count;
2665 for (int i = 0; i < orig_count; ++i) {
2666 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2668 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2671 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2672 parameter_info[i].VariableInfo = vi;
2673 ec.FlowOffset += vi.Length;
2677 public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
2679 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2680 pb.EndLocation = EndLocation;
2681 pb.statements = statements;
2683 var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2684 am_storey = new IteratorStorey (iterator);
2686 statements = new List<Statement> (1);
2687 AddStatement (new Return (iterator, iterator.Location));
2690 public void WrapIntoAsyncTask (IMemberContext context, TypeContainer host, TypeSpec returnType)
2692 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2693 pb.EndLocation = EndLocation;
2694 pb.statements = statements;
2696 var block_type = host.Module.Compiler.BuiltinTypes.Void;
2697 var initializer = new AsyncInitializer (pb, host, block_type);
2698 initializer.Type = block_type;
2700 am_storey = new AsyncTaskStorey (context, initializer, returnType);
2702 statements = new List<Statement> (1);
2703 AddStatement (new StatementExpression (initializer));
2710 public class ToplevelBlock : ParametersBlock
2712 LocalVariable this_variable;
2713 CompilerContext compiler;
2714 Dictionary<string, object> names;
2715 Dictionary<string, object> labels;
2717 public HoistedVariable HoistedThisVariable;
2719 public Report Report {
2720 get { return compiler.Report; }
2723 public ToplevelBlock (CompilerContext ctx, Location loc)
2724 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2728 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2729 : base (parameters, start)
2731 this.compiler = ctx;
2734 ProcessParameters ();
2738 // Recreates a top level block from parameters block. Used for
2739 // compiler generated methods where the original block comes from
2740 // explicit child block. This works for already resolved blocks
2741 // only to ensure we resolve them in the correct flow order
2743 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
2744 : base (source, parameters)
2746 this.compiler = source.TopBlock.compiler;
2750 public bool IsIterator
2753 return (flags & Flags.IsIterator) != 0;
2756 flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator;
2760 public override void AddLocalName (string name, INamedBlockVariable li)
2763 names = new Dictionary<string, object> ();
2766 if (!names.TryGetValue (name, out value)) {
2767 names.Add (name, li);
2771 INamedBlockVariable existing = value as INamedBlockVariable;
2772 List<INamedBlockVariable> existing_list;
2773 if (existing != null) {
2774 existing_list = new List<INamedBlockVariable> ();
2775 existing_list.Add (existing);
2776 names[name] = existing_list;
2778 existing_list = (List<INamedBlockVariable>) value;
2782 // A collision checking between local names
2784 for (int i = 0; i < existing_list.Count; ++i) {
2785 existing = existing_list[i];
2786 Block b = existing.Block;
2788 // Collision at same level
2789 if (li.Block == b) {
2790 li.Block.Error_AlreadyDeclared (name, li);
2794 // Collision with parent
2796 while ((b = b.Parent) != null) {
2797 if (existing.Block == b) {
2798 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
2799 i = existing_list.Count;
2804 // Collision with with children
2806 while ((b = b.Parent) != null) {
2807 if (li.Block == b) {
2808 li.Block.Error_AlreadyDeclared (name, li, "child");
2809 i = existing_list.Count;
2815 existing_list.Add (li);
2818 public void AddLabel (string name, LabeledStatement label)
2821 labels = new Dictionary<string, object> ();
2824 if (!labels.TryGetValue (name, out value)) {
2825 labels.Add (name, label);
2829 LabeledStatement existing = value as LabeledStatement;
2830 List<LabeledStatement> existing_list;
2831 if (existing != null) {
2832 existing_list = new List<LabeledStatement> ();
2833 existing_list.Add (existing);
2834 labels[name] = existing_list;
2836 existing_list = (List<LabeledStatement>) value;
2840 // A collision checking between labels
2842 for (int i = 0; i < existing_list.Count; ++i) {
2843 existing = existing_list[i];
2844 Block b = existing.Block;
2846 // Collision at same level
2847 if (label.Block == b) {
2848 Report.SymbolRelatedToPreviousError (existing.loc, name);
2849 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
2853 // Collision with parent
2855 while ((b = b.Parent) != null) {
2856 if (existing.Block == b) {
2857 Report.Error (158, label.loc,
2858 "The label `{0}' shadows another label by the same name in a contained scope", name);
2859 i = existing_list.Count;
2864 // Collision with with children
2866 while ((b = b.Parent) != null) {
2867 if (label.Block == b) {
2868 Report.Error (158, label.loc,
2869 "The label `{0}' shadows another label by the same name in a contained scope", name);
2870 i = existing_list.Count;
2876 existing_list.Add (label);
2880 // Creates an arguments set from all parameters, useful for method proxy calls
2882 public Arguments GetAllParametersArguments ()
2884 int count = parameters.Count;
2885 Arguments args = new Arguments (count);
2886 for (int i = 0; i < count; ++i) {
2887 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
2888 args.Add (new Argument (arg_expr));
2895 // Lookup inside a block, the returned value can represent 3 states
2897 // true+variable: A local name was found and it's valid
2898 // false+variable: A local name was found in a child block only
2899 // false+null: No local name was found
2901 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
2907 if (!names.TryGetValue (name, out value))
2910 variable = value as INamedBlockVariable;
2912 if (variable != null) {
2914 if (variable.Block == b.Original)
2918 } while (b != null);
2926 } while (b != null);
2928 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
2929 for (int i = 0; i < list.Count; ++i) {
2932 if (variable.Block == b.Original)
2936 } while (b != null);
2944 } while (b != null);
2954 public LabeledStatement GetLabel (string name, Block block)
2960 if (!labels.TryGetValue (name, out value)) {
2964 var label = value as LabeledStatement;
2966 if (label != null) {
2967 if (label.Block == b.Original)
2970 // TODO: Temporary workaround for the switch block implicit label block
2971 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2974 List<LabeledStatement> list = (List<LabeledStatement>) value;
2975 for (int i = 0; i < list.Count; ++i) {
2977 if (label.Block == b.Original)
2980 // TODO: Temporary workaround for the switch block implicit label block
2981 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2990 // Returns the "this" instance variable of this block.
2991 // See AddThisVariable() for more information.
2993 public LocalVariable ThisVariable {
2994 get { return this_variable; }
2998 // This is used by non-static `struct' constructors which do not have an
2999 // initializer - in this case, the constructor must initialize all of the
3000 // struct's fields. To do this, we add a "this" variable and use the flow
3001 // analysis code to ensure that it's been fully initialized before control
3002 // leaves the constructor.
3004 public LocalVariable AddThisVariable (BlockContext bc, TypeContainer ds, Location l)
3006 if (this_variable == null) {
3007 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, l);
3008 this_variable.Type = ds.CurrentType;
3009 this_variable.PrepareForFlowAnalysis (bc);
3012 return this_variable;
3015 public bool IsThisAssigned (BlockContext ec)
3017 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3020 public override void Emit (EmitContext ec)
3022 if (Report.Errors > 0)
3031 ec.Mark (EndLocation);
3033 if (ec.HasReturnLabel)
3034 ec.MarkLabel (ec.ReturnLabel);
3036 if (ec.return_value != null) {
3037 ec.Emit (OpCodes.Ldloc, ec.return_value);
3038 ec.Emit (OpCodes.Ret);
3041 // If `HasReturnLabel' is set, then we already emitted a
3042 // jump to the end of the method, so we must emit a `ret'
3045 // Unfortunately, System.Reflection.Emit automatically emits
3046 // a leave to the end of a finally block. This is a problem
3047 // if no code is following the try/finally block since we may
3048 // jump to a point after the end of the method.
3049 // As a workaround, we're always creating a return label in
3053 if (ec.HasReturnLabel || !unreachable) {
3054 if (ec.ReturnType.Kind != MemberKind.Void)
3055 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3056 ec.Emit (OpCodes.Ret);
3061 } catch (Exception e){
3062 Console.WriteLine ("Exception caught by the compiler while emitting:");
3063 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3065 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3071 protected override void EmitSymbolInfo (EmitContext ec)
3073 AnonymousExpression ae = ec.CurrentAnonymousMethod;
3074 if ((ae != null) && (ae.Storey != null))
3075 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
3077 base.EmitSymbolInfo (ec);
3081 public class SwitchLabel {
3084 readonly Location loc;
3089 // if expr == null, then it is the default case.
3091 public SwitchLabel (Expression expr, Location l)
3097 public bool IsDefault {
3099 return label == null;
3103 public Expression Label {
3109 public Location Location {
3115 public Constant Converted {
3124 public Label GetILLabel (EmitContext ec)
3126 if (il_label == null){
3127 il_label = ec.DefineLabel ();
3130 return il_label.Value;
3134 // Resolves the expression, reduces it to a literal if possible
3135 // and then converts it to the requested type.
3137 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3139 Expression e = label.Resolve (ec);
3144 Constant c = e as Constant;
3146 ec.Report.Error (150, loc, "A constant value is expected");
3150 if (allow_nullable && c is NullLiteral) {
3155 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3156 return converted != null;
3159 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3162 if (converted == null)
3165 label = converted.GetValueAsLiteral ();
3167 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3168 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3171 public SwitchLabel Clone (CloneContext clonectx)
3176 return new SwitchLabel (label.Clone (clonectx), loc);
3180 public class SwitchSection {
3181 public readonly List<SwitchLabel> Labels;
3182 public readonly Block Block;
3184 public SwitchSection (List<SwitchLabel> labels, Block block)
3190 public SwitchSection Clone (CloneContext clonectx)
3192 var cloned_labels = new List<SwitchLabel> ();
3194 foreach (SwitchLabel sl in Labels)
3195 cloned_labels.Add (sl.Clone (clonectx));
3197 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3201 public class Switch : Statement
3203 // structure used to hold blocks of keys while calculating table switch
3204 sealed class LabelsRange : IComparable<LabelsRange>
3206 public readonly long min;
3208 public readonly List<long> label_values;
3210 public LabelsRange (long value)
3213 label_values = new List<long> ();
3214 label_values.Add (value);
3217 public LabelsRange (long min, long max, ICollection<long> values)
3221 this.label_values = new List<long> (values);
3226 return max - min + 1;
3230 public bool AddValue (long value)
3232 var gap = value - min + 1;
3233 // Ensure the range has > 50% occupancy
3234 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3238 label_values.Add (value);
3242 public int CompareTo (LabelsRange other)
3244 int nLength = label_values.Count;
3245 int nLengthOther = other.label_values.Count;
3246 if (nLengthOther == nLength)
3247 return (int) (other.min - min);
3249 return nLength - nLengthOther;
3253 public List<SwitchSection> Sections;
3254 public Expression Expr;
3257 // Mapping of all labels to their SwitchLabels
3259 Dictionary<long, SwitchLabel> labels;
3260 Dictionary<string, SwitchLabel> string_labels;
3263 /// The governing switch type
3265 public TypeSpec SwitchType;
3270 Label default_target;
3272 Expression new_expr;
3275 SwitchSection constant_section;
3276 SwitchSection default_section;
3277 SwitchLabel null_section;
3279 ExpressionStatement string_dictionary;
3280 FieldExpr switch_cache_field;
3281 static int unique_counter;
3282 ExplicitBlock block;
3285 // Nullable Types support
3287 Nullable.Unwrap unwrap;
3289 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3297 public ExplicitBlock Block {
3303 public Label DefaultLabel {
3305 return default_target;
3309 public bool GotDefault {
3311 return default_section != null;
3315 public bool IsNullable {
3317 return unwrap != null;
3322 // Determines the governing type for a switch. The returned
3323 // expression might be the expression from the switch, or an
3324 // expression that includes any potential conversions to
3326 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3328 switch (expr.Type.BuiltinType) {
3329 case BuiltinTypeSpec.Type.Byte:
3330 case BuiltinTypeSpec.Type.SByte:
3331 case BuiltinTypeSpec.Type.UShort:
3332 case BuiltinTypeSpec.Type.Short:
3333 case BuiltinTypeSpec.Type.UInt:
3334 case BuiltinTypeSpec.Type.Int:
3335 case BuiltinTypeSpec.Type.ULong:
3336 case BuiltinTypeSpec.Type.Long:
3337 case BuiltinTypeSpec.Type.Char:
3338 case BuiltinTypeSpec.Type.String:
3339 case BuiltinTypeSpec.Type.Bool:
3343 if (expr.Type.IsEnum)
3347 // Try to find a *user* defined implicit conversion.
3349 // If there is no implicit conversion, or if there are multiple
3350 // conversions, we have to report an error
3352 Expression converted = null;
3353 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3356 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3361 // Ignore over-worked ImplicitUserConversions that do
3362 // an implicit conversion in addition to the user conversion.
3364 if (!(e is UserCast))
3367 if (converted != null){
3368 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3377 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3379 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3395 // Performs the basic sanity checks on the switch statement
3396 // (looks for duplicate keys and non-constant expressions).
3398 // It also returns a hashtable with the keys that we will later
3399 // use to compute the switch tables
3401 bool CheckSwitch (ResolveContext ec)
3404 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3405 string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3407 labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3409 foreach (SwitchSection ss in Sections){
3410 foreach (SwitchLabel sl in ss.Labels){
3412 if (default_section != null){
3413 sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3416 default_section = ss;
3420 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3426 if (string_labels != null) {
3427 string s = sl.Converted.GetValue () as string;
3431 string_labels.Add (s, sl);
3433 if (sl.Converted is NullLiteral) {
3436 labels.Add (sl.Converted.GetValueAsLong (), sl);
3439 } catch (ArgumentException) {
3440 if (string_labels != null)
3441 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3443 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3453 // This method emits code for a lookup-based switch statement (non-string)
3454 // Basically it groups the cases into blocks that are at least half full,
3455 // and then spits out individual lookup opcodes for each block.
3456 // It emits the longest blocks first, and short blocks are just
3457 // handled with direct compares.
3459 void EmitTableSwitch (EmitContext ec, Expression val)
3461 Label lbl_default = default_target;
3463 if (labels.Count > 0) {
3464 List<LabelsRange> ranges;
3465 if (string_labels != null) {
3466 // We have done all hard work for string already
3467 // setup single range only
3468 ranges = new List<LabelsRange> (1);
3469 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3471 var element_keys = new long[labels.Count];
3472 labels.Keys.CopyTo (element_keys, 0);
3473 Array.Sort (element_keys);
3476 // Build possible ranges of switch labes to reduce number
3479 ranges = new List<LabelsRange> (element_keys.Length);
3480 var range = new LabelsRange (element_keys[0]);
3482 for (int i = 1; i < element_keys.Length; ++i) {
3483 var l = element_keys[i];
3484 if (range.AddValue (l))
3487 range = new LabelsRange (l);
3491 // sort the blocks so we can tackle the largest ones first
3495 TypeSpec compare_type = TypeManager.IsEnumType (SwitchType) ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3497 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3498 LabelsRange kb = ranges[range_index];
3499 lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3501 // Optimize small ranges using simple equality check
3502 if (kb.Range <= 2) {
3503 foreach (var key in kb.label_values) {
3504 SwitchLabel sl = labels[key];
3505 if (sl.Converted.IsDefaultValue) {
3506 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3509 sl.Converted.Emit (ec);
3510 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3514 // TODO: if all the keys in the block are the same and there are
3515 // no gaps/defaults then just use a range-check.
3516 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3517 // TODO: optimize constant/I4 cases
3519 // check block range (could be > 2^31)
3521 ec.EmitLong (kb.min);
3522 ec.Emit (OpCodes.Blt, lbl_default);
3525 ec.EmitLong (kb.max);
3526 ec.Emit (OpCodes.Bgt, lbl_default);
3531 ec.EmitLong (kb.min);
3532 ec.Emit (OpCodes.Sub);
3535 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3539 int first = (int) kb.min;
3542 ec.Emit (OpCodes.Sub);
3543 } else if (first < 0) {
3544 ec.EmitInt (-first);
3545 ec.Emit (OpCodes.Add);
3549 // first, build the list of labels for the switch
3551 long cJumps = kb.Range;
3552 Label[] switch_labels = new Label[cJumps];
3553 for (int iJump = 0; iJump < cJumps; iJump++) {
3554 var key = kb.label_values[iKey];
3555 if (key == kb.min + iJump) {
3556 switch_labels[iJump] = labels[key].GetILLabel (ec);
3559 switch_labels[iJump] = lbl_default;
3563 // emit the switch opcode
3564 ec.Emit (OpCodes.Switch, switch_labels);
3567 // mark the default for this block
3568 if (range_index != 0)
3569 ec.MarkLabel (lbl_default);
3572 // the last default just goes to the end
3573 if (ranges.Count > 0)
3574 ec.Emit (OpCodes.Br, lbl_default);
3577 // now emit the code for the sections
3578 bool found_default = false;
3580 foreach (SwitchSection ss in Sections) {
3581 foreach (SwitchLabel sl in ss.Labels) {
3583 ec.MarkLabel (lbl_default);
3584 found_default = true;
3585 if (null_section == null)
3586 ec.MarkLabel (null_target);
3587 } else if (sl.Converted.IsNull) {
3588 ec.MarkLabel (null_target);
3591 ec.MarkLabel (sl.GetILLabel (ec));
3597 if (!found_default) {
3598 ec.MarkLabel (lbl_default);
3599 if (null_section == null) {
3600 ec.MarkLabel (null_target);
3605 SwitchLabel FindLabel (Constant value)
3607 SwitchLabel sl = null;
3609 if (string_labels != null) {
3610 string s = value.GetValue () as string;
3612 if (null_section != null)
3614 else if (default_section != null)
3615 sl = default_section.Labels[0];
3617 string_labels.TryGetValue (s, out sl);
3620 if (value is NullLiteral) {
3623 labels.TryGetValue (value.GetValueAsLong (), out sl);
3630 SwitchSection FindSection (SwitchLabel label)
3632 foreach (SwitchSection ss in Sections){
3633 foreach (SwitchLabel sl in ss.Labels){
3642 public static void Reset ()
3647 public override bool Resolve (BlockContext ec)
3649 Expr = Expr.Resolve (ec);
3653 new_expr = SwitchGoverningType (ec, Expr);
3655 if (new_expr == null && Expr.Type.IsNullableType) {
3656 unwrap = Nullable.Unwrap.Create (Expr, false);
3660 new_expr = SwitchGoverningType (ec, unwrap);
3663 if (new_expr == null){
3664 ec.Report.Error (151, loc,
3665 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3666 TypeManager.CSharpName (Expr.Type));
3671 SwitchType = new_expr.Type;
3673 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
3674 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
3678 if (!CheckSwitch (ec))
3681 Switch old_switch = ec.Switch;
3683 ec.Switch.SwitchType = SwitchType;
3685 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3687 var constant = new_expr as Constant;
3688 if (constant != null) {
3690 SwitchLabel label = FindLabel (constant);
3692 constant_section = FindSection (label);
3694 if (constant_section == null)
3695 constant_section = default_section;
3700 foreach (SwitchSection ss in Sections){
3702 ec.CurrentBranching.CreateSibling (
3703 null, FlowBranching.SiblingType.SwitchSection);
3707 if (is_constant && (ss != constant_section)) {
3708 // If we're a constant switch, we're only emitting
3709 // one single section - mark all the others as
3711 ec.CurrentBranching.CurrentUsageVector.Goto ();
3712 if (!ss.Block.ResolveUnreachable (ec, true)) {
3716 if (!ss.Block.Resolve (ec))
3721 if (default_section == null)
3722 ec.CurrentBranching.CreateSibling (
3723 null, FlowBranching.SiblingType.SwitchSection);
3725 ec.EndFlowBranching ();
3726 ec.Switch = old_switch;
3731 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && !is_constant) {
3732 // TODO: Optimize single case, and single+default case
3733 ResolveStringSwitchMap (ec);
3739 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
3741 var sl = FindLabel (value);
3744 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
3750 void ResolveStringSwitchMap (ResolveContext ec)
3752 FullNamedExpression string_dictionary_type;
3753 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
3754 string_dictionary_type = new TypeExpression (
3755 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
3756 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
3758 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
3759 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
3761 ec.Module.PredefinedTypes.Dictionary.Resolve ();
3765 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3766 Field field = new Field (ctype, string_dictionary_type,
3767 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3768 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3769 if (!field.Define ())
3771 ctype.AddField (field);
3773 var init = new List<Expression> ();
3775 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
3776 string value = null;
3777 foreach (SwitchSection section in Sections) {
3778 bool contains_label = false;
3779 foreach (SwitchLabel sl in section.Labels) {
3780 if (sl.IsDefault || sl.Converted.IsNull)
3783 if (!contains_label) {
3784 labels.Add (counter, sl);
3785 contains_label = true;
3788 value = (string) sl.Converted.GetValue ();
3789 var init_args = new List<Expression> (2);
3790 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
3792 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
3793 init_args.Add (sl.Converted);
3795 init.Add (new CollectionElementInitializer (init_args, loc));
3799 // Don't add empty sections
3805 Arguments args = new Arguments (1);
3806 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
3807 Expression initializer = new NewInitialize (string_dictionary_type, args,
3808 new CollectionOrObjectInitializers (init, loc), loc);
3810 switch_cache_field = new FieldExpr (field, loc);
3811 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3814 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3816 Label l_initialized = ec.DefineLabel ();
3819 // Skip initialization when value is null
3821 value.EmitBranchable (ec, null_target, false);
3824 // Check if string dictionary is initialized and initialize
3826 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3827 string_dictionary.EmitStatement (ec);
3828 ec.MarkLabel (l_initialized);
3830 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
3832 ResolveContext rc = new ResolveContext (ec.MemberContext);
3834 if (switch_cache_field.Type.IsGeneric) {
3835 Arguments get_value_args = new Arguments (2);
3836 get_value_args.Add (new Argument (value));
3837 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3838 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3839 if (get_item == null)
3843 // A value was not found, go to default case
3845 get_item.EmitBranchable (ec, default_target, false);
3847 Arguments get_value_args = new Arguments (1);
3848 get_value_args.Add (new Argument (value));
3850 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3851 if (get_item == null)
3854 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
3855 get_item_object.EmitAssign (ec, get_item, true, false);
3856 ec.Emit (OpCodes.Brfalse, default_target);
3858 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3859 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
3861 get_item_int.EmitStatement (ec);
3862 get_item_object.Release (ec);
3865 EmitTableSwitch (ec, string_switch_variable);
3866 string_switch_variable.Release (ec);
3869 protected override void DoEmit (EmitContext ec)
3872 // Needed to emit anonymous storey initialization
3873 // Otherwise it does not contain any statements for now
3877 default_target = ec.DefineLabel ();
3878 null_target = ec.DefineLabel ();
3880 // Store variable for comparission purposes
3881 // TODO: Don't duplicate non-captured VariableReference
3882 LocalTemporary value;
3884 value = new LocalTemporary (SwitchType);
3885 unwrap.EmitCheck (ec);
3886 ec.Emit (OpCodes.Brfalse, null_target);
3889 } else if (!is_constant) {
3890 value = new LocalTemporary (SwitchType);
3897 // Setup the codegen context
3899 Label old_end = ec.LoopEnd;
3900 Switch old_switch = ec.Switch;
3902 ec.LoopEnd = ec.DefineLabel ();
3907 if (constant_section != null)
3908 constant_section.Block.Emit (ec);
3909 } else if (string_dictionary != null) {
3910 DoEmitStringSwitch (value, ec);
3912 EmitTableSwitch (ec, value);
3918 // Restore context state.
3919 ec.MarkLabel (ec.LoopEnd);
3922 // Restore the previous context
3924 ec.LoopEnd = old_end;
3925 ec.Switch = old_switch;
3928 protected override void CloneTo (CloneContext clonectx, Statement t)
3930 Switch target = (Switch) t;
3932 target.Expr = Expr.Clone (clonectx);
3933 target.Sections = new List<SwitchSection> ();
3934 foreach (SwitchSection ss in Sections){
3935 target.Sections.Add (ss.Clone (clonectx));
3940 // A place where execution can restart in an iterator
3941 public abstract class ResumableStatement : Statement
3944 protected Label resume_point;
3946 public Label PrepareForEmit (EmitContext ec)
3950 resume_point = ec.DefineLabel ();
3952 return resume_point;
3955 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3960 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
3965 public abstract class TryFinallyBlock : ExceptionStatement
3967 protected Statement stmt;
3968 Label dispose_try_block;
3969 bool prepared_for_dispose, emitted_dispose;
3971 protected TryFinallyBlock (Statement stmt, Location loc)
3979 public Statement Statement {
3987 protected abstract void EmitTryBody (EmitContext ec);
3988 protected abstract void EmitFinallyBody (EmitContext ec);
3990 public override Label PrepareForDispose (EmitContext ec, Label end)
3992 if (!prepared_for_dispose) {
3993 prepared_for_dispose = true;
3994 dispose_try_block = ec.DefineLabel ();
3996 return dispose_try_block;
3999 protected sealed override void DoEmit (EmitContext ec)
4001 EmitTryBodyPrepare (ec);
4004 ec.BeginFinallyBlock ();
4006 Label start_finally = ec.DefineLabel ();
4007 if (resume_points != null) {
4008 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4010 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4011 ec.Emit (OpCodes.Brfalse_S, start_finally);
4012 ec.Emit (OpCodes.Endfinally);
4015 ec.MarkLabel (start_finally);
4016 EmitFinallyBody (ec);
4018 ec.EndExceptionBlock ();
4021 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4023 if (emitted_dispose)
4026 emitted_dispose = true;
4028 Label end_of_try = ec.DefineLabel ();
4030 // Ensure that the only way we can get into this code is through a dispatcher
4031 if (have_dispatcher)
4032 ec.Emit (OpCodes.Br, end);
4034 ec.BeginExceptionBlock ();
4036 ec.MarkLabel (dispose_try_block);
4038 Label[] labels = null;
4039 for (int i = 0; i < resume_points.Count; ++i) {
4040 ResumableStatement s = resume_points[i];
4041 Label ret = s.PrepareForDispose (ec, end_of_try);
4042 if (ret.Equals (end_of_try) && labels == null)
4044 if (labels == null) {
4045 labels = new Label[resume_points.Count];
4046 for (int j = 0; j < i; ++j)
4047 labels[j] = end_of_try;
4052 if (labels != null) {
4054 for (j = 1; j < labels.Length; ++j)
4055 if (!labels[0].Equals (labels[j]))
4057 bool emit_dispatcher = j < labels.Length;
4059 if (emit_dispatcher) {
4060 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4061 ec.Emit (OpCodes.Ldloc, pc);
4062 ec.EmitInt (first_resume_pc);
4063 ec.Emit (OpCodes.Sub);
4064 ec.Emit (OpCodes.Switch, labels);
4065 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4068 foreach (ResumableStatement s in resume_points)
4069 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4072 ec.MarkLabel (end_of_try);
4074 ec.BeginFinallyBlock ();
4076 EmitFinallyBody (ec);
4078 ec.EndExceptionBlock ();
4083 // Base class for blocks using exception handling
4085 public abstract class ExceptionStatement : ResumableStatement
4090 protected List<ResumableStatement> resume_points;
4091 protected int first_resume_pc;
4093 protected ExceptionStatement (Location loc)
4098 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4100 StateMachineInitializer state_machine = null;
4101 if (resume_points != null) {
4102 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4104 ec.EmitInt ((int) IteratorStorey.State.Running);
4105 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4108 ec.BeginExceptionBlock ();
4110 if (resume_points != null) {
4111 ec.MarkLabel (resume_point);
4113 // For normal control flow, we want to fall-through the Switch
4114 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4115 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4116 ec.EmitInt (first_resume_pc);
4117 ec.Emit (OpCodes.Sub);
4119 Label[] labels = new Label[resume_points.Count];
4120 for (int i = 0; i < resume_points.Count; ++i)
4121 labels[i] = resume_points[i].PrepareForEmit (ec);
4122 ec.Emit (OpCodes.Switch, labels);
4126 public void SomeCodeFollows ()
4129 code_follows = true;
4133 public override bool Resolve (BlockContext ec)
4136 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4137 // So, ensure there's some IL code after this statement.
4138 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4139 ec.NeedReturnLabel ();
4144 public void AddResumePoint (ResumableStatement stmt, int pc)
4146 if (resume_points == null) {
4147 resume_points = new List<ResumableStatement> ();
4148 first_resume_pc = pc;
4151 if (pc != first_resume_pc + resume_points.Count)
4152 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4154 resume_points.Add (stmt);
4159 public class Lock : TryFinallyBlock
4162 TemporaryVariableReference expr_copy;
4163 TemporaryVariableReference lock_taken;
4165 public Lock (Expression expr, Statement stmt, Location loc)
4171 public override bool Resolve (BlockContext ec)
4173 expr = expr.Resolve (ec);
4177 if (!TypeSpec.IsReferenceType (expr.Type)) {
4178 ec.Report.Error (185, loc,
4179 "`{0}' is not a reference type as required by the lock statement",
4180 expr.Type.GetSignatureForError ());
4183 if (expr.Type.IsGenericParameter) {
4184 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4187 VariableReference lv = expr as VariableReference;
4190 locked = lv.IsLockedByStatement;
4191 lv.IsLockedByStatement = true;
4197 using (ec.Set (ResolveContext.Options.LockScope)) {
4198 ec.StartFlowBranching (this);
4199 Statement.Resolve (ec);
4200 ec.EndFlowBranching ();
4204 lv.IsLockedByStatement = locked;
4210 // Have to keep original lock value around to unlock same location
4211 // in the case the original has changed or is null
4213 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4214 expr_copy.Resolve (ec);
4217 // Ensure Monitor methods are available
4219 if (ResolvePredefinedMethods (ec) > 1) {
4220 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4221 lock_taken.Resolve (ec);
4227 protected override void EmitTryBodyPrepare (EmitContext ec)
4229 expr_copy.EmitAssign (ec, expr);
4231 if (lock_taken != null) {
4233 // Initialize ref variable
4235 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4238 // Monitor.Enter (expr_copy)
4240 expr_copy.Emit (ec);
4241 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4244 base.EmitTryBodyPrepare (ec);
4247 protected override void EmitTryBody (EmitContext ec)
4250 // Monitor.Enter (expr_copy, ref lock_taken)
4252 if (lock_taken != null) {
4253 expr_copy.Emit (ec);
4254 lock_taken.LocalInfo.CreateBuilder (ec);
4255 lock_taken.AddressOf (ec, AddressOp.Load);
4256 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4259 Statement.Emit (ec);
4262 protected override void EmitFinallyBody (EmitContext ec)
4265 // if (lock_taken) Monitor.Exit (expr_copy)
4267 Label skip = ec.DefineLabel ();
4269 if (lock_taken != null) {
4270 lock_taken.Emit (ec);
4271 ec.Emit (OpCodes.Brfalse_S, skip);
4274 expr_copy.Emit (ec);
4275 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4277 ec.Emit (OpCodes.Call, m);
4279 ec.MarkLabel (skip);
4282 int ResolvePredefinedMethods (ResolveContext rc)
4284 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4285 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4289 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4293 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4297 protected override void CloneTo (CloneContext clonectx, Statement t)
4299 Lock target = (Lock) t;
4301 target.expr = expr.Clone (clonectx);
4302 target.stmt = Statement.Clone (clonectx);
4306 public class Unchecked : Statement {
4309 public Unchecked (Block b, Location loc)
4316 public override bool Resolve (BlockContext ec)
4318 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4319 return Block.Resolve (ec);
4322 protected override void DoEmit (EmitContext ec)
4324 using (ec.With (EmitContext.Options.CheckedScope, false))
4328 protected override void CloneTo (CloneContext clonectx, Statement t)
4330 Unchecked target = (Unchecked) t;
4332 target.Block = clonectx.LookupBlock (Block);
4336 public class Checked : Statement {
4339 public Checked (Block b, Location loc)
4342 b.Unchecked = false;
4346 public override bool Resolve (BlockContext ec)
4348 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4349 return Block.Resolve (ec);
4352 protected override void DoEmit (EmitContext ec)
4354 using (ec.With (EmitContext.Options.CheckedScope, true))
4358 protected override void CloneTo (CloneContext clonectx, Statement t)
4360 Checked target = (Checked) t;
4362 target.Block = clonectx.LookupBlock (Block);
4366 public class Unsafe : Statement {
4369 public Unsafe (Block b, Location loc)
4372 Block.Unsafe = true;
4376 public override bool Resolve (BlockContext ec)
4378 if (ec.CurrentIterator != null)
4379 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4381 using (ec.Set (ResolveContext.Options.UnsafeScope))
4382 return Block.Resolve (ec);
4385 protected override void DoEmit (EmitContext ec)
4390 protected override void CloneTo (CloneContext clonectx, Statement t)
4392 Unsafe target = (Unsafe) t;
4394 target.Block = clonectx.LookupBlock (Block);
4401 public class Fixed : Statement
4403 abstract class Emitter : ShimExpression
4405 protected LocalVariable vi;
4407 protected Emitter (Expression expr, LocalVariable li)
4413 public abstract void EmitExit (EmitContext ec);
4416 class ExpressionEmitter : Emitter {
4417 public ExpressionEmitter (Expression converted, LocalVariable li) :
4418 base (converted, li)
4422 protected override Expression DoResolve (ResolveContext rc)
4424 throw new NotImplementedException ();
4427 public override void Emit (EmitContext ec) {
4429 // Store pointer in pinned location
4435 public override void EmitExit (EmitContext ec)
4438 ec.Emit (OpCodes.Conv_U);
4443 class StringEmitter : Emitter
4445 LocalVariable pinned_string;
4447 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4452 protected override Expression DoResolve (ResolveContext rc)
4454 pinned_string = new LocalVariable (vi.Block, "$pinned",
4455 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4457 pinned_string.Type = rc.BuiltinTypes.String;
4459 eclass = ExprClass.Variable;
4460 type = rc.BuiltinTypes.Int;
4464 public override void Emit (EmitContext ec)
4466 pinned_string.CreateBuilder (ec);
4469 pinned_string.EmitAssign (ec);
4471 // TODO: Should use Binary::Add
4472 pinned_string.Emit (ec);
4473 ec.Emit (OpCodes.Conv_I);
4475 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
4479 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
4480 //pe.InstanceExpression = pinned_string;
4481 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4483 ec.Emit (OpCodes.Add);
4487 public override void EmitExit (EmitContext ec)
4490 pinned_string.EmitAssign (ec);
4494 public class VariableDeclaration : BlockVariableDeclaration
4496 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4501 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4503 if (!Variable.Type.IsPointer && li == Variable) {
4504 bc.Report.Error (209, TypeExpression.Location,
4505 "The type of locals declared in a fixed statement must be a pointer type");
4510 // The rules for the possible declarators are pretty wise,
4511 // but the production on the grammar is more concise.
4513 // So we have to enforce these rules here.
4515 // We do not resolve before doing the case 1 test,
4516 // because the grammar is explicit in that the token &
4517 // is present, so we need to test for this particular case.
4520 if (initializer is Cast) {
4521 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4525 initializer = initializer.Resolve (bc);
4527 if (initializer == null)
4533 if (initializer.Type.IsArray) {
4534 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4537 // Provided that array_type is unmanaged,
4539 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
4543 // and T* is implicitly convertible to the
4544 // pointer type given in the fixed statement.
4546 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4548 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
4549 if (converted == null)
4553 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4555 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4556 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4557 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
4558 new NullLiteral (loc),
4561 converted = converted.Resolve (bc);
4563 return new ExpressionEmitter (converted, li);
4569 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
4570 return new StringEmitter (initializer, li, loc).Resolve (bc);
4573 // Case 3: fixed buffer
4574 if (initializer is FixedBufferPtr) {
4575 return new ExpressionEmitter (initializer, li);
4579 // Case 4: & object.
4581 bool already_fixed = true;
4582 Unary u = initializer as Unary;
4583 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4584 IVariableReference vr = u.Expr as IVariableReference;
4585 if (vr == null || !vr.IsFixed) {
4586 already_fixed = false;
4590 if (already_fixed) {
4591 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4594 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4595 return new ExpressionEmitter (initializer, li);
4600 VariableDeclaration decl;
4601 Statement statement;
4604 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4613 public Statement Statement {
4619 public BlockVariableDeclaration Variables {
4627 public override bool Resolve (BlockContext ec)
4629 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4630 if (!decl.Resolve (ec))
4634 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4635 bool ok = statement.Resolve (ec);
4636 bool flow_unreachable = ec.EndFlowBranching ();
4637 has_ret = flow_unreachable;
4642 protected override void DoEmit (EmitContext ec)
4644 decl.Variable.CreateBuilder (ec);
4645 decl.Initializer.Emit (ec);
4646 if (decl.Declarators != null) {
4647 foreach (var d in decl.Declarators) {
4648 d.Variable.CreateBuilder (ec);
4649 d.Initializer.Emit (ec);
4653 statement.Emit (ec);
4659 // Clear the pinned variable
4661 ((Emitter) decl.Initializer).EmitExit (ec);
4662 if (decl.Declarators != null) {
4663 foreach (var d in decl.Declarators) {
4664 ((Emitter)d.Initializer).EmitExit (ec);
4669 protected override void CloneTo (CloneContext clonectx, Statement t)
4671 Fixed target = (Fixed) t;
4673 target.decl = (VariableDeclaration) decl.Clone (clonectx);
4674 target.statement = statement.Clone (clonectx);
4678 public class Catch : Statement
4682 FullNamedExpression type_expr;
4683 CompilerAssign assign;
4686 public Catch (Block block, Location loc)
4694 public Block Block {
4700 public TypeSpec CatchType {
4706 public bool IsGeneral {
4708 return type_expr == null;
4712 public FullNamedExpression TypeExpression {
4721 public LocalVariable Variable {
4732 protected override void DoEmit (EmitContext ec)
4735 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
4737 ec.BeginCatchBlock (CatchType);
4740 li.CreateBuilder (ec);
4743 // Special case hoisted catch variable, we have to use a temporary variable
4744 // to pass via anonymous storey initialization with the value still on top
4747 if (li.HoistedVariant != null) {
4748 LocalTemporary lt = new LocalTemporary (li.Type);
4749 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4751 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4753 // switch to assigning from the temporary variable and not from top of the stack
4754 assign.UpdateSource (lt);
4757 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4758 ec.Emit (OpCodes.Pop);
4759 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4765 public override bool Resolve (BlockContext ec)
4767 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4768 if (type_expr != null) {
4769 type = type_expr.ResolveAsType (ec);
4773 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
4774 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4775 } else if (li != null) {
4777 li.PrepareForFlowAnalysis (ec);
4779 // source variable is at the top of the stack
4780 Expression source = new EmptyExpression (li.Type);
4781 if (li.Type.IsGenericParameter)
4782 source = new UnboxCast (source, li.Type);
4784 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
4785 Block.AddScopeStatement (new StatementExpression (assign));
4789 return Block.Resolve (ec);
4793 protected override void CloneTo (CloneContext clonectx, Statement t)
4795 Catch target = (Catch) t;
4797 if (type_expr != null)
4798 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
4800 target.block = clonectx.LookupBlock (block);
4804 public class TryFinally : TryFinallyBlock
4808 public TryFinally (Statement stmt, Block fini, Location loc)
4814 public override bool Resolve (BlockContext ec)
4818 ec.StartFlowBranching (this);
4820 if (!stmt.Resolve (ec))
4824 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4825 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4826 if (!fini.Resolve (ec))
4830 ec.EndFlowBranching ();
4832 ok &= base.Resolve (ec);
4837 protected override void EmitTryBody (EmitContext ec)
4842 protected override void EmitFinallyBody (EmitContext ec)
4847 protected override void CloneTo (CloneContext clonectx, Statement t)
4849 TryFinally target = (TryFinally) t;
4851 target.stmt = (Statement) stmt.Clone (clonectx);
4853 target.fini = clonectx.LookupBlock (fini);
4857 public class TryCatch : ExceptionStatement
4860 public List<Catch> Specific;
4861 public Catch General;
4862 readonly bool inside_try_finally;
4864 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4868 this.Specific = catch_clauses;
4869 this.inside_try_finally = inside_try_finally;
4871 Catch c = catch_clauses [0];
4874 catch_clauses.RemoveAt (0);
4878 public bool IsTryCatchFinally {
4880 return inside_try_finally;
4884 public override bool Resolve (BlockContext ec)
4888 ec.StartFlowBranching (this);
4890 if (!Block.Resolve (ec))
4893 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4895 foreach (Catch c in Specific){
4896 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4898 if (!c.Resolve (ec)) {
4903 TypeSpec resolved_type = c.CatchType;
4904 for (int ii = 0; ii < last_index; ++ii) {
4905 if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4906 ec.Report.Error (160, c.loc,
4907 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4908 TypeManager.CSharpName (prev_catches [ii]));
4913 prev_catches [last_index++] = resolved_type;
4916 if (General != null) {
4917 foreach (Catch c in Specific) {
4918 if (c.CatchType.BuiltinType != BuiltinTypeSpec.Type.Exception)
4921 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
4924 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
4927 ec.Report.Warning (1058, 1, c.loc,
4928 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4931 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4933 if (!General.Resolve (ec))
4937 ec.EndFlowBranching ();
4939 return base.Resolve (ec) && ok;
4942 protected sealed override void DoEmit (EmitContext ec)
4944 if (!inside_try_finally)
4945 EmitTryBodyPrepare (ec);
4949 foreach (Catch c in Specific)
4952 if (General != null)
4955 if (!inside_try_finally)
4956 ec.EndExceptionBlock ();
4959 protected override void CloneTo (CloneContext clonectx, Statement t)
4961 TryCatch target = (TryCatch) t;
4963 target.Block = clonectx.LookupBlock (Block);
4964 if (General != null)
4965 target.General = (Catch) General.Clone (clonectx);
4966 if (Specific != null){
4967 target.Specific = new List<Catch> ();
4968 foreach (Catch c in Specific)
4969 target.Specific.Add ((Catch) c.Clone (clonectx));
4974 public class Using : TryFinallyBlock
4976 public class VariableDeclaration : BlockVariableDeclaration
4978 Statement dispose_call;
4980 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4985 public VariableDeclaration (LocalVariable li, Location loc)
4991 public VariableDeclaration (Expression expr)
4994 loc = expr.Location;
5000 public bool IsNested { get; private set; }
5004 public void EmitDispose (EmitContext ec)
5006 dispose_call.Emit (ec);
5009 public override bool Resolve (BlockContext bc)
5014 return base.Resolve (bc, false);
5017 public Expression ResolveExpression (BlockContext bc)
5019 var e = Initializer.Resolve (bc);
5023 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5024 Initializer = ResolveInitializer (bc, Variable, e);
5028 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5030 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5031 initializer = initializer.Resolve (bc);
5032 if (initializer == null)
5035 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5036 Arguments args = new Arguments (1);
5037 args.Add (new Argument (initializer));
5038 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5039 if (initializer == null)
5042 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5043 dispose_call = CreateDisposeCall (bc, var);
5044 dispose_call.Resolve (bc);
5046 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5049 if (li == Variable) {
5050 CheckIDiposableConversion (bc, li, initializer);
5051 dispose_call = CreateDisposeCall (bc, li);
5052 dispose_call.Resolve (bc);
5055 return base.ResolveInitializer (bc, li, initializer);
5058 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5062 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5063 if (type.IsNullableType) {
5064 // it's handled in CreateDisposeCall
5068 bc.Report.SymbolRelatedToPreviousError (type);
5069 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5070 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5071 type.GetSignatureForError ());
5077 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5079 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5081 var loc = lv.Location;
5083 var idt = bc.BuiltinTypes.IDisposable;
5084 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5086 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5087 dispose_mg.InstanceExpression = type.IsNullableType ?
5088 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5091 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5093 // Add conditional call when disposing possible null variable
5094 if (!type.IsStruct || type.IsNullableType)
5095 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
5100 public void ResolveDeclaratorInitializer (BlockContext bc)
5102 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5105 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5107 for (int i = declarators.Count - 1; i >= 0; --i) {
5108 var d = declarators [i];
5109 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5110 vd.Initializer = d.Initializer;
5112 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5113 vd.dispose_call.Resolve (bc);
5115 stmt = new Using (vd, stmt, d.Variable.Location);
5123 VariableDeclaration decl;
5125 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5131 public Using (Expression expr, Statement stmt, Location loc)
5134 this.decl = new VariableDeclaration (expr);
5139 public Expression Expression {
5141 return decl.Variable == null ? decl.Initializer : null;
5145 public BlockVariableDeclaration Variables {
5153 protected override void EmitTryBodyPrepare (EmitContext ec)
5156 base.EmitTryBodyPrepare (ec);
5159 protected override void EmitTryBody (EmitContext ec)
5164 protected override void EmitFinallyBody (EmitContext ec)
5166 decl.EmitDispose (ec);
5169 public override bool Resolve (BlockContext ec)
5171 VariableReference vr;
5172 bool vr_locked = false;
5174 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5175 if (decl.Variable == null) {
5176 vr = decl.ResolveExpression (ec) as VariableReference;
5178 vr_locked = vr.IsLockedByStatement;
5179 vr.IsLockedByStatement = true;
5182 if (decl.IsNested) {
5183 decl.ResolveDeclaratorInitializer (ec);
5185 if (!decl.Resolve (ec))
5188 if (decl.Declarators != null) {
5189 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5197 ec.StartFlowBranching (this);
5201 ec.EndFlowBranching ();
5204 vr.IsLockedByStatement = vr_locked;
5211 protected override void CloneTo (CloneContext clonectx, Statement t)
5213 Using target = (Using) t;
5215 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5216 target.stmt = stmt.Clone (clonectx);
5221 /// Implementation of the foreach C# statement
5223 public class Foreach : Statement {
5225 sealed class ArrayForeach : Statement
5227 readonly Foreach for_each;
5228 readonly Statement statement;
5231 TemporaryVariableReference[] lengths;
5232 Expression [] length_exprs;
5233 StatementExpression[] counter;
5234 TemporaryVariableReference[] variables;
5236 TemporaryVariableReference copy;
5238 LocalVariableReference variable;
5240 public ArrayForeach (Foreach @foreach, int rank)
5242 for_each = @foreach;
5243 statement = for_each.statement;
5245 variable = new LocalVariableReference (for_each.variable, loc);
5247 counter = new StatementExpression[rank];
5248 variables = new TemporaryVariableReference[rank];
5249 length_exprs = new Expression [rank];
5252 // Only use temporary length variables when dealing with
5253 // multi-dimensional arrays
5256 lengths = new TemporaryVariableReference [rank];
5259 protected override void CloneTo (CloneContext clonectx, Statement target)
5261 throw new NotImplementedException ();
5264 public override bool Resolve (BlockContext ec)
5266 Block variables_block = variable.local_info.Block;
5267 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5270 int rank = length_exprs.Length;
5271 Arguments list = new Arguments (rank);
5272 for (int i = 0; i < rank; i++) {
5273 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5275 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5276 counter[i].Resolve (ec);
5279 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5281 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5282 lengths[i].Resolve (ec);
5284 Arguments args = new Arguments (1);
5285 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5286 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5289 list.Add (new Argument (v));
5292 access = new ElementAccess (copy, list, loc).Resolve (ec);
5297 if (for_each.type is VarExpr) {
5298 // Infer implicitly typed local variable from foreach array type
5299 var_type = access.Type;
5301 var_type = for_each.type.ResolveAsType (ec);
5304 if (var_type == null)
5307 conv = Convert.ExplicitConversion (ec, access, var_type, loc);
5313 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5314 ec.CurrentBranching.CreateSibling ();
5316 variable.local_info.Type = conv.Type;
5317 variable.Resolve (ec);
5319 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5320 if (!statement.Resolve (ec))
5322 ec.EndFlowBranching ();
5324 // There's no direct control flow from the end of the embedded statement to the end of the loop
5325 ec.CurrentBranching.CurrentUsageVector.Goto ();
5327 ec.EndFlowBranching ();
5332 protected override void DoEmit (EmitContext ec)
5334 copy.EmitAssign (ec, for_each.expr);
5336 int rank = length_exprs.Length;
5337 Label[] test = new Label [rank];
5338 Label[] loop = new Label [rank];
5340 for (int i = 0; i < rank; i++) {
5341 test [i] = ec.DefineLabel ();
5342 loop [i] = ec.DefineLabel ();
5344 if (lengths != null)
5345 lengths [i].EmitAssign (ec, length_exprs [i]);
5348 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5349 for (int i = 0; i < rank; i++) {
5350 variables [i].EmitAssign (ec, zero);
5352 ec.Emit (OpCodes.Br, test [i]);
5353 ec.MarkLabel (loop [i]);
5356 variable.local_info.CreateBuilder (ec);
5357 variable.EmitAssign (ec, conv, false, false);
5359 statement.Emit (ec);
5361 ec.MarkLabel (ec.LoopBegin);
5363 for (int i = rank - 1; i >= 0; i--){
5364 counter [i].Emit (ec);
5366 ec.MarkLabel (test [i]);
5367 variables [i].Emit (ec);
5369 if (lengths != null)
5370 lengths [i].Emit (ec);
5372 length_exprs [i].Emit (ec);
5374 ec.Emit (OpCodes.Blt, loop [i]);
5377 ec.MarkLabel (ec.LoopEnd);
5381 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5383 class Body : Statement
5386 LocalVariableReference variable;
5387 Expression current, conv;
5388 Statement statement;
5390 public Body (TypeSpec type, LocalVariable variable,
5391 Expression current, Statement statement,
5395 this.variable = new LocalVariableReference (variable, loc);
5396 this.current = current;
5397 this.statement = statement;
5401 protected override void CloneTo (CloneContext clonectx, Statement target)
5403 throw new NotImplementedException ();
5406 public override bool Resolve (BlockContext ec)
5408 current = current.Resolve (ec);
5409 if (current == null)
5412 conv = Convert.ExplicitConversion (ec, current, type, loc);
5416 variable.local_info.Type = conv.Type;
5417 variable.Resolve (ec);
5419 if (!statement.Resolve (ec))
5425 protected override void DoEmit (EmitContext ec)
5427 variable.local_info.CreateBuilder (ec);
5428 variable.EmitAssign (ec, conv, false, false);
5430 statement.Emit (ec);
5434 class RuntimeDispose : Using.VariableDeclaration
5436 public RuntimeDispose (LocalVariable lv, Location loc)
5441 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5443 // Defered to runtime check
5446 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5448 var idt = bc.BuiltinTypes.IDisposable;
5451 // Fabricates code like
5453 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5456 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
5458 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5459 dispose_variable.CreateReferenceExpression (bc, loc),
5460 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5461 loc), new NullLiteral (loc), loc);
5463 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5465 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5466 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5468 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5469 return new If (idisaposable_test, dispose, loc);
5473 LocalVariable variable;
5475 Statement statement;
5476 Expression var_type;
5477 ExpressionStatement init;
5478 TemporaryVariableReference enumerator_variable;
5479 bool ambiguous_getenumerator_name;
5481 public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5483 this.var_type = var_type;
5484 this.variable = var;
5490 protected override void CloneTo (CloneContext clonectx, Statement target)
5492 throw new NotImplementedException ();
5495 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5497 rc.Report.SymbolRelatedToPreviousError (enumerator);
5498 rc.Report.Error (202, loc,
5499 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5500 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5503 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5506 // Option 1: Try to match by name GetEnumerator first
5508 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
5509 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5511 var mg = mexpr as MethodGroupExpr;
5513 mg.InstanceExpression = expr;
5514 Arguments args = new Arguments (0);
5515 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5517 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5518 if (ambiguous_getenumerator_name)
5521 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5527 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5530 PredefinedMember<MethodSpec> iface_candidate = null;
5531 var ptypes = rc.Module.PredefinedTypes;
5532 var gen_ienumerable = ptypes.IEnumerableGeneric;
5533 if (!gen_ienumerable.Define ())
5534 gen_ienumerable = null;
5537 var ifaces = t.Interfaces;
5538 if (ifaces != null) {
5539 foreach (var iface in ifaces) {
5540 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
5541 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
5542 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5543 rc.Report.Error (1640, loc,
5544 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5545 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
5550 // TODO: Cache this somehow
5551 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
5552 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
5557 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
5558 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
5563 if (t.IsGenericParameter)
5568 } while (t != null);
5570 if (iface_candidate == null) {
5571 rc.Report.Error (1579, loc,
5572 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5573 expr.Type.GetSignatureForError (), "GetEnumerator");
5578 var method = iface_candidate.Resolve (loc);
5582 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5583 mg.InstanceExpression = expr;
5587 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5589 var ms = MemberCache.FindMember (enumerator.ReturnType,
5590 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
5591 BindingRestriction.InstanceOnly) as MethodSpec;
5593 if (ms == null || !ms.IsPublic) {
5594 Error_WrongEnumerator (rc, enumerator);
5598 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5601 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5603 var ps = MemberCache.FindMember (enumerator.ReturnType,
5604 MemberFilter.Property ("Current", null),
5605 BindingRestriction.InstanceOnly) as PropertySpec;
5607 if (ps == null || !ps.IsPublic) {
5608 Error_WrongEnumerator (rc, enumerator);
5615 public override bool Resolve (BlockContext ec)
5617 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
5620 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
5621 } else if (expr.Type.IsNullableType) {
5622 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
5625 var get_enumerator_mg = ResolveGetEnumerator (ec);
5626 if (get_enumerator_mg == null) {
5630 var get_enumerator = get_enumerator_mg.BestCandidate;
5631 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
5632 enumerator_variable.Resolve (ec);
5634 // Prepare bool MoveNext ()
5635 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5636 if (move_next_mg == null) {
5640 move_next_mg.InstanceExpression = enumerator_variable;
5642 // Prepare ~T~ Current { get; }
5643 var current_prop = ResolveCurrent (ec, get_enumerator);
5644 if (current_prop == null) {
5648 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
5649 if (current_pe == null)
5652 VarExpr ve = var_type as VarExpr;
5656 // Source type is dynamic, set element type to dynamic too
5657 variable.Type = ec.BuiltinTypes.Dynamic;
5659 // Infer implicitly typed local variable from foreach enumerable type
5660 variable.Type = current_pe.Type;
5664 // Explicit cast of dynamic collection elements has to be done at runtime
5665 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
5668 variable.Type = var_type.ResolveAsType (ec);
5671 if (variable.Type == null)
5674 var init = new Invocation (get_enumerator_mg, null);
5676 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5677 new Body (variable.Type, variable, current_pe, statement, loc), loc);
5679 var enum_type = enumerator_variable.Type;
5682 // Add Dispose method call when enumerator can be IDisposable
5684 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
5685 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
5687 // Runtime Dispose check
5689 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
5690 vd.Initializer = init;
5691 statement = new Using (vd, statement, loc);
5694 // No Dispose call needed
5696 this.init = new SimpleAssign (enumerator_variable, init);
5697 this.init.Resolve (ec);
5701 // Static Dispose check
5703 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
5704 vd.Initializer = init;
5705 statement = new Using (vd, statement, loc);
5708 return statement.Resolve (ec);
5711 protected override void DoEmit (EmitContext ec)
5713 enumerator_variable.LocalInfo.CreateBuilder (ec);
5716 init.EmitStatement (ec);
5718 statement.Emit (ec);
5721 #region IErrorHandler Members
5723 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5725 ec.Report.SymbolRelatedToPreviousError (best);
5726 ec.Report.Warning (278, 2, loc,
5727 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5728 expr.Type.GetSignatureForError (), "enumerable",
5729 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5731 ambiguous_getenumerator_name = true;
5735 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5740 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5745 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5754 LocalVariable variable;
5756 Statement statement;
5758 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
5761 this.variable = var;
5767 public Statement Statement {
5768 get { return statement; }
5771 public override bool Resolve (BlockContext ec)
5773 expr = expr.Resolve (ec);
5778 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5782 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5783 statement = new ArrayForeach (this, 1);
5784 } else if (expr.Type is ArrayContainer) {
5785 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5787 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5788 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5789 expr.ExprClassName);
5793 statement = new CollectionForeach (type, variable, expr, statement, loc);
5796 return statement.Resolve (ec);
5799 protected override void DoEmit (EmitContext ec)
5801 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5802 ec.LoopBegin = ec.DefineLabel ();
5803 ec.LoopEnd = ec.DefineLabel ();
5805 statement.Emit (ec);
5807 ec.LoopBegin = old_begin;
5808 ec.LoopEnd = old_end;
5811 protected override void CloneTo (CloneContext clonectx, Statement t)
5813 Foreach target = (Foreach) t;
5815 target.type = type.Clone (clonectx);
5816 target.expr = expr.Clone (clonectx);
5817 target.statement = statement.Clone (clonectx);