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.
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Diagnostics;
18 using System.Collections.Generic;
20 namespace Mono.CSharp {
22 public abstract class Statement {
26 /// Resolves the statement, true means that all sub-statements
29 public virtual bool Resolve (BlockContext ec)
35 /// We already know that the statement is unreachable, but we still
36 /// need to resolve it to catch errors.
38 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
41 // This conflicts with csc's way of doing this, but IMHO it's
42 // the right thing to do.
44 // If something is unreachable, we still check whether it's
45 // correct. This means that you cannot use unassigned variables
46 // in unreachable code, for instance.
50 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
52 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
53 bool ok = Resolve (ec);
54 ec.KillFlowBranching ();
60 /// Return value indicates whether all code paths emitted return.
62 protected abstract void DoEmit (EmitContext ec);
64 public virtual void Emit (EmitContext ec)
71 // This routine must be overrided in derived classes and make copies
72 // of all the data that might be modified if resolved
74 protected abstract void CloneTo (CloneContext clonectx, Statement target);
76 public Statement Clone (CloneContext clonectx)
78 Statement s = (Statement) this.MemberwiseClone ();
79 CloneTo (clonectx, s);
83 public virtual Expression CreateExpressionTree (ResolveContext ec)
85 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
89 public Statement PerformClone ()
91 CloneContext clonectx = new CloneContext ();
93 return Clone (clonectx);
96 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey);
99 public sealed class EmptyStatement : Statement
101 public EmptyStatement (Location loc)
106 public override bool Resolve (BlockContext ec)
111 public override bool ResolveUnreachable (BlockContext ec, bool warn)
116 public override void Emit (EmitContext ec)
120 protected override void DoEmit (EmitContext ec)
122 throw new NotSupportedException ();
125 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
129 protected override void CloneTo (CloneContext clonectx, Statement target)
135 public class If : Statement {
137 public Statement TrueStatement;
138 public Statement FalseStatement;
142 public If (Expression bool_expr, Statement true_statement, Location l)
143 : this (bool_expr, true_statement, null, l)
147 public If (Expression bool_expr,
148 Statement true_statement,
149 Statement false_statement,
152 this.expr = bool_expr;
153 TrueStatement = true_statement;
154 FalseStatement = false_statement;
158 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
160 expr.MutateHoistedGenericType (storey);
161 TrueStatement.MutateHoistedGenericType (storey);
162 if (FalseStatement != null)
163 FalseStatement.MutateHoistedGenericType (storey);
166 public override bool Resolve (BlockContext ec)
170 Report.Debug (1, "START IF BLOCK", loc);
172 expr = expr.Resolve (ec);
177 // Dead code elimination
179 if (expr is Constant) {
180 bool take = !((Constant) expr).IsDefaultValue;
183 if (!TrueStatement.Resolve (ec))
186 if ((FalseStatement != null) &&
187 !FalseStatement.ResolveUnreachable (ec, true))
189 FalseStatement = null;
191 if (!TrueStatement.ResolveUnreachable (ec, true))
193 TrueStatement = null;
195 if ((FalseStatement != null) &&
196 !FalseStatement.Resolve (ec))
204 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
206 ok &= TrueStatement.Resolve (ec);
208 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
210 ec.CurrentBranching.CreateSibling ();
212 if (FalseStatement != null)
213 ok &= FalseStatement.Resolve (ec);
215 ec.EndFlowBranching ();
217 Report.Debug (1, "END IF BLOCK", loc);
222 protected override void DoEmit (EmitContext ec)
224 ILGenerator ig = ec.ig;
225 Label false_target = ig.DefineLabel ();
229 // If we're a boolean constant, Resolve() already
230 // eliminated dead code for us.
232 Constant c = expr as Constant;
234 c.EmitSideEffect (ec);
236 if (!c.IsDefaultValue)
237 TrueStatement.Emit (ec);
238 else if (FalseStatement != null)
239 FalseStatement.Emit (ec);
244 expr.EmitBranchable (ec, false_target, false);
246 TrueStatement.Emit (ec);
248 if (FalseStatement != null){
249 bool branch_emitted = false;
251 end = ig.DefineLabel ();
253 ig.Emit (OpCodes.Br, end);
254 branch_emitted = true;
257 ig.MarkLabel (false_target);
258 FalseStatement.Emit (ec);
263 ig.MarkLabel (false_target);
267 protected override void CloneTo (CloneContext clonectx, Statement t)
271 target.expr = expr.Clone (clonectx);
272 target.TrueStatement = TrueStatement.Clone (clonectx);
273 if (FalseStatement != null)
274 target.FalseStatement = FalseStatement.Clone (clonectx);
278 public class Do : Statement {
279 public Expression expr;
280 public Statement EmbeddedStatement;
282 public Do (Statement statement, BooleanExpression bool_expr, Location l)
285 EmbeddedStatement = statement;
289 public override bool Resolve (BlockContext ec)
293 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
295 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
297 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
298 if (!EmbeddedStatement.Resolve (ec))
300 ec.EndFlowBranching ();
302 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
303 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
305 expr = expr.Resolve (ec);
308 else if (expr is Constant){
309 bool infinite = !((Constant) expr).IsDefaultValue;
311 ec.CurrentBranching.CurrentUsageVector.Goto ();
314 ec.EndFlowBranching ();
319 protected override void DoEmit (EmitContext ec)
321 ILGenerator ig = ec.ig;
322 Label loop = ig.DefineLabel ();
323 Label old_begin = ec.LoopBegin;
324 Label old_end = ec.LoopEnd;
326 ec.LoopBegin = ig.DefineLabel ();
327 ec.LoopEnd = ig.DefineLabel ();
330 EmbeddedStatement.Emit (ec);
331 ig.MarkLabel (ec.LoopBegin);
334 // Dead code elimination
336 if (expr is Constant){
337 bool res = !((Constant) expr).IsDefaultValue;
339 expr.EmitSideEffect (ec);
341 ec.ig.Emit (OpCodes.Br, loop);
343 expr.EmitBranchable (ec, loop, true);
345 ig.MarkLabel (ec.LoopEnd);
347 ec.LoopBegin = old_begin;
348 ec.LoopEnd = old_end;
351 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
353 expr.MutateHoistedGenericType (storey);
354 EmbeddedStatement.MutateHoistedGenericType (storey);
357 protected override void CloneTo (CloneContext clonectx, Statement t)
361 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
362 target.expr = expr.Clone (clonectx);
366 public class While : Statement {
367 public Expression expr;
368 public Statement Statement;
369 bool infinite, empty;
371 public While (BooleanExpression bool_expr, Statement statement, Location l)
373 this.expr = bool_expr;
374 Statement = statement;
378 public override bool Resolve (BlockContext ec)
382 expr = expr.Resolve (ec);
387 // Inform whether we are infinite or not
389 if (expr is Constant){
390 bool value = !((Constant) expr).IsDefaultValue;
393 if (!Statement.ResolveUnreachable (ec, true))
401 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
403 ec.CurrentBranching.CreateSibling ();
405 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
406 if (!Statement.Resolve (ec))
408 ec.EndFlowBranching ();
410 // There's no direct control flow from the end of the embedded statement to the end of the loop
411 ec.CurrentBranching.CurrentUsageVector.Goto ();
413 ec.EndFlowBranching ();
418 protected override void DoEmit (EmitContext ec)
421 expr.EmitSideEffect (ec);
425 ILGenerator ig = ec.ig;
426 Label old_begin = ec.LoopBegin;
427 Label old_end = ec.LoopEnd;
429 ec.LoopBegin = ig.DefineLabel ();
430 ec.LoopEnd = ig.DefineLabel ();
433 // Inform whether we are infinite or not
435 if (expr is Constant){
436 // expr is 'true', since the 'empty' case above handles the 'false' case
437 ig.MarkLabel (ec.LoopBegin);
438 expr.EmitSideEffect (ec);
440 ig.Emit (OpCodes.Br, ec.LoopBegin);
443 // Inform that we are infinite (ie, `we return'), only
444 // if we do not `break' inside the code.
446 ig.MarkLabel (ec.LoopEnd);
448 Label while_loop = ig.DefineLabel ();
450 ig.Emit (OpCodes.Br, ec.LoopBegin);
451 ig.MarkLabel (while_loop);
455 ig.MarkLabel (ec.LoopBegin);
458 expr.EmitBranchable (ec, while_loop, true);
460 ig.MarkLabel (ec.LoopEnd);
463 ec.LoopBegin = old_begin;
464 ec.LoopEnd = old_end;
467 public override void Emit (EmitContext ec)
472 protected override void CloneTo (CloneContext clonectx, Statement t)
474 While target = (While) t;
476 target.expr = expr.Clone (clonectx);
477 target.Statement = Statement.Clone (clonectx);
480 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
482 expr.MutateHoistedGenericType (storey);
483 Statement.MutateHoistedGenericType (storey);
487 public class For : Statement {
489 Statement InitStatement;
491 public Statement Statement;
492 bool infinite, empty;
494 public For (Statement init_statement,
495 BooleanExpression test,
500 InitStatement = init_statement;
502 Increment = increment;
503 Statement = statement;
507 public override bool Resolve (BlockContext ec)
511 if (InitStatement != null){
512 if (!InitStatement.Resolve (ec))
517 Test = Test.Resolve (ec);
520 else if (Test is Constant){
521 bool value = !((Constant) Test).IsDefaultValue;
524 if (!Statement.ResolveUnreachable (ec, true))
526 if ((Increment != null) &&
527 !Increment.ResolveUnreachable (ec, false))
537 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
539 ec.CurrentBranching.CreateSibling ();
541 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
543 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
544 if (!Statement.Resolve (ec))
546 ec.EndFlowBranching ();
548 if (Increment != null){
549 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
550 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
553 if (!Increment.Resolve (ec))
558 // There's no direct control flow from the end of the embedded statement to the end of the loop
559 ec.CurrentBranching.CurrentUsageVector.Goto ();
561 ec.EndFlowBranching ();
566 protected override void DoEmit (EmitContext ec)
568 if (InitStatement != null)
569 InitStatement.Emit (ec);
572 Test.EmitSideEffect (ec);
576 ILGenerator ig = ec.ig;
577 Label old_begin = ec.LoopBegin;
578 Label old_end = ec.LoopEnd;
579 Label loop = ig.DefineLabel ();
580 Label test = ig.DefineLabel ();
582 ec.LoopBegin = ig.DefineLabel ();
583 ec.LoopEnd = ig.DefineLabel ();
585 ig.Emit (OpCodes.Br, test);
589 ig.MarkLabel (ec.LoopBegin);
594 // If test is null, there is no test, and we are just
599 // The Resolve code already catches the case for
600 // Test == Constant (false) so we know that
603 if (Test is Constant) {
604 Test.EmitSideEffect (ec);
605 ig.Emit (OpCodes.Br, loop);
607 Test.EmitBranchable (ec, loop, true);
611 ig.Emit (OpCodes.Br, loop);
612 ig.MarkLabel (ec.LoopEnd);
614 ec.LoopBegin = old_begin;
615 ec.LoopEnd = old_end;
618 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
620 if (InitStatement != null)
621 InitStatement.MutateHoistedGenericType (storey);
623 Test.MutateHoistedGenericType (storey);
624 if (Increment != null)
625 Increment.MutateHoistedGenericType (storey);
627 Statement.MutateHoistedGenericType (storey);
630 protected override void CloneTo (CloneContext clonectx, Statement t)
632 For target = (For) t;
634 if (InitStatement != null)
635 target.InitStatement = InitStatement.Clone (clonectx);
637 target.Test = Test.Clone (clonectx);
638 if (Increment != null)
639 target.Increment = Increment.Clone (clonectx);
640 target.Statement = Statement.Clone (clonectx);
644 public class StatementExpression : Statement {
645 ExpressionStatement expr;
647 public StatementExpression (ExpressionStatement expr)
653 public override bool Resolve (BlockContext ec)
655 expr = expr.ResolveStatement (ec);
659 protected override void DoEmit (EmitContext ec)
661 expr.EmitStatement (ec);
664 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
666 expr.MutateHoistedGenericType (storey);
669 public override string ToString ()
671 return "StatementExpression (" + expr + ")";
674 protected override void CloneTo (CloneContext clonectx, Statement t)
676 StatementExpression target = (StatementExpression) t;
678 target.expr = (ExpressionStatement) expr.Clone (clonectx);
682 // A 'return' or a 'yield break'
683 public abstract class ExitStatement : Statement
685 protected bool unwind_protect;
686 protected abstract bool DoResolve (BlockContext ec);
688 public virtual void Error_FinallyClause (Report Report)
690 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
693 public sealed override bool Resolve (BlockContext ec)
698 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
700 ec.NeedReturnLabel ();
701 ec.CurrentBranching.CurrentUsageVector.Goto ();
707 /// Implements the return statement
709 public class Return : ExitStatement {
710 protected Expression Expr;
711 public Return (Expression expr, Location l)
717 protected override bool DoResolve (BlockContext ec)
720 if (ec.ReturnType == TypeManager.void_type)
723 ec.Report.Error (126, loc,
724 "An object of a type convertible to `{0}' is required for the return statement",
725 TypeManager.CSharpName (ec.ReturnType));
729 if (ec.CurrentBlock.Toplevel.IsIterator) {
730 ec.Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
731 "statement to return a value, or yield break to end the iteration");
734 AnonymousExpression am = ec.CurrentAnonymousMethod;
735 if (am == null && ec.ReturnType == TypeManager.void_type) {
736 ec.Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
737 ec.GetSignatureForError ());
740 Expr = Expr.Resolve (ec);
744 if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
745 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
749 if (Expr.Type != ec.ReturnType) {
750 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
754 ec.Report.Error (1662, loc,
755 "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",
756 am.ContainerType, am.GetSignatureForError ());
765 protected override void DoEmit (EmitContext ec)
771 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
775 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
777 ec.ig.Emit (OpCodes.Ret);
780 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
783 Expr.MutateHoistedGenericType (storey);
786 protected override void CloneTo (CloneContext clonectx, Statement t)
788 Return target = (Return) t;
789 // It's null for simple return;
791 target.Expr = Expr.Clone (clonectx);
795 public class Goto : Statement {
797 LabeledStatement label;
800 public override bool Resolve (BlockContext ec)
802 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
803 ec.CurrentBranching.CurrentUsageVector.Goto ();
807 public Goto (string label, Location l)
813 public string Target {
814 get { return target; }
817 public void SetResolvedTarget (LabeledStatement label)
820 label.AddReference ();
823 protected override void CloneTo (CloneContext clonectx, Statement target)
828 protected override void DoEmit (EmitContext ec)
831 throw new InternalErrorException ("goto emitted before target resolved");
832 Label l = label.LabelTarget (ec);
833 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
836 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
841 public class LabeledStatement : Statement {
848 FlowBranching.UsageVector vectors;
850 public LabeledStatement (string name, Location l)
856 public Label LabelTarget (EmitContext ec)
861 label = ec.ig.DefineLabel ();
871 public bool IsDefined {
872 get { return defined; }
875 public bool HasBeenReferenced {
876 get { return referenced; }
879 public FlowBranching.UsageVector JumpOrigins {
880 get { return vectors; }
883 public void AddUsageVector (FlowBranching.UsageVector vector)
885 vector = vector.Clone ();
886 vector.Next = vectors;
890 protected override void CloneTo (CloneContext clonectx, Statement target)
895 public override bool Resolve (BlockContext ec)
897 // this flow-branching will be terminated when the surrounding block ends
898 ec.StartFlowBranching (this);
902 protected override void DoEmit (EmitContext ec)
904 if (ig != null && ig != ec.ig)
905 throw new InternalErrorException ("cannot happen");
907 ec.ig.MarkLabel (label);
910 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
914 public void AddReference ()
922 /// `goto default' statement
924 public class GotoDefault : Statement {
926 public GotoDefault (Location l)
931 protected override void CloneTo (CloneContext clonectx, Statement target)
936 public override bool Resolve (BlockContext ec)
938 ec.CurrentBranching.CurrentUsageVector.Goto ();
940 if (ec.Switch == null) {
941 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
945 if (!ec.Switch.GotDefault) {
946 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
953 protected override void DoEmit (EmitContext ec)
955 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
958 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
964 /// `goto case' statement
966 public class GotoCase : Statement {
970 public GotoCase (Expression e, Location l)
976 public override bool Resolve (BlockContext ec)
978 if (ec.Switch == null){
979 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
983 ec.CurrentBranching.CurrentUsageVector.Goto ();
985 expr = expr.Resolve (ec);
989 Constant c = expr as Constant;
991 ec.Report.Error (150, expr.Location, "A constant value is expected");
995 Type type = ec.Switch.SwitchType;
996 Constant res = c.TryReduce (ec, type, c.Location);
998 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1002 if (!Convert.ImplicitStandardConversionExists (c, type))
1003 ec.Report.Warning (469, 2, loc,
1004 "The `goto case' value is not implicitly convertible to type `{0}'",
1005 TypeManager.CSharpName (type));
1007 object val = res.GetValue ();
1009 val = SwitchLabel.NullStringCase;
1011 if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
1012 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1013 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1020 protected override void DoEmit (EmitContext ec)
1022 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1025 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1027 expr.MutateHoistedGenericType (storey);
1030 protected override void CloneTo (CloneContext clonectx, Statement t)
1032 GotoCase target = (GotoCase) t;
1034 target.expr = expr.Clone (clonectx);
1038 public class Throw : Statement {
1041 public Throw (Expression expr, Location l)
1047 public override bool Resolve (BlockContext ec)
1050 ec.CurrentBranching.CurrentUsageVector.Goto ();
1051 return ec.CurrentBranching.CheckRethrow (loc);
1054 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1055 ec.CurrentBranching.CurrentUsageVector.Goto ();
1060 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1061 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1063 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1068 protected override void DoEmit (EmitContext ec)
1071 ec.ig.Emit (OpCodes.Rethrow);
1075 ec.ig.Emit (OpCodes.Throw);
1079 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1082 expr.MutateHoistedGenericType (storey);
1085 protected override void CloneTo (CloneContext clonectx, Statement t)
1087 Throw target = (Throw) t;
1090 target.expr = expr.Clone (clonectx);
1094 public class Break : Statement {
1096 public Break (Location l)
1101 bool unwind_protect;
1103 public override bool Resolve (BlockContext ec)
1105 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1106 ec.CurrentBranching.CurrentUsageVector.Goto ();
1110 protected override void DoEmit (EmitContext ec)
1112 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1115 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1119 protected override void CloneTo (CloneContext clonectx, Statement t)
1125 public class Continue : Statement {
1127 public Continue (Location l)
1132 bool unwind_protect;
1134 public override bool Resolve (BlockContext ec)
1136 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1137 ec.CurrentBranching.CurrentUsageVector.Goto ();
1141 protected override void DoEmit (EmitContext ec)
1143 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1146 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1150 protected override void CloneTo (CloneContext clonectx, Statement t)
1156 public interface ILocalVariable
1158 void Emit (EmitContext ec);
1159 void EmitAssign (EmitContext ec);
1160 void EmitAddressOf (EmitContext ec);
1163 public interface IKnownVariable {
1164 Block Block { get; }
1165 Location Location { get; }
1169 // The information about a user-perceived local variable
1171 public class LocalInfo : IKnownVariable, ILocalVariable {
1172 public readonly FullNamedExpression Type;
1174 public Type VariableType;
1175 public readonly string Name;
1176 public readonly Location Location;
1177 public readonly Block Block;
1179 public VariableInfo VariableInfo;
1180 HoistedVariable hoisted_variant;
1189 CompilerGenerated = 64,
1193 public enum ReadOnlyContext: byte {
1200 ReadOnlyContext ro_context;
1201 LocalBuilder builder;
1203 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1211 public LocalInfo (DeclSpace ds, Block block, Location l)
1213 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1218 public void ResolveVariable (EmitContext ec)
1220 if (HoistedVariant != null)
1223 if (builder == null) {
1224 builder = ec.ig.DeclareLocal (TypeManager.TypeToReflectionType (VariableType), Pinned);
1228 public void Emit (EmitContext ec)
1230 ec.ig.Emit (OpCodes.Ldloc, builder);
1233 public void EmitAssign (EmitContext ec)
1235 ec.ig.Emit (OpCodes.Stloc, builder);
1238 public void EmitAddressOf (EmitContext ec)
1240 ec.ig.Emit (OpCodes.Ldloca, builder);
1243 public void EmitSymbolInfo (EmitContext ec)
1245 if (builder != null)
1246 ec.DefineLocalVariable (Name, builder);
1250 // Hoisted local variable variant
1252 public HoistedVariable HoistedVariant {
1254 return hoisted_variant;
1257 hoisted_variant = value;
1261 public bool IsThisAssigned (BlockContext ec, Block block)
1263 if (VariableInfo == null)
1264 throw new Exception ();
1266 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1269 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1272 public bool IsAssigned (BlockContext ec)
1274 if (VariableInfo == null)
1275 throw new Exception ();
1277 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1280 public bool Resolve (ResolveContext ec)
1282 if (VariableType != null)
1285 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1289 VariableType = texpr.Type;
1291 if (TypeManager.IsGenericParameter (VariableType))
1294 if (VariableType.IsAbstract && VariableType.IsSealed) {
1295 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
1299 if (VariableType.IsPointer && !ec.IsUnsafe)
1300 Expression.UnsafeError (ec, Location);
1305 public bool IsConstant {
1306 get { return (flags & Flags.IsConstant) != 0; }
1307 set { flags |= Flags.IsConstant; }
1310 public bool AddressTaken {
1311 get { return (flags & Flags.AddressTaken) != 0; }
1312 set { flags |= Flags.AddressTaken; }
1315 public bool CompilerGenerated {
1316 get { return (flags & Flags.CompilerGenerated) != 0; }
1317 set { flags |= Flags.CompilerGenerated; }
1320 public override string ToString ()
1322 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1323 Name, Type, VariableInfo, Location);
1327 get { return (flags & Flags.Used) != 0; }
1328 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1331 public bool ReadOnly {
1332 get { return (flags & Flags.ReadOnly) != 0; }
1335 public void SetReadOnlyContext (ReadOnlyContext context)
1337 flags |= Flags.ReadOnly;
1338 ro_context = context;
1341 public string GetReadOnlyContext ()
1344 throw new InternalErrorException ("Variable is not readonly");
1346 switch (ro_context) {
1347 case ReadOnlyContext.Fixed:
1348 return "fixed variable";
1349 case ReadOnlyContext.Foreach:
1350 return "foreach iteration variable";
1351 case ReadOnlyContext.Using:
1352 return "using variable";
1354 throw new NotImplementedException ();
1358 // Whether the variable is pinned, if Pinned the variable has been
1359 // allocated in a pinned slot with DeclareLocal.
1361 public bool Pinned {
1362 get { return (flags & Flags.Pinned) != 0; }
1363 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1366 public bool IsThis {
1367 get { return (flags & Flags.IsThis) != 0; }
1368 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1371 Block IKnownVariable.Block {
1372 get { return Block; }
1375 Location IKnownVariable.Location {
1376 get { return Location; }
1379 public LocalInfo Clone (CloneContext clonectx)
1382 // Variables in anonymous block are not resolved yet
1384 if (VariableType == null)
1385 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1388 // Variables in method block are resolved
1390 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1391 li.VariableType = VariableType;
1397 /// Block represents a C# block.
1401 /// This class is used in a number of places: either to represent
1402 /// explicit blocks that the programmer places or implicit blocks.
1404 /// Implicit blocks are used as labels or to introduce variable
1407 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1408 /// they contain extra information that is not necessary on normal blocks.
1410 public class Block : Statement {
1411 public Block Parent;
1412 public Location StartLocation;
1413 public Location EndLocation = Location.Null;
1415 public ExplicitBlock Explicit;
1416 public ToplevelBlock Toplevel; // TODO: Use Explicit
1423 VariablesInitialized = 4,
1427 HasCapturedVariable = 64,
1428 HasCapturedThis = 1 << 7,
1429 IsExpressionTree = 1 << 8
1432 protected Flags flags;
1434 public bool Unchecked {
1435 get { return (flags & Flags.Unchecked) != 0; }
1436 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1439 public bool Unsafe {
1440 get { return (flags & Flags.Unsafe) != 0; }
1441 set { flags |= Flags.Unsafe; }
1445 // The statements in this block
1447 protected List<Statement> statements;
1450 // An array of Blocks. We keep track of children just
1451 // to generate the local variable declarations.
1453 // Statements and child statements are handled through the
1456 List<Block> children;
1459 // Labels. (label, block) pairs.
1461 protected Dictionary<string, LabeledStatement> labels;
1464 // Keeps track of (name, type) pairs
1466 Dictionary<string, LocalInfo> variables;
1469 // Keeps track of constants
1470 Dictionary<string, Expression> constants;
1473 // Temporary variables.
1475 List<LocalInfo> temporary_variables;
1478 // If this is a switch section, the enclosing switch block.
1482 protected List<Statement> scope_initializers;
1484 List<ToplevelBlock> anonymous_children;
1486 protected static int id;
1490 int assignable_slots;
1491 bool unreachable_shown;
1494 public Block (Block parent)
1495 : this (parent, (Flags) 0, Location.Null, Location.Null)
1498 public Block (Block parent, Flags flags)
1499 : this (parent, flags, Location.Null, Location.Null)
1502 public Block (Block parent, Location start, Location end)
1503 : this (parent, (Flags) 0, start, end)
1507 // Useful when TopLevel block is downgraded to normal block
1509 public Block (ToplevelBlock parent, ToplevelBlock source)
1510 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1512 statements = source.statements;
1513 children = source.children;
1514 labels = source.labels;
1515 variables = source.variables;
1516 constants = source.constants;
1517 switch_block = source.switch_block;
1520 public Block (Block parent, Flags flags, Location start, Location end)
1522 if (parent != null) {
1523 parent.AddChild (this);
1525 // the appropriate constructors will fixup these fields
1526 Toplevel = parent.Toplevel;
1527 Explicit = parent.Explicit;
1530 this.Parent = parent;
1532 this.StartLocation = start;
1533 this.EndLocation = end;
1536 statements = new List<Statement> (4);
1539 public Block CreateSwitchBlock (Location start)
1541 // FIXME: should this be implicit?
1542 Block new_block = new ExplicitBlock (this, start, start);
1543 new_block.switch_block = this;
1548 get { return this_id; }
1551 public IDictionary<string, LocalInfo> Variables {
1553 if (variables == null)
1554 variables = new Dictionary<string, LocalInfo> ();
1559 void AddChild (Block b)
1561 if (children == null)
1562 children = new List<Block> (1);
1567 public void SetEndLocation (Location loc)
1572 protected void Error_158 (string name, Location loc)
1574 Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
1575 "by the same name in a contained scope", name);
1579 /// Adds a label to the current block.
1583 /// false if the name already exists in this block. true
1587 public bool AddLabel (LabeledStatement target)
1589 if (switch_block != null)
1590 return switch_block.AddLabel (target);
1592 string name = target.Name;
1595 while (cur != null) {
1596 LabeledStatement s = cur.DoLookupLabel (name);
1598 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1599 Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1603 if (this == Explicit)
1609 while (cur != null) {
1610 if (cur.DoLookupLabel (name) != null) {
1611 Error_158 (name, target.loc);
1615 if (children != null) {
1616 foreach (Block b in children) {
1617 LabeledStatement s = b.DoLookupLabel (name);
1621 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1622 Error_158 (name, target.loc);
1630 Toplevel.CheckError158 (name, target.loc);
1633 labels = new Dictionary<string, LabeledStatement> ();
1635 labels.Add (name, target);
1639 public LabeledStatement LookupLabel (string name)
1641 LabeledStatement s = DoLookupLabel (name);
1645 if (children == null)
1648 foreach (Block child in children) {
1649 if (Explicit != child.Explicit)
1652 s = child.LookupLabel (name);
1660 LabeledStatement DoLookupLabel (string name)
1662 if (switch_block != null)
1663 return switch_block.LookupLabel (name);
1666 if (labels.ContainsKey (name))
1667 return labels [name];
1672 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1675 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1676 while (kvi == null) {
1677 b = b.Explicit.Parent;
1680 kvi = b.Explicit.GetKnownVariable (name);
1686 // Is kvi.Block nested inside 'b'
1687 if (b.Explicit != kvi.Block.Explicit) {
1689 // If a variable by the same name it defined in a nested block of this
1690 // block, we violate the invariant meaning in a block.
1693 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1694 Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1699 // It's ok if the definition is in a nested subblock of b, but not
1700 // nested inside this block -- a definition in a sibling block
1701 // should not affect us.
1707 // Block 'b' and kvi.Block are the same textual block.
1708 // However, different variables are extant.
1710 // Check if the variable is in scope in both blocks. We use
1711 // an indirect check that depends on AddVariable doing its
1712 // part in maintaining the invariant-meaning-in-block property.
1714 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1717 if (this is ToplevelBlock) {
1718 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1719 e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
1724 // Even though we detected the error when the name is used, we
1725 // treat it as if the variable declaration was in error.
1727 Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
1728 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1732 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1734 LocalInfo vi = GetLocalInfo (name);
1736 block.Report.SymbolRelatedToPreviousError (vi.Location, name);
1737 if (Explicit == vi.Block.Explicit) {
1738 Error_AlreadyDeclared (l, name, null);
1740 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1741 "parent or current" : "parent");
1746 if (block != null) {
1747 Expression e = block.GetParameterReference (name, Location.Null);
1749 ParameterReference pr = e as ParameterReference;
1750 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1751 Error_AlreadyDeclared (loc, name);
1753 Error_AlreadyDeclared (loc, name, "parent or current");
1761 public LocalInfo AddVariable (Expression type, string name, Location l)
1763 if (!CheckParentConflictName (Toplevel, name, l))
1766 if (Toplevel.GenericMethod != null) {
1767 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1768 if (tp.Name == name) {
1769 Toplevel.Report.SymbolRelatedToPreviousError (tp);
1770 Error_AlreadyDeclaredTypeParameter (Toplevel.Report, loc, name, "local variable");
1776 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1778 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1779 Error_AlreadyDeclared (l, name, "child");
1783 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1786 if ((flags & Flags.VariablesInitialized) != 0)
1787 throw new InternalErrorException ("block has already been resolved");
1792 protected virtual void AddVariable (LocalInfo li)
1794 Variables.Add (li.Name, li);
1795 Explicit.AddKnownVariable (li.Name, li);
1798 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1800 if (reason == null) {
1801 Error_AlreadyDeclared (loc, var);
1805 Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1806 "in this scope because it would give a different meaning " +
1807 "to `{0}', which is already used in a `{1}' scope " +
1808 "to denote something else", var, reason);
1811 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1813 Toplevel.Report.Error (128, loc,
1814 "A local variable named `{0}' is already defined in this scope", name);
1817 public virtual void Error_AlreadyDeclaredTypeParameter (Report r, Location loc, string name, string conflict)
1819 r.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1823 public bool AddConstant (Expression type, string name, Expression value, Location l)
1825 if (AddVariable (type, name, l) == null)
1828 if (constants == null)
1829 constants = new Dictionary<string, Expression> ();
1831 constants.Add (name, value);
1833 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1838 static int next_temp_id = 0;
1840 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1842 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1844 if (temporary_variables == null)
1845 temporary_variables = new List<LocalInfo> ();
1847 int id = ++next_temp_id;
1848 string name = "$s_" + id.ToString ();
1850 LocalInfo li = new LocalInfo (te, name, this, loc);
1851 li.CompilerGenerated = true;
1852 temporary_variables.Add (li);
1856 public LocalInfo GetLocalInfo (string name)
1859 for (Block b = this; b != null; b = b.Parent) {
1860 if (b.variables != null) {
1861 if (b.variables.TryGetValue (name, out ret))
1869 public Expression GetVariableType (string name)
1871 LocalInfo vi = GetLocalInfo (name);
1872 return vi == null ? null : vi.Type;
1875 public Expression GetConstantExpression (string name)
1878 for (Block b = this; b != null; b = b.Parent) {
1879 if (b.constants != null) {
1880 if (b.constants.TryGetValue (name, out ret))
1888 // It should be used by expressions which require to
1889 // register a statement during resolve process.
1891 public void AddScopeStatement (Statement s)
1893 if (scope_initializers == null)
1894 scope_initializers = new List<Statement> ();
1896 scope_initializers.Add (s);
1899 public void AddStatement (Statement s)
1902 flags |= Flags.BlockUsed;
1906 get { return (flags & Flags.BlockUsed) != 0; }
1911 flags |= Flags.BlockUsed;
1914 public bool HasRet {
1915 get { return (flags & Flags.HasRet) != 0; }
1918 public int AssignableSlots {
1921 // if ((flags & Flags.VariablesInitialized) == 0)
1922 // throw new Exception ("Variables have not been initialized yet");
1923 return assignable_slots;
1927 public IList<ToplevelBlock> AnonymousChildren {
1928 get { return anonymous_children; }
1931 public void AddAnonymousChild (ToplevelBlock b)
1933 if (anonymous_children == null)
1934 anonymous_children = new List<ToplevelBlock> ();
1936 anonymous_children.Add (b);
1939 void DoResolveConstants (BlockContext ec)
1941 if (constants == null)
1944 if (variables == null)
1945 throw new InternalErrorException ("cannot happen");
1947 foreach (var de in variables) {
1948 string name = de.Key;
1949 LocalInfo vi = de.Value;
1950 Type variable_type = vi.VariableType;
1952 if (variable_type == null) {
1953 if (vi.Type is VarExpr)
1954 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1960 if (!constants.TryGetValue (name, out cv))
1963 // Don't let 'const int Foo = Foo;' succeed.
1964 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1965 // which in turn causes the 'must be constant' error to be triggered.
1966 constants.Remove (name);
1968 if (!Const.IsConstantTypeValid (variable_type)) {
1969 Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1973 ec.CurrentBlock = this;
1975 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1976 e = cv.Resolve (ec);
1981 Constant ce = e as Constant;
1983 e.Error_ExpressionMustBeConstant (ec, vi.Location, name);
1987 e = ce.ConvertImplicitly (ec, variable_type);
1989 if (TypeManager.IsReferenceType (variable_type))
1990 ce.Error_ConstantCanBeInitializedWithNullOnly (ec, variable_type, vi.Location, vi.Name);
1992 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1996 constants.Add (name, e);
1997 vi.IsConstant = true;
2001 protected void ResolveMeta (BlockContext ec, int offset)
2003 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2005 // If some parent block was unsafe, we remain unsafe even if this block
2006 // isn't explicitly marked as such.
2007 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
2008 flags |= Flags.VariablesInitialized;
2010 if (variables != null) {
2011 foreach (LocalInfo li in variables.Values) {
2012 if (!li.Resolve (ec))
2014 li.VariableInfo = new VariableInfo (li, offset);
2015 offset += li.VariableInfo.Length;
2018 assignable_slots = offset;
2020 DoResolveConstants (ec);
2022 if (children == null)
2024 foreach (Block b in children)
2025 b.ResolveMeta (ec, offset);
2030 // Emits the local variable declarations for a block
2032 public virtual void EmitMeta (EmitContext ec)
2034 if (variables != null){
2035 foreach (LocalInfo vi in variables.Values)
2036 vi.ResolveVariable (ec);
2039 if (temporary_variables != null) {
2040 for (int i = 0; i < temporary_variables.Count; i++)
2041 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2044 if (children != null) {
2045 for (int i = 0; i < children.Count; i++)
2046 ((Block)children[i]).EmitMeta(ec);
2050 void UsageWarning (BlockContext ec)
2052 if (variables == null || ec.Report.WarningLevel < 3)
2055 foreach (var de in variables) {
2056 LocalInfo vi = de.Value;
2059 string name = de.Key;
2061 // vi.VariableInfo can be null for 'catch' variables
2062 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2063 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2065 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2070 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
2074 // Some statements are wrapped by a Block. Since
2075 // others' internal could be changed, here I treat
2076 // them as possibly wrapped by Block equally.
2077 Block b = s as Block;
2078 if (b != null && b.statements.Count == 1)
2079 s = (Statement) b.statements [0];
2082 body = ((Lock) s).Statement;
2084 body = ((For) s).Statement;
2085 else if (s is Foreach)
2086 body = ((Foreach) s).Statement;
2087 else if (s is While)
2088 body = ((While) s).Statement;
2089 else if (s is Fixed)
2090 body = ((Fixed) s).Statement;
2091 else if (s is Using)
2092 body = ((Using) s).EmbeddedStatement;
2093 else if (s is UsingTemporary)
2094 body = ((UsingTemporary) s).Statement;
2098 if (body == null || body is EmptyStatement)
2099 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2102 public override bool Resolve (BlockContext ec)
2104 Block prev_block = ec.CurrentBlock;
2107 int errors = ec.Report.Errors;
2109 ec.CurrentBlock = this;
2110 ec.StartFlowBranching (this);
2112 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2115 // Compiler generated scope statements
2117 if (scope_initializers != null) {
2118 foreach (Statement s in scope_initializers)
2123 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2124 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2125 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2126 // responsible for handling the situation.
2128 int statement_count = statements.Count;
2129 for (int ix = 0; ix < statement_count; ix++){
2130 Statement s = statements [ix];
2131 // Check possible empty statement (CS0642)
2132 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2133 statements [ix + 1] is ExplicitBlock)
2134 CheckPossibleMistakenEmptyStatement (ec, s);
2137 // Warn if we detect unreachable code.
2140 if (s is EmptyStatement)
2143 if (!unreachable_shown && !(s is LabeledStatement)) {
2144 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2145 unreachable_shown = true;
2148 Block c_block = s as Block;
2149 if (c_block != null)
2150 c_block.unreachable = c_block.unreachable_shown = true;
2154 // Note that we're not using ResolveUnreachable() for unreachable
2155 // statements here. ResolveUnreachable() creates a temporary
2156 // flow branching and kills it afterwards. This leads to problems
2157 // if you have two unreachable statements where the first one
2158 // assigns a variable and the second one tries to access it.
2161 if (!s.Resolve (ec)) {
2163 if (ec.IsInProbingMode)
2166 statements [ix] = new EmptyStatement (s.loc);
2170 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2171 statements [ix] = new EmptyStatement (s.loc);
2173 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2174 if (unreachable && s is LabeledStatement)
2175 throw new InternalErrorException ("should not happen");
2178 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2179 ec.CurrentBranching, statement_count);
2181 while (ec.CurrentBranching is FlowBranchingLabeled)
2182 ec.EndFlowBranching ();
2184 bool flow_unreachable = ec.EndFlowBranching ();
2186 ec.CurrentBlock = prev_block;
2188 if (flow_unreachable)
2189 flags |= Flags.HasRet;
2191 // If we're a non-static `struct' constructor which doesn't have an
2192 // initializer, then we must initialize all of the struct's fields.
2193 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2196 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2197 foreach (LabeledStatement label in labels.Values)
2198 if (!label.HasBeenReferenced)
2199 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2202 if (ok && errors == ec.Report.Errors)
2208 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2210 unreachable_shown = true;
2214 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2216 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2217 bool ok = Resolve (ec);
2218 ec.KillFlowBranching ();
2223 protected override void DoEmit (EmitContext ec)
2225 for (int ix = 0; ix < statements.Count; ix++){
2226 Statement s = (Statement) statements [ix];
2231 public override void Emit (EmitContext ec)
2233 if (scope_initializers != null)
2234 EmitScopeInitializers (ec);
2236 ec.Mark (StartLocation);
2239 if (SymbolWriter.HasSymbolWriter)
2240 EmitSymbolInfo (ec);
2243 protected void EmitScopeInitializers (EmitContext ec)
2245 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2247 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2248 foreach (Statement s in scope_initializers)
2252 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2255 protected virtual void EmitSymbolInfo (EmitContext ec)
2257 if (variables != null) {
2258 foreach (LocalInfo vi in variables.Values) {
2259 vi.EmitSymbolInfo (ec);
2264 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2266 MutateVariables (storey);
2268 if (scope_initializers != null) {
2269 foreach (Statement s in scope_initializers)
2270 s.MutateHoistedGenericType (storey);
2273 foreach (Statement s in statements)
2274 s.MutateHoistedGenericType (storey);
2277 void MutateVariables (AnonymousMethodStorey storey)
2279 if (variables != null) {
2280 foreach (LocalInfo vi in variables.Values) {
2281 vi.VariableType = storey.MutateType (vi.VariableType);
2285 if (temporary_variables != null) {
2286 foreach (LocalInfo vi in temporary_variables)
2287 vi.VariableType = storey.MutateType (vi.VariableType);
2291 public override string ToString ()
2293 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2296 protected override void CloneTo (CloneContext clonectx, Statement t)
2298 Block target = (Block) t;
2300 clonectx.AddBlockMap (this, target);
2302 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2303 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2305 target.Parent = clonectx.RemapBlockCopy (Parent);
2307 if (variables != null){
2308 target.variables = new Dictionary<string, LocalInfo> ();
2310 foreach (var de in variables){
2311 LocalInfo newlocal = de.Value.Clone (clonectx);
2312 target.variables [de.Key] = newlocal;
2313 clonectx.AddVariableMap (de.Value, newlocal);
2317 target.statements = new List<Statement> (statements.Count);
2318 foreach (Statement s in statements)
2319 target.statements.Add (s.Clone (clonectx));
2321 if (target.children != null){
2322 target.children = new List<Block> (children.Count);
2323 foreach (Block b in children){
2324 target.children.Add (clonectx.LookupBlock (b));
2329 // TODO: labels, switch_block, constants (?), anonymous_children
2334 public class ExplicitBlock : Block
2336 Dictionary<string, IKnownVariable> known_variables;
2337 protected AnonymousMethodStorey am_storey;
2339 public ExplicitBlock (Block parent, Location start, Location end)
2340 : this (parent, (Flags) 0, start, end)
2344 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2345 : base (parent, flags, start, end)
2347 this.Explicit = this;
2351 // Marks a variable with name @name as being used in this or a child block.
2352 // If a variable name has been used in a child block, it's illegal to
2353 // declare a variable with the same name in the current block.
2355 internal void AddKnownVariable (string name, IKnownVariable info)
2357 if (known_variables == null)
2358 known_variables = new Dictionary<string, IKnownVariable> ();
2360 known_variables [name] = info;
2363 Parent.Explicit.AddKnownVariable (name, info);
2366 public AnonymousMethodStorey AnonymousMethodStorey {
2367 get { return am_storey; }
2371 // Creates anonymous method storey in current block
2373 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2376 // When referencing a variable in iterator storey from children anonymous method
2378 if (Toplevel.am_storey is IteratorStorey) {
2379 return Toplevel.am_storey;
2383 // An iterator has only 1 storey block
2385 if (ec.CurrentIterator != null)
2386 return ec.CurrentIterator.Storey;
2388 if (am_storey == null) {
2389 MemberBase mc = ec.MemberContext as MemberBase;
2390 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2393 // Creates anonymous method storey for this block
2395 am_storey = new AnonymousMethodStorey (this, ec.CurrentTypeDefinition, mc, gm, "AnonStorey");
2401 public override void Emit (EmitContext ec)
2403 if (am_storey != null)
2404 am_storey.EmitStoreyInstantiation (ec);
2406 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2407 if (emit_debug_info)
2412 if (emit_debug_info)
2416 public override void EmitMeta (EmitContext ec)
2419 // Creates anonymous method storey
2421 if (am_storey != null) {
2422 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2424 // Creates parent storey reference when hoisted this is accessible
2426 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2427 ExplicitBlock parent = Toplevel.Parent.Explicit;
2430 // Hoisted this exists in top-level parent storey only
2432 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2433 parent = parent.Parent.Explicit;
2435 am_storey.AddParentStoreyReference (parent.am_storey);
2438 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2441 am_storey.DefineType ();
2442 am_storey.ResolveType ();
2443 am_storey.Define ();
2444 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2446 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2447 if (ref_blocks != null) {
2448 foreach (ExplicitBlock ref_block in ref_blocks) {
2449 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2450 if (b.am_storey != null) {
2451 b.am_storey.AddParentStoreyReference (am_storey);
2453 // Stop propagation inside same top block
2454 if (b.Toplevel == Toplevel)
2459 b.HasCapturedVariable = true;
2468 internal IKnownVariable GetKnownVariable (string name)
2470 if (known_variables == null)
2474 if (!known_variables.TryGetValue (name, out kw))
2480 public bool HasCapturedThis
2482 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2483 get { return (flags & Flags.HasCapturedThis) != 0; }
2486 public bool HasCapturedVariable
2488 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2489 get { return (flags & Flags.HasCapturedVariable) != 0; }
2492 protected override void CloneTo (CloneContext clonectx, Statement t)
2494 ExplicitBlock target = (ExplicitBlock) t;
2495 target.known_variables = null;
2496 base.CloneTo (clonectx, t);
2500 public class ToplevelParameterInfo : IKnownVariable {
2501 public readonly ToplevelBlock Block;
2502 public readonly int Index;
2503 public VariableInfo VariableInfo;
2505 Block IKnownVariable.Block {
2506 get { return Block; }
2508 public Parameter Parameter {
2509 get { return Block.Parameters [Index]; }
2512 public Type ParameterType {
2513 get { return Block.Parameters.Types [Index]; }
2516 public Location Location {
2517 get { return Parameter.Location; }
2520 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2528 // A toplevel block contains extra information, the split is done
2529 // only to separate information that would otherwise bloat the more
2530 // lightweight Block.
2532 // In particular, this was introduced when the support for Anonymous
2533 // Methods was implemented.
2535 public class ToplevelBlock : ExplicitBlock
2538 // Block is converted to an expression
2540 sealed class BlockScopeExpression : Expression
2543 readonly ToplevelBlock block;
2545 public BlockScopeExpression (Expression child, ToplevelBlock block)
2551 public override Expression CreateExpressionTree (ResolveContext ec)
2553 throw new NotSupportedException ();
2556 protected override Expression DoResolve (ResolveContext ec)
2561 child = child.Resolve (ec);
2565 eclass = child.eclass;
2570 public override void Emit (EmitContext ec)
2572 block.EmitMeta (ec);
2573 block.EmitScopeInitializers (ec);
2577 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2579 type = storey.MutateType (type);
2580 child.MutateHoistedGenericType (storey);
2581 block.MutateHoistedGenericType (storey);
2585 GenericMethod generic;
2586 protected ParametersCompiled parameters;
2587 ToplevelParameterInfo[] parameter_info;
2588 LocalInfo this_variable;
2591 CompilerContext compiler;
2593 public HoistedVariable HoistedThisVariable;
2595 public bool Resolved {
2602 // The parameters for the block.
2604 public ParametersCompiled Parameters {
2605 get { return parameters; }
2608 public Report Report {
2609 get { return compiler.Report; }
2612 public GenericMethod GenericMethod {
2613 get { return generic; }
2616 public ToplevelBlock Container {
2617 get { return Parent == null ? null : Parent.Toplevel; }
2620 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2621 this (ctx, parent, (Flags) 0, parameters, start)
2625 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2626 this (ctx, parent, parameters, start)
2628 this.generic = generic;
2631 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2632 this (ctx, null, (Flags) 0, parameters, start)
2636 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2637 this (ctx, null, flags, parameters, start)
2641 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2642 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2643 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2644 base (null, flags, start, Location.Null)
2646 this.compiler = ctx;
2647 this.Toplevel = this;
2649 this.parameters = parameters;
2650 this.Parent = parent;
2652 parent.AddAnonymousChild (this);
2654 if (!this.parameters.IsEmpty)
2655 ProcessParameters ();
2658 public ToplevelBlock (CompilerContext ctx, Location loc)
2659 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2663 protected override void CloneTo (CloneContext clonectx, Statement t)
2665 ToplevelBlock target = (ToplevelBlock) t;
2666 base.CloneTo (clonectx, t);
2668 if (parameters.Count != 0)
2669 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2670 for (int i = 0; i < parameters.Count; ++i)
2671 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2674 public bool CheckError158 (string name, Location loc)
2676 if (AnonymousChildren != null) {
2677 foreach (ToplevelBlock child in AnonymousChildren) {
2678 if (!child.CheckError158 (name, loc))
2683 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2684 if (!c.DoCheckError158 (name, loc))
2691 void ProcessParameters ()
2693 int n = parameters.Count;
2694 parameter_info = new ToplevelParameterInfo [n];
2695 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2696 for (int i = 0; i < n; ++i) {
2697 parameter_info [i] = new ToplevelParameterInfo (this, i);
2699 Parameter p = parameters [i];
2703 string name = p.Name;
2704 if (CheckParentConflictName (top_parent, name, loc))
2705 AddKnownVariable (name, parameter_info [i]);
2708 // mark this block as "used" so that we create local declarations in a sub-block
2709 // FIXME: This appears to uncover a lot of bugs
2713 bool DoCheckError158 (string name, Location loc)
2715 LabeledStatement s = LookupLabel (name);
2717 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2718 Error_158 (name, loc);
2725 public override Expression CreateExpressionTree (ResolveContext ec)
2727 if (statements.Count == 1) {
2728 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2729 if (scope_initializers != null)
2730 expr = new BlockScopeExpression (expr, this);
2735 return base.CreateExpressionTree (ec);
2739 // Reformats this block to be top-level iterator block
2741 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2745 // Creates block with original statements
2746 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2748 source.statements = new List<Statement> (1);
2749 source.AddStatement (new Return (iterator, iterator.Location));
2750 source.IsIterator = false;
2752 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2753 source.am_storey = iterator_storey;
2754 return iterator_storey;
2758 // Returns a parameter reference expression for the given name,
2759 // or null if there is no such parameter
2761 public Expression GetParameterReference (string name, Location loc)
2763 for (ToplevelBlock t = this; t != null; t = t.Container) {
2764 Expression expr = t.GetParameterReferenceExpression (name, loc);
2772 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2774 int idx = parameters.GetParameterIndexByName (name);
2776 null : new ParameterReference (parameter_info [idx], loc);
2780 // Returns the "this" instance variable of this block.
2781 // See AddThisVariable() for more information.
2783 public LocalInfo ThisVariable {
2784 get { return this_variable; }
2788 // This is used by non-static `struct' constructors which do not have an
2789 // initializer - in this case, the constructor must initialize all of the
2790 // struct's fields. To do this, we add a "this" variable and use the flow
2791 // analysis code to ensure that it's been fully initialized before control
2792 // leaves the constructor.
2794 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2796 if (this_variable == null) {
2797 this_variable = new LocalInfo (ds, this, l);
2798 this_variable.Used = true;
2799 this_variable.IsThis = true;
2801 Variables.Add ("this", this_variable);
2804 return this_variable;
2807 public bool IsIterator {
2808 get { return (flags & Flags.IsIterator) != 0; }
2809 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2813 // Block has been converted to expression tree
2815 public bool IsExpressionTree {
2816 get { return (flags & Flags.IsExpressionTree) != 0; }
2819 public bool IsThisAssigned (BlockContext ec)
2821 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2824 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2831 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2832 flags |= Flags.IsExpressionTree;
2835 if (!ResolveMeta (rc, ip))
2838 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2839 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2844 unreachable = top_level.End ();
2846 } catch (Exception) {
2848 if (rc.CurrentBlock != null) {
2849 ec.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2851 ec.Report.Error (587, "Internal compiler error: Phase Resolve");
2857 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2858 if (rc.CurrentAnonymousMethod == null) {
2859 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2861 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2862 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2863 rc.CurrentAnonymousMethod.GetSignatureForError ());
2871 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2873 int errors = ec.Report.Errors;
2874 int orig_count = parameters.Count;
2879 // Assert: orig_count != parameter.Count => orig_count == 0
2880 if (orig_count != 0 && orig_count != parameters.Count)
2881 throw new InternalErrorException ("parameter information mismatch");
2883 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2885 for (int i = 0; i < orig_count; ++i) {
2886 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2888 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2891 VariableInfo vi = new VariableInfo (ip, i, offset);
2892 parameter_info [i].VariableInfo = vi;
2893 offset += vi.Length;
2896 ResolveMeta (ec, offset);
2898 return ec.Report.Errors == errors;
2902 // Check whether all `out' parameters have been assigned.
2904 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2906 if (vector.IsUnreachable)
2909 int n = parameter_info == null ? 0 : parameter_info.Length;
2911 for (int i = 0; i < n; i++) {
2912 VariableInfo var = parameter_info [i].VariableInfo;
2917 if (vector.IsAssigned (var, false))
2920 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2925 public override void Emit (EmitContext ec)
2927 if (Report.Errors > 0)
2935 if (ec.HasReturnLabel)
2936 ec.ReturnLabel = ec.ig.DefineLabel ();
2940 ec.Mark (EndLocation);
2942 if (ec.HasReturnLabel)
2943 ec.ig.MarkLabel (ec.ReturnLabel);
2945 if (ec.return_value != null) {
2946 ec.ig.Emit (OpCodes.Ldloc, ec.return_value);
2947 ec.ig.Emit (OpCodes.Ret);
2950 // If `HasReturnLabel' is set, then we already emitted a
2951 // jump to the end of the method, so we must emit a `ret'
2954 // Unfortunately, System.Reflection.Emit automatically emits
2955 // a leave to the end of a finally block. This is a problem
2956 // if no code is following the try/finally block since we may
2957 // jump to a point after the end of the method.
2958 // As a workaround, we're always creating a return label in
2962 if (ec.HasReturnLabel || !unreachable) {
2963 if (ec.ReturnType != TypeManager.void_type)
2964 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2965 ec.ig.Emit (OpCodes.Ret);
2970 } catch (Exception e){
2971 Console.WriteLine ("Exception caught by the compiler while emitting:");
2972 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2974 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2980 public override void EmitMeta (EmitContext ec)
2982 parameters.ResolveVariable ();
2984 // Avoid declaring an IL variable for this_variable since it is not accessed
2985 // from the generated IL
2986 if (this_variable != null)
2987 Variables.Remove ("this");
2991 protected override void EmitSymbolInfo (EmitContext ec)
2993 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2994 if ((ae != null) && (ae.Storey != null))
2995 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2997 base.EmitSymbolInfo (ec);
3001 public class SwitchLabel {
3008 Label il_label_code;
3009 bool il_label_code_set;
3011 public static readonly object NullStringCase = new object ();
3014 // if expr == null, then it is the default case.
3016 public SwitchLabel (Expression expr, Location l)
3022 public Expression Label {
3028 public Location Location {
3032 public object Converted {
3038 public Label GetILLabel (EmitContext ec)
3041 il_label = ec.ig.DefineLabel ();
3042 il_label_set = true;
3047 public Label GetILLabelCode (EmitContext ec)
3049 if (!il_label_code_set){
3050 il_label_code = ec.ig.DefineLabel ();
3051 il_label_code_set = true;
3053 return il_label_code;
3057 // Resolves the expression, reduces it to a literal if possible
3058 // and then converts it to the requested type.
3060 public bool ResolveAndReduce (ResolveContext ec, Type required_type, bool allow_nullable)
3062 Expression e = label.Resolve (ec);
3067 Constant c = e as Constant;
3069 ec.Report.Error (150, loc, "A constant value is expected");
3073 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3074 converted = NullStringCase;
3078 if (allow_nullable && c.GetValue () == null) {
3079 converted = NullStringCase;
3083 c = c.ImplicitConversionRequired (ec, required_type, loc);
3087 converted = c.GetValue ();
3091 public void Error_AlreadyOccurs (ResolveContext ec, Type switch_type, SwitchLabel collision_with)
3094 if (converted == null)
3096 else if (converted == NullStringCase)
3099 label = converted.ToString ();
3101 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3102 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3105 public SwitchLabel Clone (CloneContext clonectx)
3107 return new SwitchLabel (label.Clone (clonectx), loc);
3111 public class SwitchSection {
3112 // An array of SwitchLabels.
3113 public readonly List<SwitchLabel> Labels;
3114 public readonly Block Block;
3116 public SwitchSection (List<SwitchLabel> labels, Block block)
3122 public SwitchSection Clone (CloneContext clonectx)
3124 var cloned_labels = new List<SwitchLabel> ();
3126 foreach (SwitchLabel sl in cloned_labels)
3127 cloned_labels.Add (sl.Clone (clonectx));
3129 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3133 public class Switch : Statement {
3134 public List<SwitchSection> Sections;
3135 public Expression Expr;
3138 /// Maps constants whose type type SwitchType to their SwitchLabels.
3140 public IDictionary<object, SwitchLabel> Elements;
3143 /// The governing switch type
3145 public Type SwitchType;
3150 Label default_target;
3152 Expression new_expr;
3155 SwitchSection constant_section;
3156 SwitchSection default_section;
3158 ExpressionStatement string_dictionary;
3159 FieldExpr switch_cache_field;
3160 static int unique_counter;
3163 // Nullable Types support
3165 Nullable.Unwrap unwrap;
3167 protected bool HaveUnwrap {
3168 get { return unwrap != null; }
3172 // The types allowed to be implicitly cast from
3173 // on the governing type
3175 static Type [] allowed_types;
3177 public Switch (Expression e, List<SwitchSection> sects, Location l)
3184 public bool GotDefault {
3186 return default_section != null;
3190 public Label DefaultTarget {
3192 return default_target;
3197 // Determines the governing type for a switch. The returned
3198 // expression might be the expression from the switch, or an
3199 // expression that includes any potential conversions to the
3200 // integral types or to string.
3202 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3206 if (t == TypeManager.byte_type ||
3207 t == TypeManager.sbyte_type ||
3208 t == TypeManager.ushort_type ||
3209 t == TypeManager.short_type ||
3210 t == TypeManager.uint32_type ||
3211 t == TypeManager.int32_type ||
3212 t == TypeManager.uint64_type ||
3213 t == TypeManager.int64_type ||
3214 t == TypeManager.char_type ||
3215 t == TypeManager.string_type ||
3216 t == TypeManager.bool_type ||
3217 TypeManager.IsEnumType (t))
3220 if (allowed_types == null){
3221 allowed_types = new Type [] {
3222 TypeManager.sbyte_type,
3223 TypeManager.byte_type,
3224 TypeManager.short_type,
3225 TypeManager.ushort_type,
3226 TypeManager.int32_type,
3227 TypeManager.uint32_type,
3228 TypeManager.int64_type,
3229 TypeManager.uint64_type,
3230 TypeManager.char_type,
3231 TypeManager.string_type
3236 // Try to find a *user* defined implicit conversion.
3238 // If there is no implicit conversion, or if there are multiple
3239 // conversions, we have to report an error
3241 Expression converted = null;
3242 foreach (Type tt in allowed_types){
3245 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3250 // Ignore over-worked ImplicitUserConversions that do
3251 // an implicit conversion in addition to the user conversion.
3253 if (!(e is UserCast))
3256 if (converted != null){
3257 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3267 // Performs the basic sanity checks on the switch statement
3268 // (looks for duplicate keys and non-constant expressions).
3270 // It also returns a hashtable with the keys that we will later
3271 // use to compute the switch tables
3273 bool CheckSwitch (ResolveContext ec)
3276 Elements = new Dictionary<object, SwitchLabel> ();
3278 foreach (SwitchSection ss in Sections){
3279 foreach (SwitchLabel sl in ss.Labels){
3280 if (sl.Label == null){
3281 if (default_section != null){
3282 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3285 default_section = ss;
3289 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3294 object key = sl.Converted;
3295 if (key == SwitchLabel.NullStringCase)
3296 has_null_case = true;
3299 Elements.Add (key, sl);
3300 } catch (ArgumentException) {
3301 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3309 void EmitObjectInteger (ILGenerator ig, object k)
3312 IntConstant.EmitInt (ig, (int) k);
3313 else if (k is Constant) {
3314 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3317 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3320 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3322 IntConstant.EmitInt (ig, (int) (long) k);
3323 ig.Emit (OpCodes.Conv_I8);
3326 LongConstant.EmitLong (ig, (long) k);
3328 else if (k is ulong)
3330 ulong ul = (ulong) k;
3333 IntConstant.EmitInt (ig, unchecked ((int) ul));
3334 ig.Emit (OpCodes.Conv_U8);
3338 LongConstant.EmitLong (ig, unchecked ((long) ul));
3342 IntConstant.EmitInt (ig, (int) ((char) k));
3343 else if (k is sbyte)
3344 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3346 IntConstant.EmitInt (ig, (int) ((byte) k));
3347 else if (k is short)
3348 IntConstant.EmitInt (ig, (int) ((short) k));
3349 else if (k is ushort)
3350 IntConstant.EmitInt (ig, (int) ((ushort) k));
3352 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3354 throw new Exception ("Unhandled case");
3357 // structure used to hold blocks of keys while calculating table switch
3358 class KeyBlock : IComparable
3360 public KeyBlock (long _first)
3362 first = last = _first;
3366 public List<object> element_keys;
3367 // how many items are in the bucket
3368 public int Size = 1;
3371 get { return (int) (last - first + 1); }
3373 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3375 return kb_last.last - kb_first.first + 1;
3377 public int CompareTo (object obj)
3379 KeyBlock kb = (KeyBlock) obj;
3380 int nLength = Length;
3381 int nLengthOther = kb.Length;
3382 if (nLengthOther == nLength)
3383 return (int) (kb.first - first);
3384 return nLength - nLengthOther;
3389 /// This method emits code for a lookup-based switch statement (non-string)
3390 /// Basically it groups the cases into blocks that are at least half full,
3391 /// and then spits out individual lookup opcodes for each block.
3392 /// It emits the longest blocks first, and short blocks are just
3393 /// handled with direct compares.
3395 /// <param name="ec"></param>
3396 /// <param name="val"></param>
3397 /// <returns></returns>
3398 void TableSwitchEmit (EmitContext ec, Expression val)
3400 int element_count = Elements.Count;
3401 object [] element_keys = new object [element_count];
3402 Elements.Keys.CopyTo (element_keys, 0);
3403 Array.Sort (element_keys);
3405 // initialize the block list with one element per key
3406 var key_blocks = new List<KeyBlock> (element_count);
3407 foreach (object key in element_keys)
3408 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3410 KeyBlock current_kb;
3411 // iteratively merge the blocks while they are at least half full
3412 // there's probably a really cool way to do this with a tree...
3413 while (key_blocks.Count > 1)
3415 var key_blocks_new = new List<KeyBlock> ();
3416 current_kb = (KeyBlock) key_blocks [0];
3417 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3419 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3420 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3423 current_kb.last = kb.last;
3424 current_kb.Size += kb.Size;
3428 // start a new block
3429 key_blocks_new.Add (current_kb);
3433 key_blocks_new.Add (current_kb);
3434 if (key_blocks.Count == key_blocks_new.Count)
3436 key_blocks = key_blocks_new;
3439 // initialize the key lists
3440 foreach (KeyBlock kb in key_blocks)
3441 kb.element_keys = new List<object> ();
3443 // fill the key lists
3445 if (key_blocks.Count > 0) {
3446 current_kb = (KeyBlock) key_blocks [0];
3447 foreach (object key in element_keys)
3449 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3450 System.Convert.ToInt64 (key) > current_kb.last;
3452 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3453 current_kb.element_keys.Add (key);
3457 // sort the blocks so we can tackle the largest ones first
3460 // okay now we can start...
3461 ILGenerator ig = ec.ig;
3462 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3463 Label lbl_default = default_target;
3465 Type type_keys = null;
3466 if (element_keys.Length > 0)
3467 type_keys = element_keys [0].GetType (); // used for conversions
3471 if (TypeManager.IsEnumType (SwitchType))
3472 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3474 compare_type = SwitchType;
3476 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3478 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3479 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3482 foreach (object key in kb.element_keys) {
3483 SwitchLabel sl = (SwitchLabel) Elements [key];
3484 if (key is int && (int) key == 0) {
3485 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3488 EmitObjectInteger (ig, key);
3489 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3495 // TODO: if all the keys in the block are the same and there are
3496 // no gaps/defaults then just use a range-check.
3497 if (compare_type == TypeManager.int64_type ||
3498 compare_type == TypeManager.uint64_type)
3500 // TODO: optimize constant/I4 cases
3502 // check block range (could be > 2^31)
3504 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3505 ig.Emit (OpCodes.Blt, lbl_default);
3507 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3508 ig.Emit (OpCodes.Bgt, lbl_default);
3514 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3515 ig.Emit (OpCodes.Sub);
3517 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3523 int first = (int) kb.first;
3526 IntConstant.EmitInt (ig, first);
3527 ig.Emit (OpCodes.Sub);
3531 IntConstant.EmitInt (ig, -first);
3532 ig.Emit (OpCodes.Add);
3536 // first, build the list of labels for the switch
3538 int cJumps = kb.Length;
3539 Label [] switch_labels = new Label [cJumps];
3540 for (int iJump = 0; iJump < cJumps; iJump++)
3542 object key = kb.element_keys [iKey];
3543 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3545 SwitchLabel sl = (SwitchLabel) Elements [key];
3546 switch_labels [iJump] = sl.GetILLabel (ec);
3550 switch_labels [iJump] = lbl_default;
3552 // emit the switch opcode
3553 ig.Emit (OpCodes.Switch, switch_labels);
3556 // mark the default for this block
3558 ig.MarkLabel (lbl_default);
3561 // TODO: find the default case and emit it here,
3562 // to prevent having to do the following jump.
3563 // make sure to mark other labels in the default section
3565 // the last default just goes to the end
3566 if (element_keys.Length > 0)
3567 ig.Emit (OpCodes.Br, lbl_default);
3569 // now emit the code for the sections
3570 bool found_default = false;
3572 foreach (SwitchSection ss in Sections) {
3573 foreach (SwitchLabel sl in ss.Labels) {
3574 if (sl.Converted == SwitchLabel.NullStringCase) {
3575 ig.MarkLabel (null_target);
3576 } else if (sl.Label == null) {
3577 ig.MarkLabel (lbl_default);
3578 found_default = true;
3580 ig.MarkLabel (null_target);
3582 ig.MarkLabel (sl.GetILLabel (ec));
3583 ig.MarkLabel (sl.GetILLabelCode (ec));
3588 if (!found_default) {
3589 ig.MarkLabel (lbl_default);
3590 if (!has_null_case) {
3591 ig.MarkLabel (null_target);
3595 ig.MarkLabel (lbl_end);
3598 SwitchSection FindSection (SwitchLabel label)
3600 foreach (SwitchSection ss in Sections){
3601 foreach (SwitchLabel sl in ss.Labels){
3610 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3612 foreach (SwitchSection ss in Sections)
3613 ss.Block.MutateHoistedGenericType (storey);
3616 public static void Reset ()
3619 allowed_types = null;
3622 public override bool Resolve (BlockContext ec)
3624 Expr = Expr.Resolve (ec);
3628 new_expr = SwitchGoverningType (ec, Expr);
3630 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3631 unwrap = Nullable.Unwrap.Create (Expr, false);
3635 new_expr = SwitchGoverningType (ec, unwrap);
3638 if (new_expr == null){
3639 ec.Report.Error (151, loc,
3640 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3641 TypeManager.CSharpName (Expr.Type));
3646 SwitchType = new_expr.Type;
3648 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3649 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3653 if (!CheckSwitch (ec))
3657 Elements.Remove (SwitchLabel.NullStringCase);
3659 Switch old_switch = ec.Switch;
3661 ec.Switch.SwitchType = SwitchType;
3663 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3664 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3666 var constant = new_expr as Constant;
3667 if (constant != null) {
3669 object key = constant.GetValue ();
3671 if (Elements.TryGetValue (key, out label))
3672 constant_section = FindSection (label);
3674 if (constant_section == null)
3675 constant_section = default_section;
3680 foreach (SwitchSection ss in Sections){
3682 ec.CurrentBranching.CreateSibling (
3683 null, FlowBranching.SiblingType.SwitchSection);
3687 if (is_constant && (ss != constant_section)) {
3688 // If we're a constant switch, we're only emitting
3689 // one single section - mark all the others as
3691 ec.CurrentBranching.CurrentUsageVector.Goto ();
3692 if (!ss.Block.ResolveUnreachable (ec, true)) {
3696 if (!ss.Block.Resolve (ec))
3701 if (default_section == null)
3702 ec.CurrentBranching.CreateSibling (
3703 null, FlowBranching.SiblingType.SwitchSection);
3705 ec.EndFlowBranching ();
3706 ec.Switch = old_switch;
3708 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3713 if (SwitchType == TypeManager.string_type && !is_constant) {
3714 // TODO: Optimize single case, and single+default case
3715 ResolveStringSwitchMap (ec);
3721 void ResolveStringSwitchMap (ResolveContext ec)
3723 FullNamedExpression string_dictionary_type;
3724 if (TypeManager.generic_ienumerable_type != null) {
3725 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3726 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3728 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3730 new TypeExpression (TypeManager.string_type, loc),
3731 new TypeExpression (TypeManager.int32_type, loc)), loc);
3733 MemberAccess system_collections_generic = new MemberAccess (
3734 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3736 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3739 Field field = new Field (ec.CurrentTypeDefinition, string_dictionary_type,
3740 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3741 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3742 if (!field.Define ())
3744 ec.CurrentTypeDefinition.PartialContainer.AddField (field);
3746 var init = new List<Expression> ();
3749 string value = null;
3750 foreach (SwitchSection section in Sections) {
3751 int last_count = init.Count;
3752 foreach (SwitchLabel sl in section.Labels) {
3753 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3756 value = (string) sl.Converted;
3757 var init_args = new List<Expression> (2);
3758 init_args.Add (new StringLiteral (value, sl.Location));
3759 init_args.Add (new IntConstant (counter, loc));
3760 init.Add (new CollectionElementInitializer (init_args, loc));
3764 // Don't add empty sections
3766 if (last_count == init.Count)
3769 Elements.Add (counter, section.Labels [0]);
3773 Arguments args = new Arguments (1);
3774 args.Add (new Argument (new IntConstant (init.Count, loc)));
3775 Expression initializer = new NewInitialize (string_dictionary_type, args,
3776 new CollectionOrObjectInitializers (init, loc), loc);
3778 switch_cache_field = new FieldExpr (field, loc);
3779 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3782 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3784 ILGenerator ig = ec.ig;
3785 Label l_initialized = ig.DefineLabel ();
3788 // Skip initialization when value is null
3790 value.EmitBranchable (ec, null_target, false);
3793 // Check if string dictionary is initialized and initialize
3795 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3796 string_dictionary.EmitStatement (ec);
3797 ig.MarkLabel (l_initialized);
3799 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3801 ResolveContext rc = new ResolveContext (ec.MemberContext);
3803 if (TypeManager.generic_ienumerable_type != null) {
3804 Arguments get_value_args = new Arguments (2);
3805 get_value_args.Add (new Argument (value));
3806 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3807 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3808 if (get_item == null)
3812 // A value was not found, go to default case
3814 get_item.EmitBranchable (ec, default_target, false);
3816 Arguments get_value_args = new Arguments (1);
3817 get_value_args.Add (new Argument (value));
3819 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3820 if (get_item == null)
3823 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3824 get_item_object.EmitAssign (ec, get_item, true, false);
3825 ec.ig.Emit (OpCodes.Brfalse, default_target);
3827 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3828 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3830 get_item_int.EmitStatement (ec);
3831 get_item_object.Release (ec);
3834 TableSwitchEmit (ec, string_switch_variable);
3835 string_switch_variable.Release (ec);
3838 protected override void DoEmit (EmitContext ec)
3840 ILGenerator ig = ec.ig;
3842 default_target = ig.DefineLabel ();
3843 null_target = ig.DefineLabel ();
3845 // Store variable for comparission purposes
3846 // TODO: Don't duplicate non-captured VariableReference
3847 LocalTemporary value;
3849 value = new LocalTemporary (SwitchType);
3850 unwrap.EmitCheck (ec);
3851 ig.Emit (OpCodes.Brfalse, null_target);
3854 } else if (!is_constant) {
3855 value = new LocalTemporary (SwitchType);
3862 // Setup the codegen context
3864 Label old_end = ec.LoopEnd;
3865 Switch old_switch = ec.Switch;
3867 ec.LoopEnd = ig.DefineLabel ();
3872 if (constant_section != null)
3873 constant_section.Block.Emit (ec);
3874 } else if (string_dictionary != null) {
3875 DoEmitStringSwitch (value, ec);
3877 TableSwitchEmit (ec, value);
3883 // Restore context state.
3884 ig.MarkLabel (ec.LoopEnd);
3887 // Restore the previous context
3889 ec.LoopEnd = old_end;
3890 ec.Switch = old_switch;
3893 protected override void CloneTo (CloneContext clonectx, Statement t)
3895 Switch target = (Switch) t;
3897 target.Expr = Expr.Clone (clonectx);
3898 target.Sections = new List<SwitchSection> ();
3899 foreach (SwitchSection ss in Sections){
3900 target.Sections.Add (ss.Clone (clonectx));
3905 // A place where execution can restart in an iterator
3906 public abstract class ResumableStatement : Statement
3909 protected Label resume_point;
3911 public Label PrepareForEmit (EmitContext ec)
3915 resume_point = ec.ig.DefineLabel ();
3917 return resume_point;
3920 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3924 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3929 // Base class for statements that are implemented in terms of try...finally
3930 public abstract class ExceptionStatement : ResumableStatement
3934 List<ResumableStatement> resume_points;
3935 int first_resume_pc;
3937 protected abstract void EmitPreTryBody (EmitContext ec);
3938 protected abstract void EmitTryBody (EmitContext ec);
3939 protected abstract void EmitFinallyBody (EmitContext ec);
3941 protected sealed override void DoEmit (EmitContext ec)
3943 ILGenerator ig = ec.ig;
3945 EmitPreTryBody (ec);
3947 if (resume_points != null) {
3948 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3949 ig.Emit (OpCodes.Stloc, iter.CurrentPC);
3952 ig.BeginExceptionBlock ();
3954 if (resume_points != null) {
3955 ig.MarkLabel (resume_point);
3957 // For normal control flow, we want to fall-through the Switch
3958 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3959 ig.Emit (OpCodes.Ldloc, iter.CurrentPC);
3960 IntConstant.EmitInt (ig, first_resume_pc);
3961 ig.Emit (OpCodes.Sub);
3963 Label [] labels = new Label [resume_points.Count];
3964 for (int i = 0; i < resume_points.Count; ++i)
3965 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3966 ig.Emit (OpCodes.Switch, labels);
3971 ig.BeginFinallyBlock ();
3973 Label start_finally = ec.ig.DefineLabel ();
3974 if (resume_points != null) {
3975 ig.Emit (OpCodes.Ldloc, iter.SkipFinally);
3976 ig.Emit (OpCodes.Brfalse_S, start_finally);
3977 ig.Emit (OpCodes.Endfinally);
3980 ig.MarkLabel (start_finally);
3981 EmitFinallyBody (ec);
3983 ig.EndExceptionBlock ();
3986 public void SomeCodeFollows ()
3988 code_follows = true;
3991 public override bool Resolve (BlockContext ec)
3993 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3994 // So, ensure there's some IL code after this statement.
3995 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3996 ec.NeedReturnLabel ();
3998 iter = ec.CurrentIterator;
4002 public void AddResumePoint (ResumableStatement stmt, int pc)
4004 if (resume_points == null) {
4005 resume_points = new List<ResumableStatement> ();
4006 first_resume_pc = pc;
4009 if (pc != first_resume_pc + resume_points.Count)
4010 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4012 resume_points.Add (stmt);
4015 Label dispose_try_block;
4016 bool prepared_for_dispose, emitted_dispose;
4017 public override Label PrepareForDispose (EmitContext ec, Label end)
4019 if (!prepared_for_dispose) {
4020 prepared_for_dispose = true;
4021 dispose_try_block = ec.ig.DefineLabel ();
4023 return dispose_try_block;
4026 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4028 if (emitted_dispose)
4031 emitted_dispose = true;
4033 ILGenerator ig = ec.ig;
4035 Label end_of_try = ig.DefineLabel ();
4037 // Ensure that the only way we can get into this code is through a dispatcher
4038 if (have_dispatcher)
4039 ig.Emit (OpCodes.Br, end);
4041 ig.BeginExceptionBlock ();
4043 ig.MarkLabel (dispose_try_block);
4045 Label [] labels = null;
4046 for (int i = 0; i < resume_points.Count; ++i) {
4047 ResumableStatement s = (ResumableStatement) resume_points [i];
4048 Label ret = s.PrepareForDispose (ec, end_of_try);
4049 if (ret.Equals (end_of_try) && labels == null)
4051 if (labels == null) {
4052 labels = new Label [resume_points.Count];
4053 for (int j = 0; j < i; ++j)
4054 labels [j] = end_of_try;
4059 if (labels != null) {
4061 for (j = 1; j < labels.Length; ++j)
4062 if (!labels [0].Equals (labels [j]))
4064 bool emit_dispatcher = j < labels.Length;
4066 if (emit_dispatcher) {
4067 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4068 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4069 IntConstant.EmitInt (ig, first_resume_pc);
4070 ig.Emit (OpCodes.Sub);
4071 ig.Emit (OpCodes.Switch, labels);
4072 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4075 foreach (ResumableStatement s in resume_points)
4076 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4079 ig.MarkLabel (end_of_try);
4081 ig.BeginFinallyBlock ();
4083 EmitFinallyBody (ec);
4085 ig.EndExceptionBlock ();
4089 public class Lock : ExceptionStatement {
4091 public Statement Statement;
4092 TemporaryVariable temp;
4094 public Lock (Expression expr, Statement stmt, Location l)
4101 public override bool Resolve (BlockContext ec)
4103 expr = expr.Resolve (ec);
4107 if (!TypeManager.IsReferenceType (expr.Type)){
4108 ec.Report.Error (185, loc,
4109 "`{0}' is not a reference type as required by the lock statement",
4110 TypeManager.CSharpName (expr.Type));
4114 ec.StartFlowBranching (this);
4115 bool ok = Statement.Resolve (ec);
4116 ec.EndFlowBranching ();
4118 ok &= base.Resolve (ec);
4120 // Avoid creating libraries that reference the internal
4123 if (t == TypeManager.null_type)
4124 t = TypeManager.object_type;
4126 temp = new TemporaryVariable (t, loc);
4129 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4130 Type monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4131 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4132 monitor_type, "Enter", loc, TypeManager.object_type);
4133 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4134 monitor_type, "Exit", loc, TypeManager.object_type);
4140 protected override void EmitPreTryBody (EmitContext ec)
4142 ILGenerator ig = ec.ig;
4144 temp.EmitAssign (ec, expr);
4146 ig.Emit (OpCodes.Call, (MethodInfo) TypeManager.void_monitor_enter_object.MetaInfo);
4149 protected override void EmitTryBody (EmitContext ec)
4151 Statement.Emit (ec);
4154 protected override void EmitFinallyBody (EmitContext ec)
4157 ec.ig.Emit (OpCodes.Call, (MethodInfo) TypeManager.void_monitor_exit_object.MetaInfo);
4160 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4162 expr.MutateHoistedGenericType (storey);
4163 temp.MutateHoistedGenericType (storey);
4164 Statement.MutateHoistedGenericType (storey);
4167 protected override void CloneTo (CloneContext clonectx, Statement t)
4169 Lock target = (Lock) t;
4171 target.expr = expr.Clone (clonectx);
4172 target.Statement = Statement.Clone (clonectx);
4176 public class Unchecked : Statement {
4179 public Unchecked (Block b)
4185 public override bool Resolve (BlockContext ec)
4187 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4188 return Block.Resolve (ec);
4191 protected override void DoEmit (EmitContext ec)
4193 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4197 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4199 Block.MutateHoistedGenericType (storey);
4202 protected override void CloneTo (CloneContext clonectx, Statement t)
4204 Unchecked target = (Unchecked) t;
4206 target.Block = clonectx.LookupBlock (Block);
4210 public class Checked : Statement {
4213 public Checked (Block b)
4216 b.Unchecked = false;
4219 public override bool Resolve (BlockContext ec)
4221 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4222 return Block.Resolve (ec);
4225 protected override void DoEmit (EmitContext ec)
4227 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4231 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4233 Block.MutateHoistedGenericType (storey);
4236 protected override void CloneTo (CloneContext clonectx, Statement t)
4238 Checked target = (Checked) t;
4240 target.Block = clonectx.LookupBlock (Block);
4244 public class Unsafe : Statement {
4247 public Unsafe (Block b)
4250 Block.Unsafe = true;
4251 loc = b.StartLocation;
4254 public override bool Resolve (BlockContext ec)
4256 if (ec.CurrentIterator != null)
4257 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4259 using (ec.Set (ResolveContext.Options.UnsafeScope))
4260 return Block.Resolve (ec);
4263 protected override void DoEmit (EmitContext ec)
4268 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4270 Block.MutateHoistedGenericType (storey);
4273 protected override void CloneTo (CloneContext clonectx, Statement t)
4275 Unsafe target = (Unsafe) t;
4277 target.Block = clonectx.LookupBlock (Block);
4284 class Fixed : Statement {
4286 List<KeyValuePair<LocalInfo, Expression>> declarators;
4287 Statement statement;
4292 abstract class Emitter
4294 protected LocalInfo vi;
4295 protected Expression converted;
4297 protected Emitter (Expression expr, LocalInfo li)
4303 public abstract void Emit (EmitContext ec);
4304 public abstract void EmitExit (EmitContext ec);
4307 class ExpressionEmitter : Emitter {
4308 public ExpressionEmitter (Expression converted, LocalInfo li) :
4309 base (converted, li)
4313 public override void Emit (EmitContext ec) {
4315 // Store pointer in pinned location
4317 converted.Emit (ec);
4321 public override void EmitExit (EmitContext ec)
4323 ec.ig.Emit (OpCodes.Ldc_I4_0);
4324 ec.ig.Emit (OpCodes.Conv_U);
4329 class StringEmitter : Emitter
4331 LocalInfo pinned_string;
4333 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4336 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4337 pinned_string.Pinned = true;
4340 public StringEmitter Resolve (ResolveContext rc)
4342 pinned_string.Resolve (rc);
4344 if (TypeManager.int_get_offset_to_string_data == null) {
4345 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4346 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4352 public override void Emit (EmitContext ec)
4354 pinned_string.ResolveVariable (ec);
4356 converted.Emit (ec);
4357 pinned_string.EmitAssign (ec);
4359 // TODO: Should use Binary::Add
4360 pinned_string.Emit (ec);
4361 ec.ig.Emit (OpCodes.Conv_I);
4363 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4364 //pe.InstanceExpression = pinned_string;
4365 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4367 ec.ig.Emit (OpCodes.Add);
4371 public override void EmitExit (EmitContext ec)
4373 ec.ig.Emit (OpCodes.Ldnull);
4374 pinned_string.EmitAssign (ec);
4378 public Fixed (Expression type, List<KeyValuePair<LocalInfo, Expression>> decls, Statement stmt, Location l)
4381 declarators = decls;
4386 public Statement Statement {
4387 get { return statement; }
4390 public override bool Resolve (BlockContext ec)
4393 Expression.UnsafeError (ec, loc);
4397 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4398 if (texpr == null) {
4399 if (type is VarExpr)
4400 ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4405 expr_type = texpr.Type;
4407 data = new Emitter [declarators.Count];
4409 if (!expr_type.IsPointer){
4410 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4415 foreach (var p in declarators){
4416 LocalInfo vi = p.Key;
4417 Expression e = p.Value;
4419 vi.VariableInfo.SetAssigned (ec);
4420 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4423 // The rules for the possible declarators are pretty wise,
4424 // but the production on the grammar is more concise.
4426 // So we have to enforce these rules here.
4428 // We do not resolve before doing the case 1 test,
4429 // because the grammar is explicit in that the token &
4430 // is present, so we need to test for this particular case.
4434 ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4438 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4448 if (e.Type.IsArray){
4449 Type array_type = TypeManager.GetElementType (e.Type);
4452 // Provided that array_type is unmanaged,
4454 if (!TypeManager.VerifyUnmanaged (ec.Compiler, array_type, loc))
4458 // and T* is implicitly convertible to the
4459 // pointer type given in the fixed statement.
4461 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4463 Expression converted = Convert.ImplicitConversionRequired (
4464 ec, array_ptr, vi.VariableType, loc);
4465 if (converted == null)
4469 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4471 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4472 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc), loc),
4473 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc), loc), loc)),
4474 new NullPointer (loc),
4477 converted = converted.Resolve (ec);
4479 data [i] = new ExpressionEmitter (converted, vi);
4488 if (e.Type == TypeManager.string_type){
4489 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4494 // Case 4: fixed buffer
4495 if (e is FixedBufferPtr) {
4496 data [i++] = new ExpressionEmitter (e, vi);
4501 // Case 1: & object.
4503 Unary u = e as Unary;
4504 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4505 IVariableReference vr = u.Expr as IVariableReference;
4506 if (vr == null || !vr.IsFixed) {
4507 data [i] = new ExpressionEmitter (e, vi);
4511 if (data [i++] == null)
4512 ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4514 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4517 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4518 bool ok = statement.Resolve (ec);
4519 bool flow_unreachable = ec.EndFlowBranching ();
4520 has_ret = flow_unreachable;
4525 protected override void DoEmit (EmitContext ec)
4527 for (int i = 0; i < data.Length; i++) {
4531 statement.Emit (ec);
4537 // Clear the pinned variable
4539 for (int i = 0; i < data.Length; i++) {
4540 data [i].EmitExit (ec);
4544 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4546 // Fixed statement cannot be used inside anonymous methods or lambdas
4547 throw new NotSupportedException ();
4550 protected override void CloneTo (CloneContext clonectx, Statement t)
4552 Fixed target = (Fixed) t;
4554 target.type = type.Clone (clonectx);
4555 target.declarators = new List<KeyValuePair<LocalInfo, Expression>> (declarators.Count);
4556 foreach (var p in declarators) {
4557 target.declarators.Add (new KeyValuePair<LocalInfo, Expression> (
4558 clonectx.LookupVariable (p.Key), p.Value.Clone (clonectx)));
4561 target.statement = statement.Clone (clonectx);
4565 public class Catch : Statement {
4566 public readonly string Name;
4568 public Block VarBlock;
4570 Expression type_expr;
4573 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4578 VarBlock = var_block;
4582 public Type CatchType {
4588 public bool IsGeneral {
4590 return type_expr == null;
4594 protected override void DoEmit (EmitContext ec)
4596 ILGenerator ig = ec.ig;
4598 if (CatchType != null)
4599 ig.BeginCatchBlock (CatchType);
4601 ig.BeginCatchBlock (TypeManager.object_type);
4603 if (VarBlock != null)
4607 // TODO: Move to resolve
4608 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4609 lvr.Resolve (new ResolveContext (ec.MemberContext));
4611 // Only to make verifier happy
4612 if (TypeManager.IsGenericParameter (lvr.Type))
4613 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4616 if (lvr.IsHoisted) {
4617 LocalTemporary lt = new LocalTemporary (lvr.Type);
4621 // Variable is at the top of the stack
4622 source = EmptyExpression.Null;
4625 lvr.EmitAssign (ec, source, false, false);
4627 ig.Emit (OpCodes.Pop);
4632 public override bool Resolve (BlockContext ec)
4634 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4635 if (type_expr != null) {
4636 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4642 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4643 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4649 if (!Block.Resolve (ec))
4652 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4653 // emit the "unused variable" warnings.
4654 if (VarBlock != null)
4655 return VarBlock.Resolve (ec);
4661 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4664 type = storey.MutateType (type);
4665 if (VarBlock != null)
4666 VarBlock.MutateHoistedGenericType (storey);
4667 Block.MutateHoistedGenericType (storey);
4670 protected override void CloneTo (CloneContext clonectx, Statement t)
4672 Catch target = (Catch) t;
4674 if (type_expr != null)
4675 target.type_expr = type_expr.Clone (clonectx);
4676 if (VarBlock != null)
4677 target.VarBlock = clonectx.LookupBlock (VarBlock);
4678 target.Block = clonectx.LookupBlock (Block);
4682 public class TryFinally : ExceptionStatement {
4686 public TryFinally (Statement stmt, Block fini, Location l)
4693 public override bool Resolve (BlockContext ec)
4697 ec.StartFlowBranching (this);
4699 if (!stmt.Resolve (ec))
4703 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4704 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4705 if (!fini.Resolve (ec))
4709 ec.EndFlowBranching ();
4711 ok &= base.Resolve (ec);
4716 protected override void EmitPreTryBody (EmitContext ec)
4720 protected override void EmitTryBody (EmitContext ec)
4725 protected override void EmitFinallyBody (EmitContext ec)
4730 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4732 stmt.MutateHoistedGenericType (storey);
4733 fini.MutateHoistedGenericType (storey);
4736 protected override void CloneTo (CloneContext clonectx, Statement t)
4738 TryFinally target = (TryFinally) t;
4740 target.stmt = (Statement) stmt.Clone (clonectx);
4742 target.fini = clonectx.LookupBlock (fini);
4746 public class TryCatch : Statement {
4748 public List<Catch> Specific;
4749 public Catch General;
4750 bool inside_try_finally, code_follows;
4752 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4755 this.Specific = catch_clauses;
4756 this.inside_try_finally = inside_try_finally;
4758 Catch c = catch_clauses [0];
4761 catch_clauses.RemoveAt (0);
4767 public override bool Resolve (BlockContext ec)
4771 ec.StartFlowBranching (this);
4773 if (!Block.Resolve (ec))
4776 Type[] prev_catches = new Type [Specific.Count];
4778 foreach (Catch c in Specific){
4779 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4781 if (c.Name != null) {
4782 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4784 throw new Exception ();
4786 vi.VariableInfo = null;
4789 if (!c.Resolve (ec)) {
4794 Type resolved_type = c.CatchType;
4795 for (int ii = 0; ii < last_index; ++ii) {
4796 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4797 ec.Report.Error (160, c.loc,
4798 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4799 TypeManager.CSharpName (prev_catches [ii]));
4804 prev_catches [last_index++] = resolved_type;
4807 if (General != null) {
4808 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4809 foreach (Catch c in Specific){
4810 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4811 ec.Report.Warning (1058, 1, c.loc, "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4816 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4818 if (!General.Resolve (ec))
4822 ec.EndFlowBranching ();
4824 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4825 // So, ensure there's some IL code after this statement
4826 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4827 ec.NeedReturnLabel ();
4832 public void SomeCodeFollows ()
4834 code_follows = true;
4837 protected override void DoEmit (EmitContext ec)
4839 ILGenerator ig = ec.ig;
4841 if (!inside_try_finally)
4842 ig.BeginExceptionBlock ();
4846 foreach (Catch c in Specific)
4849 if (General != null)
4852 if (!inside_try_finally)
4853 ig.EndExceptionBlock ();
4856 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4858 Block.MutateHoistedGenericType (storey);
4860 if (General != null)
4861 General.MutateHoistedGenericType (storey);
4862 if (Specific != null) {
4863 foreach (Catch c in Specific)
4864 c.MutateHoistedGenericType (storey);
4868 protected override void CloneTo (CloneContext clonectx, Statement t)
4870 TryCatch target = (TryCatch) t;
4872 target.Block = clonectx.LookupBlock (Block);
4873 if (General != null)
4874 target.General = (Catch) General.Clone (clonectx);
4875 if (Specific != null){
4876 target.Specific = new List<Catch> ();
4877 foreach (Catch c in Specific)
4878 target.Specific.Add ((Catch) c.Clone (clonectx));
4883 // FIXME: Why is it almost exact copy of Using ??
4884 public class UsingTemporary : ExceptionStatement {
4885 TemporaryVariable local_copy;
4886 public Statement Statement;
4890 public UsingTemporary (Expression expr, Statement stmt, Location l)
4897 public override bool Resolve (BlockContext ec)
4899 expr = expr.Resolve (ec);
4903 expr_type = expr.Type;
4905 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type) &&
4906 Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4907 if (!TypeManager.IsDynamicType (expr_type)) {
4908 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4912 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.idisposable_type, loc);
4913 expr_type = expr.Type;
4916 local_copy = new TemporaryVariable (expr_type, loc);
4917 local_copy.Resolve (ec);
4919 ec.StartFlowBranching (this);
4921 bool ok = Statement.Resolve (ec);
4923 ec.EndFlowBranching ();
4925 ok &= base.Resolve (ec);
4927 if (TypeManager.void_dispose_void == null) {
4928 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4929 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4935 protected override void EmitPreTryBody (EmitContext ec)
4937 local_copy.EmitAssign (ec, expr);
4940 protected override void EmitTryBody (EmitContext ec)
4942 Statement.Emit (ec);
4945 protected override void EmitFinallyBody (EmitContext ec)
4947 ILGenerator ig = ec.ig;
4948 if (!TypeManager.IsStruct (expr_type)) {
4949 Label skip = ig.DefineLabel ();
4950 local_copy.Emit (ec);
4951 ig.Emit (OpCodes.Brfalse, skip);
4952 local_copy.Emit (ec);
4953 ig.Emit (OpCodes.Callvirt, (MethodInfo) TypeManager.void_dispose_void.MetaInfo);
4954 ig.MarkLabel (skip);
4958 Expression ml = Expression.MemberLookup (RootContext.ToplevelTypes.Compiler,
4959 ec.CurrentType, TypeManager.idisposable_type, expr_type,
4960 "Dispose", Location.Null);
4962 if (!(ml is MethodGroupExpr)) {
4963 local_copy.Emit (ec);
4964 ig.Emit (OpCodes.Box, expr_type);
4965 ig.Emit (OpCodes.Callvirt, (MethodInfo) TypeManager.void_dispose_void.MetaInfo);
4969 MethodSpec mi = null;
4971 foreach (var mk in ((MethodGroupExpr) ml).Methods) {
4972 if (mk.Parameters.IsEmpty) {
4979 ec.Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4983 local_copy.AddressOf (ec, AddressOp.Load);
4984 ig.Emit (OpCodes.Call, (MethodInfo) mi.MetaInfo);
4987 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4989 expr_type = storey.MutateType (expr_type);
4990 local_copy.MutateHoistedGenericType (storey);
4991 Statement.MutateHoistedGenericType (storey);
4994 protected override void CloneTo (CloneContext clonectx, Statement t)
4996 UsingTemporary target = (UsingTemporary) t;
4998 target.expr = expr.Clone (clonectx);
4999 target.Statement = Statement.Clone (clonectx);
5003 public class Using : ExceptionStatement {
5005 public Statement EmbeddedStatement {
5006 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
5012 ExpressionStatement assign;
5014 public Using (Expression var, Expression init, Statement stmt, Location l)
5022 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
5024 ec.Report.SymbolRelatedToPreviousError (expr.Type);
5025 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5026 TypeManager.CSharpName (expr.Type));
5029 protected override void EmitPreTryBody (EmitContext ec)
5031 assign.EmitStatement (ec);
5034 protected override void EmitTryBody (EmitContext ec)
5039 protected override void EmitFinallyBody (EmitContext ec)
5041 ILGenerator ig = ec.ig;
5042 Label skip = ig.DefineLabel ();
5044 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5045 if (emit_null_check) {
5047 ig.Emit (OpCodes.Brfalse, skip);
5050 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5052 if (emit_null_check)
5053 ig.MarkLabel (skip);
5056 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5058 assign.MutateHoistedGenericType (storey);
5059 var.MutateHoistedGenericType (storey);
5060 stmt.MutateHoistedGenericType (storey);
5063 public override bool Resolve (BlockContext ec)
5065 if (!ResolveVariable (ec))
5068 ec.StartFlowBranching (this);
5070 bool ok = stmt.Resolve (ec);
5072 ec.EndFlowBranching ();
5074 ok &= base.Resolve (ec);
5076 if (TypeManager.void_dispose_void == null) {
5077 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5078 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5084 bool ResolveVariable (BlockContext ec)
5086 assign = new SimpleAssign (var, init, loc);
5087 assign = assign.ResolveStatement (ec);
5091 if (assign.Type == TypeManager.idisposable_type ||
5092 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5096 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5098 if (TypeManager.IsDynamicType (assign.Type)) {
5099 e = Convert.ImplicitConversionRequired (ec, assign, TypeManager.idisposable_type, loc);
5100 var = new TemporaryVariable (e.Type, loc);
5101 assign = new SimpleAssign (var, e, loc).ResolveStatement (ec);
5105 Error_IsNotConvertibleToIDisposable (ec, var);
5109 throw new NotImplementedException ("covariance?");
5112 protected override void CloneTo (CloneContext clonectx, Statement t)
5114 Using target = (Using) t;
5116 target.var = var.Clone (clonectx);
5117 target.init = init.Clone (clonectx);
5118 target.stmt = stmt.Clone (clonectx);
5123 /// Implementation of the foreach C# statement
5125 public class Foreach : Statement {
5127 sealed class ArrayForeach : Statement
5129 class ArrayCounter : TemporaryVariable
5131 StatementExpression increment;
5133 public ArrayCounter (Location loc)
5134 : base (TypeManager.int32_type, loc)
5138 public void ResolveIncrement (BlockContext ec)
5140 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
5141 increment.Resolve (ec);
5144 public void EmitIncrement (EmitContext ec)
5146 increment.Emit (ec);
5150 readonly Foreach for_each;
5151 readonly Statement statement;
5154 TemporaryVariable[] lengths;
5155 Expression [] length_exprs;
5156 ArrayCounter[] counter;
5158 TemporaryVariable copy;
5161 public ArrayForeach (Foreach @foreach, int rank)
5163 for_each = @foreach;
5164 statement = for_each.statement;
5167 counter = new ArrayCounter [rank];
5168 length_exprs = new Expression [rank];
5171 // Only use temporary length variables when dealing with
5172 // multi-dimensional arrays
5175 lengths = new TemporaryVariable [rank];
5178 protected override void CloneTo (CloneContext clonectx, Statement target)
5180 throw new NotImplementedException ();
5183 public override bool Resolve (BlockContext ec)
5185 copy = new TemporaryVariable (for_each.expr.Type, loc);
5188 int rank = length_exprs.Length;
5189 Arguments list = new Arguments (rank);
5190 for (int i = 0; i < rank; i++) {
5191 counter [i] = new ArrayCounter (loc);
5192 counter [i].ResolveIncrement (ec);
5195 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5197 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5198 lengths [i].Resolve (ec);
5200 Arguments args = new Arguments (1);
5201 args.Add (new Argument (new IntConstant (i, loc)));
5202 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5205 list.Add (new Argument (counter [i]));
5208 access = new ElementAccess (copy, list).Resolve (ec);
5212 Expression var_type = for_each.type;
5213 VarExpr ve = var_type as VarExpr;
5215 // Infer implicitly typed local variable from foreach array type
5216 var_type = new TypeExpression (access.Type, ve.Location);
5219 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5220 if (var_type == null)
5223 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5229 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5230 ec.CurrentBranching.CreateSibling ();
5232 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5233 if (for_each.variable == null)
5236 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5237 if (!statement.Resolve (ec))
5239 ec.EndFlowBranching ();
5241 // There's no direct control flow from the end of the embedded statement to the end of the loop
5242 ec.CurrentBranching.CurrentUsageVector.Goto ();
5244 ec.EndFlowBranching ();
5249 protected override void DoEmit (EmitContext ec)
5251 ILGenerator ig = ec.ig;
5253 copy.EmitAssign (ec, for_each.expr);
5255 int rank = length_exprs.Length;
5256 Label[] test = new Label [rank];
5257 Label[] loop = new Label [rank];
5259 for (int i = 0; i < rank; i++) {
5260 test [i] = ig.DefineLabel ();
5261 loop [i] = ig.DefineLabel ();
5263 if (lengths != null)
5264 lengths [i].EmitAssign (ec, length_exprs [i]);
5267 IntConstant zero = new IntConstant (0, loc);
5268 for (int i = 0; i < rank; i++) {
5269 counter [i].EmitAssign (ec, zero);
5271 ig.Emit (OpCodes.Br, test [i]);
5272 ig.MarkLabel (loop [i]);
5275 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5277 statement.Emit (ec);
5279 ig.MarkLabel (ec.LoopBegin);
5281 for (int i = rank - 1; i >= 0; i--){
5282 counter [i].EmitIncrement (ec);
5284 ig.MarkLabel (test [i]);
5285 counter [i].Emit (ec);
5287 if (lengths != null)
5288 lengths [i].Emit (ec);
5290 length_exprs [i].Emit (ec);
5292 ig.Emit (OpCodes.Blt, loop [i]);
5295 ig.MarkLabel (ec.LoopEnd);
5298 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5300 for_each.expr.MutateHoistedGenericType (storey);
5302 copy.MutateHoistedGenericType (storey);
5303 conv.MutateHoistedGenericType (storey);
5304 statement.MutateHoistedGenericType (storey);
5306 for (int i = 0; i < counter.Length; i++) {
5307 counter [i].MutateHoistedGenericType (storey);
5308 if (lengths != null)
5309 lengths [i].MutateHoistedGenericType (storey);
5314 sealed class CollectionForeach : Statement
5316 class CollectionForeachStatement : Statement
5319 Expression variable, current, conv;
5320 Statement statement;
5323 public CollectionForeachStatement (Type type, Expression variable,
5324 Expression current, Statement statement,
5328 this.variable = variable;
5329 this.current = current;
5330 this.statement = statement;
5334 protected override void CloneTo (CloneContext clonectx, Statement target)
5336 throw new NotImplementedException ();
5339 public override bool Resolve (BlockContext ec)
5341 current = current.Resolve (ec);
5342 if (current == null)
5345 conv = Convert.ExplicitConversion (ec, current, type, loc);
5349 assign = new SimpleAssign (variable, conv, loc);
5350 if (assign.Resolve (ec) == null)
5353 if (!statement.Resolve (ec))
5359 protected override void DoEmit (EmitContext ec)
5361 assign.EmitStatement (ec);
5362 statement.Emit (ec);
5365 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5367 assign.MutateHoistedGenericType (storey);
5368 statement.MutateHoistedGenericType (storey);
5372 Expression variable, expr;
5373 Statement statement;
5375 TemporaryVariable enumerator;
5380 MethodGroupExpr get_enumerator;
5381 PropertyExpr get_current;
5382 MethodSpec move_next;
5383 Expression var_type;
5384 Type enumerator_type;
5385 bool enumerator_found;
5387 public CollectionForeach (Expression var_type, Expression var,
5388 Expression expr, Statement stmt, Location l)
5390 this.var_type = var_type;
5391 this.variable = var;
5397 protected override void CloneTo (CloneContext clonectx, Statement target)
5399 throw new NotImplementedException ();
5402 bool GetEnumeratorFilter (ResolveContext ec, MethodSpec mi)
5404 Type return_type = mi.ReturnType;
5407 // Ok, we can access it, now make sure that we can do something
5408 // with this `GetEnumerator'
5411 if (return_type == TypeManager.ienumerator_type ||
5412 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5414 // If it is not an interface, lets try to find the methods ourselves.
5415 // For example, if we have:
5416 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5417 // We can avoid the iface call. This is a runtime perf boost.
5418 // even bigger if we have a ValueType, because we avoid the cost
5421 // We have to make sure that both methods exist for us to take
5422 // this path. If one of the methods does not exist, we will just
5423 // use the interface. Sadly, this complex if statement is the only
5424 // way I could do this without a goto
5427 if (TypeManager.bool_movenext_void == null) {
5428 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5429 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5432 if (TypeManager.ienumerator_getcurrent == null) {
5433 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5434 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5438 // Prefer a generic enumerator over a non-generic one.
5440 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5441 enumerator_type = return_type;
5442 if (!FetchGetCurrent (ec, return_type))
5443 get_current = new PropertyExpr (
5444 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5445 if (!FetchMoveNext (return_type))
5446 move_next = TypeManager.bool_movenext_void;
5450 if (return_type.IsInterface ||
5451 !FetchMoveNext (return_type) ||
5452 !FetchGetCurrent (ec, return_type)) {
5453 enumerator_type = return_type;
5454 move_next = TypeManager.bool_movenext_void;
5455 get_current = new PropertyExpr (
5456 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5461 // Ok, so they dont return an IEnumerable, we will have to
5462 // find if they support the GetEnumerator pattern.
5465 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5466 ec.Report.Error (202, loc, "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5467 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5472 enumerator_type = return_type;
5478 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5480 bool FetchMoveNext (Type t)
5482 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5484 BindingFlags.Public | BindingFlags.Instance,
5487 if (move_next_list == null)
5490 foreach (MemberInfo m in move_next_list){
5491 MethodInfo mi = (MethodInfo) m;
5493 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5494 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5495 move_next = Import.CreateMethod (mi);
5504 // Retrieves a `public T get_Current ()' method from the Type `t'
5506 bool FetchGetCurrent (ResolveContext ec, Type t)
5508 PropertyExpr pe = Expression.MemberLookup (ec.Compiler,
5509 ec.CurrentType, t, "Current", MemberTypes.Property,
5510 Expression.AllBindingFlags, loc) as PropertyExpr;
5518 void Error_Enumerator (BlockContext ec)
5520 if (enumerator_found) {
5524 ec.Report.Error (1579, loc,
5525 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5526 TypeManager.CSharpName (expr.Type));
5529 bool IsOverride (MethodSpec ms)
5531 MethodInfo m = (MethodInfo) ms.MetaInfo;
5532 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5534 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5536 if (m is MethodBuilder)
5539 MethodInfo base_method = m.GetBaseDefinition ();
5540 return base_method != m;
5543 bool TryType (ResolveContext ec, Type t)
5545 MethodGroupExpr mg = Expression.MemberLookup (ec.Compiler,
5546 ec.CurrentType, t, "GetEnumerator", MemberTypes.Method,
5547 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5551 MethodSpec result = null;
5552 MethodSpec tmp_move_next = null;
5553 PropertyExpr tmp_get_cur = null;
5554 Type tmp_enumerator_type = enumerator_type;
5555 foreach (var mi in mg.Methods) {
5556 if (!mi.Parameters.IsEmpty)
5559 // Check whether GetEnumerator is public
5560 if ((mi.MetaInfo.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5563 if (IsOverride (mi))
5566 enumerator_found = true;
5568 if (!GetEnumeratorFilter (ec, mi))
5571 if (result != null) {
5572 if (TypeManager.IsGenericType (result.ReturnType)) {
5573 if (!TypeManager.IsGenericType (mi.ReturnType))
5576 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5577 ec.Report.SymbolRelatedToPreviousError (t);
5578 ec.Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5579 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5580 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5584 // Always prefer generics enumerators
5585 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5586 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5587 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5590 ec.Report.SymbolRelatedToPreviousError (result.MetaInfo);
5591 ec.Report.SymbolRelatedToPreviousError (mi.MetaInfo);
5592 ec.Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5593 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result.MetaInfo), TypeManager.CSharpSignature (mi.MetaInfo));
5598 tmp_move_next = move_next;
5599 tmp_get_cur = get_current;
5600 tmp_enumerator_type = enumerator_type;
5601 if (mi.DeclaringType == t)
5605 if (result != null) {
5606 move_next = tmp_move_next;
5607 get_current = tmp_get_cur;
5608 enumerator_type = tmp_enumerator_type;
5609 var mi = new [] { result };
5610 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5612 if (t != expr.Type) {
5613 expr = Convert.ExplicitConversion (
5616 throw new InternalErrorException ();
5619 get_enumerator.InstanceExpression = expr;
5620 get_enumerator.IsBase = t != expr.Type;
5628 bool ProbeCollectionType (ResolveContext ec, Type t)
5630 int errors = ec.Report.Errors;
5631 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5632 if (TryType (ec, tt))
5637 if (ec.Report.Errors > errors)
5641 // Now try to find the method in the interfaces
5643 Type [] ifaces = TypeManager.GetInterfaces (t);
5644 foreach (Type i in ifaces){
5645 if (TryType (ec, i))
5652 public override bool Resolve (BlockContext ec)
5654 enumerator_type = TypeManager.ienumerator_type;
5656 bool is_dynamic = TypeManager.IsDynamicType (expr.Type);
5658 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5660 if (!ProbeCollectionType (ec, expr.Type)) {
5661 Error_Enumerator (ec);
5665 VarExpr ve = var_type as VarExpr;
5667 // Infer implicitly typed local variable from foreach enumerable type
5668 var_type = new TypeExpression (
5669 is_dynamic ? InternalType.Dynamic : get_current.PropertyInfo.PropertyType,
5673 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5674 if (var_type == null)
5677 enumerator = new TemporaryVariable (enumerator_type, loc);
5678 enumerator.Resolve (ec);
5680 init = new Invocation (get_enumerator, null);
5681 init = init.Resolve (ec);
5685 Expression move_next_expr;
5687 var mi = new [] { move_next };
5688 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5689 mg.InstanceExpression = enumerator;
5691 move_next_expr = new Invocation (mg, null);
5694 get_current.InstanceExpression = enumerator;
5696 Statement block = new CollectionForeachStatement (
5697 var_type.Type, variable, get_current, statement, loc);
5699 loop = new While (new BooleanExpression (move_next_expr), block, loc);
5702 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5703 if (implements_idisposable || !enumerator_type.IsSealed) {
5704 wrapper = new DisposableWrapper (this, implements_idisposable);
5706 wrapper = new NonDisposableWrapper (this);
5709 return wrapper.Resolve (ec);
5712 protected override void DoEmit (EmitContext ec)
5717 class NonDisposableWrapper : Statement {
5718 CollectionForeach parent;
5720 internal NonDisposableWrapper (CollectionForeach parent)
5722 this.parent = parent;
5725 protected override void CloneTo (CloneContext clonectx, Statement target)
5727 throw new NotSupportedException ();
5730 public override bool Resolve (BlockContext ec)
5732 return parent.ResolveLoop (ec);
5735 protected override void DoEmit (EmitContext ec)
5737 parent.EmitLoopInit (ec);
5738 parent.EmitLoopBody (ec);
5741 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5743 throw new NotSupportedException ();
5747 sealed class DisposableWrapper : ExceptionStatement
5749 CollectionForeach parent;
5750 bool implements_idisposable;
5752 internal DisposableWrapper (CollectionForeach parent, bool implements)
5754 this.parent = parent;
5755 this.implements_idisposable = implements;
5758 protected override void CloneTo (CloneContext clonectx, Statement target)
5760 throw new NotSupportedException ();
5763 public override bool Resolve (BlockContext ec)
5767 ec.StartFlowBranching (this);
5769 if (!parent.ResolveLoop (ec))
5772 ec.EndFlowBranching ();
5774 ok &= base.Resolve (ec);
5776 if (TypeManager.void_dispose_void == null) {
5777 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5778 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5783 protected override void EmitPreTryBody (EmitContext ec)
5785 parent.EmitLoopInit (ec);
5788 protected override void EmitTryBody (EmitContext ec)
5790 parent.EmitLoopBody (ec);
5793 protected override void EmitFinallyBody (EmitContext ec)
5795 Expression instance = parent.enumerator;
5796 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5797 ILGenerator ig = ec.ig;
5799 parent.enumerator.Emit (ec);
5801 Label call_dispose = ig.DefineLabel ();
5803 if (!implements_idisposable) {
5804 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5805 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5811 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5813 // using 'endfinally' to empty the evaluation stack
5814 ig.Emit (OpCodes.Endfinally);
5815 ig.MarkLabel (call_dispose);
5818 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5821 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5823 throw new NotSupportedException ();
5827 bool ResolveLoop (BlockContext ec)
5829 return loop.Resolve (ec);
5832 void EmitLoopInit (EmitContext ec)
5834 enumerator.EmitAssign (ec, init);
5837 void EmitLoopBody (EmitContext ec)
5842 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5844 enumerator_type = storey.MutateType (enumerator_type);
5845 init.MutateHoistedGenericType (storey);
5846 loop.MutateHoistedGenericType (storey);
5851 Expression variable;
5853 Statement statement;
5855 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5856 Statement stmt, Location l)
5859 this.variable = var;
5865 public Statement Statement {
5866 get { return statement; }
5869 public override bool Resolve (BlockContext ec)
5871 expr = expr.Resolve (ec);
5876 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5880 if (expr.Type == TypeManager.string_type) {
5881 statement = new ArrayForeach (this, 1);
5882 } else if (expr.Type.IsArray) {
5883 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5885 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5886 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5887 expr.ExprClassName);
5891 statement = new CollectionForeach (type, variable, expr, statement, loc);
5894 return statement.Resolve (ec);
5897 protected override void DoEmit (EmitContext ec)
5899 ILGenerator ig = ec.ig;
5901 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5902 ec.LoopBegin = ig.DefineLabel ();
5903 ec.LoopEnd = ig.DefineLabel ();
5905 statement.Emit (ec);
5907 ec.LoopBegin = old_begin;
5908 ec.LoopEnd = old_end;
5911 protected override void CloneTo (CloneContext clonectx, Statement t)
5913 Foreach target = (Foreach) t;
5915 target.type = type.Clone (clonectx);
5916 target.variable = variable.Clone (clonectx);
5917 target.expr = expr.Clone (clonectx);
5918 target.statement = statement.Clone (clonectx);
5921 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5923 statement.MutateHoistedGenericType (storey);