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);
97 public sealed class EmptyStatement : Statement
99 public EmptyStatement (Location loc)
104 public override bool Resolve (BlockContext ec)
109 public override bool ResolveUnreachable (BlockContext ec, bool warn)
114 public override void Emit (EmitContext ec)
118 protected override void DoEmit (EmitContext ec)
120 throw new NotSupportedException ();
123 protected override void CloneTo (CloneContext clonectx, Statement target)
129 public class If : Statement {
131 public Statement TrueStatement;
132 public Statement FalseStatement;
136 public If (Expression bool_expr, Statement true_statement, Location l)
137 : this (bool_expr, true_statement, null, l)
141 public If (Expression bool_expr,
142 Statement true_statement,
143 Statement false_statement,
146 this.expr = bool_expr;
147 TrueStatement = true_statement;
148 FalseStatement = false_statement;
152 public override bool Resolve (BlockContext ec)
156 Report.Debug (1, "START IF BLOCK", loc);
158 expr = expr.Resolve (ec);
163 // Dead code elimination
165 if (expr is Constant) {
166 bool take = !((Constant) expr).IsDefaultValue;
169 if (!TrueStatement.Resolve (ec))
172 if ((FalseStatement != null) &&
173 !FalseStatement.ResolveUnreachable (ec, true))
175 FalseStatement = null;
177 if (!TrueStatement.ResolveUnreachable (ec, true))
179 TrueStatement = null;
181 if ((FalseStatement != null) &&
182 !FalseStatement.Resolve (ec))
190 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
192 ok &= TrueStatement.Resolve (ec);
194 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
196 ec.CurrentBranching.CreateSibling ();
198 if (FalseStatement != null)
199 ok &= FalseStatement.Resolve (ec);
201 ec.EndFlowBranching ();
203 Report.Debug (1, "END IF BLOCK", loc);
208 protected override void DoEmit (EmitContext ec)
210 Label false_target = ec.DefineLabel ();
214 // If we're a boolean constant, Resolve() already
215 // eliminated dead code for us.
217 Constant c = expr as Constant;
219 c.EmitSideEffect (ec);
221 if (!c.IsDefaultValue)
222 TrueStatement.Emit (ec);
223 else if (FalseStatement != null)
224 FalseStatement.Emit (ec);
229 expr.EmitBranchable (ec, false_target, false);
231 TrueStatement.Emit (ec);
233 if (FalseStatement != null){
234 bool branch_emitted = false;
236 end = ec.DefineLabel ();
238 ec.Emit (OpCodes.Br, end);
239 branch_emitted = true;
242 ec.MarkLabel (false_target);
243 FalseStatement.Emit (ec);
248 ec.MarkLabel (false_target);
252 protected override void CloneTo (CloneContext clonectx, Statement t)
256 target.expr = expr.Clone (clonectx);
257 target.TrueStatement = TrueStatement.Clone (clonectx);
258 if (FalseStatement != null)
259 target.FalseStatement = FalseStatement.Clone (clonectx);
263 public class Do : Statement {
264 public Expression expr;
265 public Statement EmbeddedStatement;
267 public Do (Statement statement, BooleanExpression bool_expr, Location l)
270 EmbeddedStatement = statement;
274 public override bool Resolve (BlockContext ec)
278 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
280 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
282 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
283 if (!EmbeddedStatement.Resolve (ec))
285 ec.EndFlowBranching ();
287 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
288 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
290 expr = expr.Resolve (ec);
293 else if (expr is Constant){
294 bool infinite = !((Constant) expr).IsDefaultValue;
296 ec.CurrentBranching.CurrentUsageVector.Goto ();
299 ec.EndFlowBranching ();
304 protected override void DoEmit (EmitContext ec)
306 Label loop = ec.DefineLabel ();
307 Label old_begin = ec.LoopBegin;
308 Label old_end = ec.LoopEnd;
310 ec.LoopBegin = ec.DefineLabel ();
311 ec.LoopEnd = ec.DefineLabel ();
314 EmbeddedStatement.Emit (ec);
315 ec.MarkLabel (ec.LoopBegin);
318 // Dead code elimination
320 if (expr is Constant){
321 bool res = !((Constant) expr).IsDefaultValue;
323 expr.EmitSideEffect (ec);
325 ec.Emit (OpCodes.Br, loop);
327 expr.EmitBranchable (ec, loop, true);
329 ec.MarkLabel (ec.LoopEnd);
331 ec.LoopBegin = old_begin;
332 ec.LoopEnd = old_end;
335 protected override void CloneTo (CloneContext clonectx, Statement t)
339 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
340 target.expr = expr.Clone (clonectx);
344 public class While : Statement {
345 public Expression expr;
346 public Statement Statement;
347 bool infinite, empty;
349 public While (BooleanExpression bool_expr, Statement statement, Location l)
351 this.expr = bool_expr;
352 Statement = statement;
356 public override bool Resolve (BlockContext ec)
360 expr = expr.Resolve (ec);
365 // Inform whether we are infinite or not
367 if (expr is Constant){
368 bool value = !((Constant) expr).IsDefaultValue;
371 if (!Statement.ResolveUnreachable (ec, true))
379 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
381 ec.CurrentBranching.CreateSibling ();
383 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
384 if (!Statement.Resolve (ec))
386 ec.EndFlowBranching ();
388 // There's no direct control flow from the end of the embedded statement to the end of the loop
389 ec.CurrentBranching.CurrentUsageVector.Goto ();
391 ec.EndFlowBranching ();
396 protected override void DoEmit (EmitContext ec)
399 expr.EmitSideEffect (ec);
403 Label old_begin = ec.LoopBegin;
404 Label old_end = ec.LoopEnd;
406 ec.LoopBegin = ec.DefineLabel ();
407 ec.LoopEnd = ec.DefineLabel ();
410 // Inform whether we are infinite or not
412 if (expr is Constant){
413 // expr is 'true', since the 'empty' case above handles the 'false' case
414 ec.MarkLabel (ec.LoopBegin);
415 expr.EmitSideEffect (ec);
417 ec.Emit (OpCodes.Br, ec.LoopBegin);
420 // Inform that we are infinite (ie, `we return'), only
421 // if we do not `break' inside the code.
423 ec.MarkLabel (ec.LoopEnd);
425 Label while_loop = ec.DefineLabel ();
427 ec.Emit (OpCodes.Br, ec.LoopBegin);
428 ec.MarkLabel (while_loop);
432 ec.MarkLabel (ec.LoopBegin);
435 expr.EmitBranchable (ec, while_loop, true);
437 ec.MarkLabel (ec.LoopEnd);
440 ec.LoopBegin = old_begin;
441 ec.LoopEnd = old_end;
444 public override void Emit (EmitContext ec)
449 protected override void CloneTo (CloneContext clonectx, Statement t)
451 While target = (While) t;
453 target.expr = expr.Clone (clonectx);
454 target.Statement = Statement.Clone (clonectx);
458 public class For : Statement {
460 Statement InitStatement;
462 public Statement Statement;
463 bool infinite, empty;
465 public For (Statement init_statement,
466 BooleanExpression test,
471 InitStatement = init_statement;
473 Increment = increment;
474 Statement = statement;
478 public override bool Resolve (BlockContext ec)
482 if (InitStatement != null){
483 if (!InitStatement.Resolve (ec))
488 Test = Test.Resolve (ec);
491 else if (Test is Constant){
492 bool value = !((Constant) Test).IsDefaultValue;
495 if (!Statement.ResolveUnreachable (ec, true))
497 if ((Increment != null) &&
498 !Increment.ResolveUnreachable (ec, false))
508 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
510 ec.CurrentBranching.CreateSibling ();
512 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
514 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
515 if (!Statement.Resolve (ec))
517 ec.EndFlowBranching ();
519 if (Increment != null){
520 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
521 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
524 if (!Increment.Resolve (ec))
529 // There's no direct control flow from the end of the embedded statement to the end of the loop
530 ec.CurrentBranching.CurrentUsageVector.Goto ();
532 ec.EndFlowBranching ();
537 protected override void DoEmit (EmitContext ec)
539 if (InitStatement != null)
540 InitStatement.Emit (ec);
543 Test.EmitSideEffect (ec);
547 Label old_begin = ec.LoopBegin;
548 Label old_end = ec.LoopEnd;
549 Label loop = ec.DefineLabel ();
550 Label test = ec.DefineLabel ();
552 ec.LoopBegin = ec.DefineLabel ();
553 ec.LoopEnd = ec.DefineLabel ();
555 ec.Emit (OpCodes.Br, test);
559 ec.MarkLabel (ec.LoopBegin);
564 // If test is null, there is no test, and we are just
569 // The Resolve code already catches the case for
570 // Test == Constant (false) so we know that
573 if (Test is Constant) {
574 Test.EmitSideEffect (ec);
575 ec.Emit (OpCodes.Br, loop);
577 Test.EmitBranchable (ec, loop, true);
581 ec.Emit (OpCodes.Br, loop);
582 ec.MarkLabel (ec.LoopEnd);
584 ec.LoopBegin = old_begin;
585 ec.LoopEnd = old_end;
588 protected override void CloneTo (CloneContext clonectx, Statement t)
590 For target = (For) t;
592 if (InitStatement != null)
593 target.InitStatement = InitStatement.Clone (clonectx);
595 target.Test = Test.Clone (clonectx);
596 if (Increment != null)
597 target.Increment = Increment.Clone (clonectx);
598 target.Statement = Statement.Clone (clonectx);
602 public class StatementExpression : Statement {
603 ExpressionStatement expr;
605 public StatementExpression (ExpressionStatement expr)
611 public override bool Resolve (BlockContext ec)
613 expr = expr.ResolveStatement (ec);
617 protected override void DoEmit (EmitContext ec)
619 expr.EmitStatement (ec);
622 public override string ToString ()
624 return "StatementExpression (" + expr + ")";
627 protected override void CloneTo (CloneContext clonectx, Statement t)
629 StatementExpression target = (StatementExpression) t;
631 target.expr = (ExpressionStatement) expr.Clone (clonectx);
636 // Simple version of statement list not requiring a block
638 public class StatementList : Statement
640 List<Statement> statements;
642 public StatementList (Statement first, Statement second)
644 statements = new List<Statement> () { first, second };
648 public IList<Statement> Statements {
655 public void Add (Statement statement)
657 statements.Add (statement);
660 public override bool Resolve (BlockContext ec)
662 foreach (var s in statements)
668 protected override void DoEmit (EmitContext ec)
670 foreach (var s in statements)
674 protected override void CloneTo (CloneContext clonectx, Statement target)
676 StatementList t = (StatementList) target;
678 t.statements = new List<Statement> (statements.Count);
679 foreach (Statement s in statements)
680 t.statements.Add (s.Clone (clonectx));
684 // A 'return' or a 'yield break'
685 public abstract class ExitStatement : Statement
687 protected bool unwind_protect;
688 protected abstract bool DoResolve (BlockContext ec);
690 public virtual void Error_FinallyClause (Report Report)
692 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
695 public sealed override bool Resolve (BlockContext ec)
700 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
702 ec.NeedReturnLabel ();
703 ec.CurrentBranching.CurrentUsageVector.Goto ();
709 /// Implements the return statement
711 public class Return : ExitStatement
713 protected Expression Expr;
714 public Return (Expression expr, Location l)
721 public Expression Expression {
728 protected override bool DoResolve (BlockContext ec)
731 if (ec.ReturnType == TypeManager.void_type)
734 if (ec.CurrentIterator != null) {
735 Error_ReturnFromIterator (ec);
737 ec.Report.Error (126, loc,
738 "An object of a type convertible to `{0}' is required for the return statement",
739 ec.ReturnType.GetSignatureForError ());
745 Expr = Expr.Resolve (ec);
747 AnonymousExpression am = ec.CurrentAnonymousMethod;
749 if (ec.ReturnType == TypeManager.void_type) {
750 ec.Report.Error (127, loc,
751 "`{0}': A return keyword must not be followed by any expression when method returns void",
752 ec.GetSignatureForError ());
756 Error_ReturnFromIterator (ec);
760 var l = am as AnonymousMethodBody;
761 if (l != null && l.ReturnTypeInference != null && Expr != null) {
762 l.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
770 if (Expr.Type != ec.ReturnType) {
771 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
775 ec.Report.Error (1662, loc,
776 "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",
777 am.ContainerType, am.GetSignatureForError ());
786 protected override void DoEmit (EmitContext ec)
792 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
796 ec.Emit (OpCodes.Leave, ec.ReturnLabel);
798 ec.Emit (OpCodes.Ret);
801 void Error_ReturnFromIterator (ResolveContext rc)
803 rc.Report.Error (1622, loc,
804 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
807 protected override void CloneTo (CloneContext clonectx, Statement t)
809 Return target = (Return) t;
810 // It's null for simple return;
812 target.Expr = Expr.Clone (clonectx);
816 public class Goto : Statement {
818 LabeledStatement label;
821 public override bool Resolve (BlockContext ec)
823 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
824 ec.CurrentBranching.CurrentUsageVector.Goto ();
828 public Goto (string label, Location l)
834 public string Target {
835 get { return target; }
838 public void SetResolvedTarget (LabeledStatement label)
841 label.AddReference ();
844 protected override void CloneTo (CloneContext clonectx, Statement target)
849 protected override void DoEmit (EmitContext ec)
852 throw new InternalErrorException ("goto emitted before target resolved");
853 Label l = label.LabelTarget (ec);
854 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
858 public class LabeledStatement : Statement {
864 FlowBranching.UsageVector vectors;
866 public LabeledStatement (string name, Location l)
872 public Label LabelTarget (EmitContext ec)
877 label = ec.DefineLabel ();
886 public bool IsDefined {
887 get { return defined; }
890 public bool HasBeenReferenced {
891 get { return referenced; }
894 public FlowBranching.UsageVector JumpOrigins {
895 get { return vectors; }
898 public void AddUsageVector (FlowBranching.UsageVector vector)
900 vector = vector.Clone ();
901 vector.Next = vectors;
905 protected override void CloneTo (CloneContext clonectx, Statement target)
910 public override bool Resolve (BlockContext ec)
912 // this flow-branching will be terminated when the surrounding block ends
913 ec.StartFlowBranching (this);
917 protected override void DoEmit (EmitContext ec)
920 ec.MarkLabel (label);
923 public void AddReference ()
931 /// `goto default' statement
933 public class GotoDefault : Statement {
935 public GotoDefault (Location l)
940 protected override void CloneTo (CloneContext clonectx, Statement target)
945 public override bool Resolve (BlockContext ec)
947 ec.CurrentBranching.CurrentUsageVector.Goto ();
949 if (ec.Switch == null) {
950 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
954 if (!ec.Switch.GotDefault) {
955 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
962 protected override void DoEmit (EmitContext ec)
964 ec.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
969 /// `goto case' statement
971 public class GotoCase : Statement {
975 public GotoCase (Expression e, Location l)
981 public override bool Resolve (BlockContext ec)
983 if (ec.Switch == null){
984 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
988 ec.CurrentBranching.CurrentUsageVector.Goto ();
990 expr = expr.Resolve (ec);
994 Constant c = expr as Constant;
996 ec.Report.Error (150, expr.Location, "A constant value is expected");
1000 TypeSpec type = ec.Switch.SwitchType;
1001 Constant res = c.TryReduce (ec, type, c.Location);
1003 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1007 if (!Convert.ImplicitStandardConversionExists (c, type))
1008 ec.Report.Warning (469, 2, loc,
1009 "The `goto case' value is not implicitly convertible to type `{0}'",
1010 TypeManager.CSharpName (type));
1012 object val = res.GetValue ();
1014 val = SwitchLabel.NullStringCase;
1016 if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
1017 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1018 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1025 protected override void DoEmit (EmitContext ec)
1027 ec.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
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.Emit (OpCodes.Rethrow);
1075 ec.Emit (OpCodes.Throw);
1079 protected override void CloneTo (CloneContext clonectx, Statement t)
1081 Throw target = (Throw) t;
1084 target.expr = expr.Clone (clonectx);
1088 public class Break : Statement {
1090 public Break (Location l)
1095 bool unwind_protect;
1097 public override bool Resolve (BlockContext ec)
1099 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1100 ec.CurrentBranching.CurrentUsageVector.Goto ();
1104 protected override void DoEmit (EmitContext ec)
1106 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1109 protected override void CloneTo (CloneContext clonectx, Statement t)
1115 public class Continue : Statement {
1117 public Continue (Location l)
1122 bool unwind_protect;
1124 public override bool Resolve (BlockContext ec)
1126 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1127 ec.CurrentBranching.CurrentUsageVector.Goto ();
1131 protected override void DoEmit (EmitContext ec)
1133 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1136 protected override void CloneTo (CloneContext clonectx, Statement t)
1142 public interface ILocalVariable
1144 void Emit (EmitContext ec);
1145 void EmitAssign (EmitContext ec);
1146 void EmitAddressOf (EmitContext ec);
1149 public interface IKnownVariable {
1150 Block Block { get; }
1151 Location Location { get; }
1155 // The information about a user-perceived local variable
1157 public class LocalInfo : IKnownVariable, ILocalVariable {
1158 public readonly FullNamedExpression Type;
1160 public TypeSpec VariableType;
1161 public readonly string Name;
1162 public readonly Location Location;
1163 public readonly Block Block;
1165 public VariableInfo VariableInfo;
1166 HoistedVariable hoisted_variant;
1175 CompilerGenerated = 64,
1179 public enum ReadOnlyContext: byte {
1186 ReadOnlyContext ro_context;
1187 LocalBuilder builder;
1189 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1197 public LocalInfo (TypeContainer ds, Block block, Location l)
1199 VariableType = ds.IsGeneric ? ds.CurrentType : ds.Definition;
1204 public void ResolveVariable (EmitContext ec)
1206 if (HoistedVariant != null)
1209 if (builder == null) {
1210 builder = ec.DeclareLocal (VariableType, Pinned);
1214 public void Emit (EmitContext ec)
1216 ec.Emit (OpCodes.Ldloc, builder);
1219 public void EmitAssign (EmitContext ec)
1221 ec.Emit (OpCodes.Stloc, builder);
1224 public void EmitAddressOf (EmitContext ec)
1226 ec.Emit (OpCodes.Ldloca, builder);
1229 public void EmitSymbolInfo (EmitContext ec)
1231 if (builder != null)
1232 ec.DefineLocalVariable (Name, builder);
1236 // Hoisted local variable variant
1238 public HoistedVariable HoistedVariant {
1240 return hoisted_variant;
1243 hoisted_variant = value;
1247 public bool IsThisAssigned (BlockContext ec, Block block)
1249 if (VariableInfo == null)
1250 throw new Exception ();
1252 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1255 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1258 public bool IsAssigned (BlockContext ec)
1260 if (VariableInfo == null)
1261 throw new Exception ();
1263 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1266 public bool Resolve (ResolveContext ec)
1268 if (VariableType != null)
1271 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1275 VariableType = texpr.Type;
1277 if (VariableType.IsStatic) {
1278 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
1282 if (VariableType.IsPointer && !ec.IsUnsafe)
1283 Expression.UnsafeError (ec, Location);
1288 public bool IsConstant {
1289 get { return (flags & Flags.IsConstant) != 0; }
1290 set { flags |= Flags.IsConstant; }
1293 public bool AddressTaken {
1294 get { return (flags & Flags.AddressTaken) != 0; }
1295 set { flags |= Flags.AddressTaken; }
1298 public bool CompilerGenerated {
1299 get { return (flags & Flags.CompilerGenerated) != 0; }
1300 set { flags |= Flags.CompilerGenerated; }
1303 public override string ToString ()
1305 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1306 Name, Type, VariableInfo, Location);
1310 get { return (flags & Flags.Used) != 0; }
1311 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1314 public bool ReadOnly {
1315 get { return (flags & Flags.ReadOnly) != 0; }
1318 public void SetReadOnlyContext (ReadOnlyContext context)
1320 flags |= Flags.ReadOnly;
1321 ro_context = context;
1324 public string GetReadOnlyContext ()
1327 throw new InternalErrorException ("Variable is not readonly");
1329 switch (ro_context) {
1330 case ReadOnlyContext.Fixed:
1331 return "fixed variable";
1332 case ReadOnlyContext.Foreach:
1333 return "foreach iteration variable";
1334 case ReadOnlyContext.Using:
1335 return "using variable";
1337 throw new NotImplementedException ();
1341 // Whether the variable is pinned, if Pinned the variable has been
1342 // allocated in a pinned slot with DeclareLocal.
1344 public bool Pinned {
1345 get { return (flags & Flags.Pinned) != 0; }
1346 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1349 public bool IsThis {
1350 get { return (flags & Flags.IsThis) != 0; }
1351 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1354 Block IKnownVariable.Block {
1355 get { return Block; }
1358 Location IKnownVariable.Location {
1359 get { return Location; }
1362 public LocalInfo Clone (CloneContext clonectx)
1365 // Variables in anonymous block are not resolved yet
1367 if (VariableType == null)
1368 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1371 // Variables in method block are resolved
1373 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1374 li.VariableType = VariableType;
1380 /// Block represents a C# block.
1384 /// This class is used in a number of places: either to represent
1385 /// explicit blocks that the programmer places or implicit blocks.
1387 /// Implicit blocks are used as labels or to introduce variable
1390 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1391 /// they contain extra information that is not necessary on normal blocks.
1393 public class Block : Statement {
1394 public Block Parent;
1395 public Location StartLocation;
1396 public Location EndLocation = Location.Null;
1398 public ExplicitBlock Explicit;
1399 public ToplevelBlock Toplevel; // TODO: Use Explicit
1406 VariablesInitialized = 4,
1410 HasCapturedVariable = 64,
1411 HasCapturedThis = 1 << 7,
1412 IsExpressionTree = 1 << 8
1415 protected Flags flags;
1417 public bool Unchecked {
1418 get { return (flags & Flags.Unchecked) != 0; }
1419 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1422 public bool Unsafe {
1423 get { return (flags & Flags.Unsafe) != 0; }
1424 set { flags |= Flags.Unsafe; }
1428 // The statements in this block
1430 protected List<Statement> statements;
1433 // An array of Blocks. We keep track of children just
1434 // to generate the local variable declarations.
1436 // Statements and child statements are handled through the
1439 List<Block> children;
1442 // Labels. (label, block) pairs.
1444 protected Dictionary<string, LabeledStatement> labels;
1447 // Keeps track of (name, type) pairs
1449 Dictionary<string, LocalInfo> variables;
1452 // Keeps track of constants
1453 Dictionary<string, Expression> constants;
1456 // Temporary variables.
1458 List<LocalInfo> temporary_variables;
1461 // If this is a switch section, the enclosing switch block.
1463 protected ExplicitBlock switch_block;
1465 protected List<Statement> scope_initializers;
1467 List<ToplevelBlock> anonymous_children;
1469 int? resolving_init_idx;
1471 protected static int id;
1475 int assignable_slots;
1476 bool unreachable_shown;
1479 public Block (Block parent)
1480 : this (parent, (Flags) 0, Location.Null, Location.Null)
1483 public Block (Block parent, Flags flags)
1484 : this (parent, flags, Location.Null, Location.Null)
1487 public Block (Block parent, Location start, Location end)
1488 : this (parent, (Flags) 0, start, end)
1492 // Useful when TopLevel block is downgraded to normal block
1494 public Block (ToplevelBlock parent, ToplevelBlock source)
1495 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1497 statements = source.statements;
1498 children = source.children;
1499 labels = source.labels;
1500 variables = source.variables;
1501 constants = source.constants;
1502 switch_block = source.switch_block;
1505 public Block (Block parent, Flags flags, Location start, Location end)
1507 if (parent != null) {
1508 parent.AddChild (this);
1510 // the appropriate constructors will fixup these fields
1511 Toplevel = parent.Toplevel;
1512 Explicit = parent.Explicit;
1515 this.Parent = parent;
1517 this.StartLocation = start;
1518 this.EndLocation = end;
1521 statements = new List<Statement> (4);
1527 get { return this_id; }
1530 public IDictionary<string, LocalInfo> Variables {
1532 if (variables == null)
1533 variables = new Dictionary<string, LocalInfo> ();
1540 public ExplicitBlock CreateSwitchBlock (Location start)
1542 // FIXME: Only explicit block should be created
1543 var new_block = new ExplicitBlock (this, start, start);
1544 new_block.switch_block = Explicit;
1548 void AddChild (Block b)
1550 if (children == null)
1551 children = new List<Block> (1);
1556 public void SetEndLocation (Location loc)
1561 protected void Error_158 (string name, Location loc)
1563 Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
1564 "by the same name in a contained scope", name);
1568 /// Adds a label to the current block.
1572 /// false if the name already exists in this block. true
1576 public bool AddLabel (LabeledStatement target)
1578 if (switch_block != null)
1579 return switch_block.AddLabel (target);
1581 string name = target.Name;
1584 while (cur != null) {
1585 LabeledStatement s = cur.DoLookupLabel (name);
1587 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1588 Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1592 if (this == Explicit)
1598 while (cur != null) {
1599 if (cur.DoLookupLabel (name) != null) {
1600 Error_158 (name, target.loc);
1604 if (children != null) {
1605 foreach (Block b in children) {
1606 LabeledStatement s = b.DoLookupLabel (name);
1610 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1611 Error_158 (name, target.loc);
1619 Toplevel.CheckError158 (name, target.loc);
1622 labels = new Dictionary<string, LabeledStatement> ();
1624 labels.Add (name, target);
1628 public LabeledStatement LookupLabel (string name)
1630 LabeledStatement s = DoLookupLabel (name);
1634 if (children == null)
1637 foreach (Block child in children) {
1638 if (Explicit != child.Explicit)
1641 s = child.LookupLabel (name);
1649 LabeledStatement DoLookupLabel (string name)
1651 if (switch_block != null)
1652 return switch_block.LookupLabel (name);
1655 if (labels.ContainsKey (name))
1656 return labels [name];
1661 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1664 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1665 while (kvi == null) {
1666 b = b.Explicit.Parent;
1669 kvi = b.Explicit.GetKnownVariable (name);
1675 // Is kvi.Block nested inside 'b'
1676 if (b.Explicit != kvi.Block.Explicit) {
1678 // If a variable by the same name it defined in a nested block of this
1679 // block, we violate the invariant meaning in a block.
1682 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1683 Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1688 // It's ok if the definition is in a nested subblock of b, but not
1689 // nested inside this block -- a definition in a sibling block
1690 // should not affect us.
1696 // Block 'b' and kvi.Block are the same textual block.
1697 // However, different variables are extant.
1699 // Check if the variable is in scope in both blocks. We use
1700 // an indirect check that depends on AddVariable doing its
1701 // part in maintaining the invariant-meaning-in-block property.
1703 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1706 if (this is ToplevelBlock) {
1707 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1708 e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
1713 // Even though we detected the error when the name is used, we
1714 // treat it as if the variable declaration was in error.
1716 Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
1717 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1721 protected bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1723 LocalInfo vi = GetLocalInfo (name);
1725 block.Report.SymbolRelatedToPreviousError (vi.Location, name);
1726 if (Explicit == vi.Block.Explicit) {
1727 Error_AlreadyDeclared (l, name, null);
1729 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1730 "parent or current" : "parent");
1735 if (block != null) {
1736 var tblock = block.CheckParameterNameConflict (name);
1737 if (tblock != null) {
1738 if (block == tblock && block is Linq.QueryBlock)
1739 Error_AlreadyDeclared (loc, name);
1741 Error_AlreadyDeclared (loc, name, "parent or current");
1750 public LocalInfo AddVariable (Expression type, string name, Location l)
1752 if (!CheckParentConflictName (Toplevel, name, l))
1755 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1757 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1758 Error_AlreadyDeclared (l, name, "child");
1762 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1765 if ((flags & Flags.VariablesInitialized) != 0)
1766 throw new InternalErrorException ("block has already been resolved");
1771 protected virtual void AddVariable (LocalInfo li)
1773 Variables.Add (li.Name, li);
1774 Explicit.AddKnownVariable (li.Name, li);
1777 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1779 if (reason == null) {
1780 Error_AlreadyDeclared (loc, var);
1784 Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1785 "in this scope because it would give a different meaning " +
1786 "to `{0}', which is already used in a `{1}' scope " +
1787 "to denote something else", var, reason);
1790 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1792 Toplevel.Report.Error (128, loc,
1793 "A local variable named `{0}' is already defined in this scope", name);
1796 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1798 Toplevel.Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1802 public bool AddConstant (Expression type, string name, Expression value, Location l)
1804 if (AddVariable (type, name, l) == null)
1807 if (constants == null)
1808 constants = new Dictionary<string, Expression> ();
1810 constants.Add (name, value);
1812 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1817 static int next_temp_id = 0;
1819 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1821 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1823 if (temporary_variables == null)
1824 temporary_variables = new List<LocalInfo> ();
1826 int id = ++next_temp_id;
1827 string name = "$s_" + id.ToString ();
1829 LocalInfo li = new LocalInfo (te, name, this, loc);
1830 li.CompilerGenerated = true;
1831 temporary_variables.Add (li);
1835 public LocalInfo GetLocalInfo (string name)
1838 for (Block b = this; b != null; b = b.Parent) {
1839 if (b.variables != null && b.variables.TryGetValue (name, out ret)) {
1847 public Expression GetVariableType (string name)
1849 LocalInfo vi = GetLocalInfo (name);
1850 return vi == null ? null : vi.Type;
1853 public Expression GetConstantExpression (string name)
1856 for (Block b = this; b != null; b = b.Parent) {
1857 if (b.constants != null) {
1858 if (b.constants.TryGetValue (name, out ret))
1866 // It should be used by expressions which require to
1867 // register a statement during resolve process.
1869 public void AddScopeStatement (Statement s)
1871 if (scope_initializers == null)
1872 scope_initializers = new List<Statement> ();
1875 // Simple recursive helper, when resolve scope initializer another
1876 // new scope initializer can be added, this ensures it's initialized
1877 // before existing one. For now this can happen with expression trees
1878 // in base ctor initializer only
1880 if (resolving_init_idx.HasValue) {
1881 scope_initializers.Insert (resolving_init_idx.Value, s);
1882 ++resolving_init_idx;
1884 scope_initializers.Add (s);
1888 public void AddStatement (Statement s)
1891 flags |= Flags.BlockUsed;
1895 get { return (flags & Flags.BlockUsed) != 0; }
1900 flags |= Flags.BlockUsed;
1903 public bool HasRet {
1904 get { return (flags & Flags.HasRet) != 0; }
1907 public int AssignableSlots {
1910 // if ((flags & Flags.VariablesInitialized) == 0)
1911 // throw new Exception ("Variables have not been initialized yet");
1912 return assignable_slots;
1916 public IList<ToplevelBlock> AnonymousChildren {
1917 get { return anonymous_children; }
1920 public void AddAnonymousChild (ToplevelBlock b)
1922 if (anonymous_children == null)
1923 anonymous_children = new List<ToplevelBlock> ();
1925 anonymous_children.Add (b);
1928 void DoResolveConstants (BlockContext ec)
1930 if (constants == null)
1933 if (variables == null)
1934 throw new InternalErrorException ("cannot happen");
1936 foreach (var de in variables) {
1937 string name = de.Key;
1938 LocalInfo vi = de.Value;
1939 TypeSpec variable_type = vi.VariableType;
1941 if (variable_type == null) {
1942 if (vi.Type is VarExpr)
1943 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1949 if (!constants.TryGetValue (name, out cv))
1952 // Don't let 'const int Foo = Foo;' succeed.
1953 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1954 // which in turn causes the 'must be constant' error to be triggered.
1955 constants.Remove (name);
1957 if (!variable_type.IsConstantCompatible) {
1958 Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1962 ec.CurrentBlock = this;
1964 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1965 using (ec.With (ResolveContext.Options.DoFlowAnalysis, false)) {
1966 e = cv.Resolve (ec);
1972 Constant ce = e as Constant;
1974 e.Error_ExpressionMustBeConstant (ec, vi.Location, name);
1978 e = ce.ConvertImplicitly (ec, variable_type);
1980 if (TypeManager.IsReferenceType (variable_type))
1981 ce.Error_ConstantCanBeInitializedWithNullOnly (ec, variable_type, vi.Location, vi.Name);
1983 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1987 constants.Add (name, e);
1988 vi.IsConstant = true;
1992 protected void ResolveMeta (BlockContext ec, int offset)
1994 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
1996 // If some parent block was unsafe, we remain unsafe even if this block
1997 // isn't explicitly marked as such.
1998 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
1999 flags |= Flags.VariablesInitialized;
2001 if (variables != null) {
2002 foreach (LocalInfo li in variables.Values) {
2003 if (!li.Resolve (ec))
2005 li.VariableInfo = new VariableInfo (li, offset);
2006 offset += li.VariableInfo.Length;
2009 assignable_slots = offset;
2011 DoResolveConstants (ec);
2013 if (children == null)
2015 foreach (Block b in children)
2016 b.ResolveMeta (ec, offset);
2021 // Emits the local variable declarations for a block
2023 public virtual void EmitMeta (EmitContext ec)
2025 if (variables != null){
2026 foreach (LocalInfo vi in variables.Values)
2027 vi.ResolveVariable (ec);
2030 if (temporary_variables != null) {
2031 for (int i = 0; i < temporary_variables.Count; i++)
2032 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2035 if (children != null) {
2036 for (int i = 0; i < children.Count; i++)
2037 ((Block)children[i]).EmitMeta(ec);
2041 void UsageWarning (BlockContext ec)
2043 if (variables == null || ec.Report.WarningLevel < 3)
2046 foreach (var de in variables) {
2047 LocalInfo vi = de.Value;
2050 string name = de.Key;
2052 // vi.VariableInfo can be null for 'catch' variables
2053 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2054 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2056 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2061 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
2065 // Some statements are wrapped by a Block. Since
2066 // others' internal could be changed, here I treat
2067 // them as possibly wrapped by Block equally.
2068 Block b = s as Block;
2069 if (b != null && b.statements.Count == 1)
2070 s = (Statement) b.statements [0];
2073 body = ((Lock) s).Statement;
2075 body = ((For) s).Statement;
2076 else if (s is Foreach)
2077 body = ((Foreach) s).Statement;
2078 else if (s is While)
2079 body = ((While) s).Statement;
2080 else if (s is Fixed)
2081 body = ((Fixed) s).Statement;
2082 else if (s is Using)
2083 body = ((Using) s).EmbeddedStatement;
2084 else if (s is UsingTemporary)
2085 body = ((UsingTemporary) s).Statement;
2089 if (body == null || body is EmptyStatement)
2090 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2093 public override bool Resolve (BlockContext ec)
2095 Block prev_block = ec.CurrentBlock;
2098 int errors = ec.Report.Errors;
2100 ec.CurrentBlock = this;
2101 ec.StartFlowBranching (this);
2103 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2106 // Compiler generated scope statements
2108 if (scope_initializers != null) {
2109 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2110 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2113 resolving_init_idx = null;
2117 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2118 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2119 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2120 // responsible for handling the situation.
2122 int statement_count = statements.Count;
2123 for (int ix = 0; ix < statement_count; ix++){
2124 Statement s = statements [ix];
2125 // Check possible empty statement (CS0642)
2126 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2127 statements [ix + 1] is ExplicitBlock)
2128 CheckPossibleMistakenEmptyStatement (ec, s);
2131 // Warn if we detect unreachable code.
2134 if (s is EmptyStatement)
2137 if (!unreachable_shown && !(s is LabeledStatement)) {
2138 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2139 unreachable_shown = true;
2142 Block c_block = s as Block;
2143 if (c_block != null)
2144 c_block.unreachable = c_block.unreachable_shown = true;
2148 // Note that we're not using ResolveUnreachable() for unreachable
2149 // statements here. ResolveUnreachable() creates a temporary
2150 // flow branching and kills it afterwards. This leads to problems
2151 // if you have two unreachable statements where the first one
2152 // assigns a variable and the second one tries to access it.
2155 if (!s.Resolve (ec)) {
2157 if (ec.IsInProbingMode)
2160 statements [ix] = new EmptyStatement (s.loc);
2164 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2165 statements [ix] = new EmptyStatement (s.loc);
2167 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2168 if (unreachable && s is LabeledStatement)
2169 throw new InternalErrorException ("should not happen");
2172 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2173 ec.CurrentBranching, statement_count);
2175 while (ec.CurrentBranching is FlowBranchingLabeled)
2176 ec.EndFlowBranching ();
2178 bool flow_unreachable = ec.EndFlowBranching ();
2180 ec.CurrentBlock = prev_block;
2182 if (flow_unreachable)
2183 flags |= Flags.HasRet;
2185 // If we're a non-static `struct' constructor which doesn't have an
2186 // initializer, then we must initialize all of the struct's fields.
2187 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2190 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2191 foreach (LabeledStatement label in labels.Values)
2192 if (!label.HasBeenReferenced)
2193 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2196 if (ok && errors == ec.Report.Errors)
2202 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2204 unreachable_shown = true;
2208 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2210 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2211 bool ok = Resolve (ec);
2212 ec.KillFlowBranching ();
2217 protected override void DoEmit (EmitContext ec)
2219 for (int ix = 0; ix < statements.Count; ix++){
2220 statements [ix].Emit (ec);
2224 public override void Emit (EmitContext ec)
2226 if (scope_initializers != null)
2227 EmitScopeInitializers (ec);
2229 ec.Mark (StartLocation);
2232 if (SymbolWriter.HasSymbolWriter)
2233 EmitSymbolInfo (ec);
2236 protected void EmitScopeInitializers (EmitContext ec)
2238 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2240 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2241 foreach (Statement s in scope_initializers)
2245 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2248 protected virtual void EmitSymbolInfo (EmitContext ec)
2250 if (variables != null) {
2251 foreach (LocalInfo vi in variables.Values) {
2252 vi.EmitSymbolInfo (ec);
2257 public override string ToString ()
2259 return String.Format ("{0} ({1}:{2})", GetType (), this_id, StartLocation);
2262 protected override void CloneTo (CloneContext clonectx, Statement t)
2264 Block target = (Block) t;
2266 clonectx.AddBlockMap (this, target);
2268 target.Toplevel = (ToplevelBlock) (Toplevel == this ? target : clonectx.LookupBlock (Toplevel));
2269 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2272 target.Parent = clonectx.RemapBlockCopy (Parent);
2274 if (variables != null){
2275 target.variables = new Dictionary<string, LocalInfo> ();
2277 foreach (var de in variables){
2278 LocalInfo newlocal = de.Value.Clone (clonectx);
2279 target.variables [de.Key] = newlocal;
2280 clonectx.AddVariableMap (de.Value, newlocal);
2284 target.statements = new List<Statement> (statements.Count);
2285 foreach (Statement s in statements)
2286 target.statements.Add (s.Clone (clonectx));
2288 if (target.children != null){
2289 target.children = new List<Block> (children.Count);
2290 foreach (Block b in children){
2291 target.children.Add (clonectx.LookupBlock (b));
2296 // TODO: labels, switch_block, constants (?), anonymous_children
2301 public class ExplicitBlock : Block
2303 Dictionary<string, IKnownVariable> known_variables;
2304 protected AnonymousMethodStorey am_storey;
2306 public ExplicitBlock (Block parent, Location start, Location end)
2307 : this (parent, (Flags) 0, start, end)
2311 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2312 : base (parent, flags, start, end)
2314 this.Explicit = this;
2318 // Marks a variable with name @name as being used in this or a child block.
2319 // If a variable name has been used in a child block, it's illegal to
2320 // declare a variable with the same name in the current block.
2322 internal void AddKnownVariable (string name, IKnownVariable info)
2324 if (known_variables == null)
2325 known_variables = new Dictionary<string, IKnownVariable> ();
2327 known_variables [name] = info;
2330 Parent.Explicit.AddKnownVariable (name, info);
2333 public AnonymousMethodStorey AnonymousMethodStorey {
2334 get { return am_storey; }
2338 // Creates anonymous method storey in current block
2340 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2343 // When referencing a variable in iterator storey from children anonymous method
2345 if (Toplevel.am_storey is IteratorStorey) {
2346 return Toplevel.am_storey;
2350 // An iterator has only 1 storey block
2352 if (ec.CurrentIterator != null)
2353 return ec.CurrentIterator.Storey;
2356 // Switch block does not follow sequential flow and we cannot emit
2357 // storey initialization inside the block because it can be jumped over
2358 // for all non-first cases. Instead we push it to the parent block to be
2359 // always initialized
2361 if (switch_block != null)
2362 return switch_block.CreateAnonymousMethodStorey (ec);
2364 if (am_storey == null) {
2365 MemberBase mc = ec.MemberContext as MemberBase;
2366 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2369 // Creates anonymous method storey for this block
2371 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, gm, "AnonStorey");
2377 public override void Emit (EmitContext ec)
2379 if (am_storey != null)
2380 am_storey.EmitStoreyInstantiation (ec);
2382 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2383 if (emit_debug_info)
2388 if (emit_debug_info)
2392 public override void EmitMeta (EmitContext ec)
2395 // Creates anonymous method storey
2397 if (am_storey != null) {
2398 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2400 // Creates parent storey reference when hoisted this is accessible
2402 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2403 ExplicitBlock parent = Toplevel.Parent.Explicit;
2406 // Hoisted this exists in top-level parent storey only
2408 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2409 parent = parent.Parent.Explicit;
2411 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2414 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2416 // TODO MemberCache: Review
2417 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2420 am_storey.CreateType ();
2421 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2422 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2424 am_storey.DefineType ();
2425 am_storey.ResolveTypeParameters ();
2426 am_storey.Define ();
2427 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2429 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2430 if (ref_blocks != null) {
2431 foreach (ExplicitBlock ref_block in ref_blocks) {
2432 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2433 if (b.am_storey != null) {
2434 b.am_storey.AddParentStoreyReference (ec, am_storey);
2436 // Stop propagation inside same top block
2437 if (b.Toplevel == Toplevel)
2442 b.HasCapturedVariable = true;
2451 public IKnownVariable GetKnownVariable (string name)
2453 if (known_variables == null)
2457 known_variables.TryGetValue (name, out kw);
2461 public bool HasCapturedThis
2463 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2464 get { return (flags & Flags.HasCapturedThis) != 0; }
2467 public bool HasCapturedVariable
2469 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2470 get { return (flags & Flags.HasCapturedVariable) != 0; }
2473 protected override void CloneTo (CloneContext clonectx, Statement t)
2475 ExplicitBlock target = (ExplicitBlock) t;
2476 target.known_variables = null;
2477 base.CloneTo (clonectx, t);
2481 public class ToplevelParameterInfo : IKnownVariable {
2482 public readonly ToplevelBlock Block;
2483 public readonly int Index;
2484 public VariableInfo VariableInfo;
2486 Block IKnownVariable.Block {
2487 get { return Block; }
2489 public Parameter Parameter {
2490 get { return Block.Parameters [Index]; }
2493 public TypeSpec ParameterType {
2494 get { return Block.Parameters.Types [Index]; }
2497 public Location Location {
2498 get { return Parameter.Location; }
2501 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2509 // A toplevel block contains extra information, the split is done
2510 // only to separate information that would otherwise bloat the more
2511 // lightweight Block.
2513 // In particular, this was introduced when the support for Anonymous
2514 // Methods was implemented.
2516 public class ToplevelBlock : ExplicitBlock
2519 // Block is converted to an expression
2521 sealed class BlockScopeExpression : Expression
2524 readonly ToplevelBlock block;
2526 public BlockScopeExpression (Expression child, ToplevelBlock block)
2532 public override Expression CreateExpressionTree (ResolveContext ec)
2534 throw new NotSupportedException ();
2537 protected override Expression DoResolve (ResolveContext ec)
2542 child = child.Resolve (ec);
2546 eclass = child.eclass;
2551 public override void Emit (EmitContext ec)
2553 block.EmitMeta (ec);
2554 block.EmitScopeInitializers (ec);
2559 protected ParametersCompiled parameters;
2560 protected ToplevelParameterInfo[] parameter_info;
2561 LocalInfo this_variable;
2564 CompilerContext compiler;
2566 public HoistedVariable HoistedThisVariable;
2568 public bool Resolved {
2575 // The parameters for the block.
2577 public ParametersCompiled Parameters {
2578 get { return parameters; }
2581 public Report Report {
2582 get { return compiler.Report; }
2585 public ToplevelBlock Container {
2586 get { return Parent == null ? null : Parent.Toplevel; }
2589 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2590 this (ctx, parent, (Flags) 0, parameters, start)
2594 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2595 this (ctx, null, (Flags) 0, parameters, start)
2599 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2600 this (ctx, null, flags, parameters, start)
2604 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2605 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2606 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2607 base (null, flags, start, Location.Null)
2609 this.compiler = ctx;
2610 this.Toplevel = this;
2612 this.parameters = parameters;
2613 this.Parent = parent;
2615 parent.AddAnonymousChild (this);
2617 if (!this.parameters.IsEmpty)
2618 ProcessParameters ();
2621 public ToplevelBlock (CompilerContext ctx, Location loc)
2622 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2626 protected override void CloneTo (CloneContext clonectx, Statement t)
2628 base.CloneTo (clonectx, t);
2630 if (parameters.Count != 0) {
2631 ToplevelBlock target = (ToplevelBlock) t;
2633 target.parameter_info = new ToplevelParameterInfo[parameters.Count];
2634 for (int i = 0; i < parameters.Count; ++i)
2635 target.parameter_info[i] = new ToplevelParameterInfo (target, i);
2639 public bool CheckError158 (string name, Location loc)
2641 if (AnonymousChildren != null) {
2642 foreach (ToplevelBlock child in AnonymousChildren) {
2643 if (!child.CheckError158 (name, loc))
2648 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2649 if (!c.DoCheckError158 (name, loc))
2656 void ProcessParameters ()
2658 int n = parameters.Count;
2659 parameter_info = new ToplevelParameterInfo [n];
2660 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2661 for (int i = 0; i < n; ++i) {
2662 parameter_info [i] = new ToplevelParameterInfo (this, i);
2664 var p = parameters.FixedParameters [i];
2668 string name = p.Name;
2669 if (CheckParentConflictName (top_parent, name, loc))
2670 AddKnownVariable (name, parameter_info [i]);
2673 // mark this block as "used" so that we create local declarations in a sub-block
2674 // FIXME: This appears to uncover a lot of bugs
2678 bool DoCheckError158 (string name, Location loc)
2680 LabeledStatement s = LookupLabel (name);
2682 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2683 Error_158 (name, loc);
2690 public override Expression CreateExpressionTree (ResolveContext ec)
2692 if (statements.Count == 1) {
2693 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2694 if (scope_initializers != null)
2695 expr = new BlockScopeExpression (expr, this);
2700 return base.CreateExpressionTree (ec);
2704 // Reformats this block to be top-level iterator block
2706 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2710 // Creates block with original statements
2711 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2713 source.statements = new List<Statement> (1);
2714 source.AddStatement (new Return (iterator, iterator.Location));
2715 source.IsIterator = false;
2717 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2718 source.am_storey = iterator_storey;
2719 return iterator_storey;
2723 // Creates an arguments set from all parameters, useful for method proxy calls
2725 public Arguments GetAllParametersArguments ()
2727 int count = parameters.Count;
2728 Arguments args = new Arguments (count);
2729 for (int i = 0; i < count; ++i) {
2730 var arg_expr = new ParameterReference (parameter_info[i], parameter_info[i].Location);
2731 args.Add (new Argument (arg_expr));
2738 // Returns a parameter reference expression for the given name,
2739 // or null if there is no such parameter
2741 public Expression GetParameterReference (string name, Location loc)
2743 for (ToplevelBlock t = this; t != null; t = t.Container) {
2744 if (t.parameters.IsEmpty)
2747 Expression expr = t.GetParameterReferenceExpression (name, loc);
2755 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2757 int idx = parameters.GetParameterIndexByName (name);
2759 null : new ParameterReference (parameter_info [idx], loc);
2762 public ToplevelBlock CheckParameterNameConflict (string name)
2764 for (ToplevelBlock t = this; t != null; t = t.Container) {
2765 if (t.HasParameterWithName (name))
2772 protected virtual bool HasParameterWithName (string name)
2774 return parameters.GetParameterIndexByName (name) >= 0;
2778 // Returns the "this" instance variable of this block.
2779 // See AddThisVariable() for more information.
2781 public LocalInfo ThisVariable {
2782 get { return this_variable; }
2786 // This is used by non-static `struct' constructors which do not have an
2787 // initializer - in this case, the constructor must initialize all of the
2788 // struct's fields. To do this, we add a "this" variable and use the flow
2789 // analysis code to ensure that it's been fully initialized before control
2790 // leaves the constructor.
2792 public LocalInfo AddThisVariable (TypeContainer ds, Location l)
2794 if (this_variable == null) {
2795 this_variable = new LocalInfo (ds, this, l);
2796 this_variable.Used = true;
2797 this_variable.IsThis = true;
2799 Variables.Add ("this", this_variable);
2802 return this_variable;
2805 public bool IsIterator {
2806 get { return (flags & Flags.IsIterator) != 0; }
2807 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2811 // Block has been converted to expression tree
2813 public bool IsExpressionTree {
2814 get { return (flags & Flags.IsExpressionTree) != 0; }
2817 public bool IsThisAssigned (BlockContext ec)
2819 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2822 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2829 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2830 flags |= Flags.IsExpressionTree;
2833 if (!ResolveMeta (rc, ip))
2836 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2837 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2842 unreachable = top_level.End ();
2844 } catch (Exception e) {
2845 if (e is CompletionResult || rc.Report.IsDisabled)
2848 if (rc.CurrentBlock != null) {
2849 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2851 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2854 if (Report.DebugFlags > 0)
2858 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2859 if (rc.CurrentAnonymousMethod == null) {
2860 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2862 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2863 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2864 rc.CurrentAnonymousMethod.GetSignatureForError ());
2872 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2874 int errors = ec.Report.Errors;
2875 int orig_count = parameters.Count;
2880 // Assert: orig_count != parameter.Count => orig_count == 0
2881 if (orig_count != 0 && orig_count != parameters.Count)
2882 throw new InternalErrorException ("parameter information mismatch");
2884 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2886 for (int i = 0; i < orig_count; ++i) {
2887 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2889 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2892 VariableInfo vi = new VariableInfo (ip, i, offset);
2893 parameter_info [i].VariableInfo = vi;
2894 offset += vi.Length;
2897 ResolveMeta (ec, offset);
2899 return ec.Report.Errors == errors;
2903 // Check whether all `out' parameters have been assigned.
2905 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2907 if (vector.IsUnreachable)
2910 int n = parameter_info == null ? 0 : parameter_info.Length;
2912 for (int i = 0; i < n; i++) {
2913 VariableInfo var = parameter_info [i].VariableInfo;
2918 if (vector.IsAssigned (var, false))
2921 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2926 public override void Emit (EmitContext ec)
2928 if (Report.Errors > 0)
2936 if (ec.HasReturnLabel)
2937 ec.ReturnLabel = ec.DefineLabel ();
2941 ec.Mark (EndLocation);
2943 if (ec.HasReturnLabel)
2944 ec.MarkLabel (ec.ReturnLabel);
2946 if (ec.return_value != null) {
2947 ec.Emit (OpCodes.Ldloc, ec.return_value);
2948 ec.Emit (OpCodes.Ret);
2951 // If `HasReturnLabel' is set, then we already emitted a
2952 // jump to the end of the method, so we must emit a `ret'
2955 // Unfortunately, System.Reflection.Emit automatically emits
2956 // a leave to the end of a finally block. This is a problem
2957 // if no code is following the try/finally block since we may
2958 // jump to a point after the end of the method.
2959 // As a workaround, we're always creating a return label in
2963 if (ec.HasReturnLabel || !unreachable) {
2964 if (ec.ReturnType != TypeManager.void_type)
2965 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2966 ec.Emit (OpCodes.Ret);
2971 } catch (Exception e){
2972 Console.WriteLine ("Exception caught by the compiler while emitting:");
2973 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2975 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2981 public override void EmitMeta (EmitContext ec)
2983 // Avoid declaring an IL variable for this_variable since it is not accessed
2984 // from the generated IL
2985 if (this_variable != null)
2986 Variables.Remove ("this");
2990 protected override void EmitSymbolInfo (EmitContext ec)
2992 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2993 if ((ae != null) && (ae.Storey != null))
2994 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2996 base.EmitSymbolInfo (ec);
3000 public class SwitchLabel {
3007 Label il_label_code;
3008 bool il_label_code_set;
3010 public static readonly object NullStringCase = new object ();
3013 // if expr == null, then it is the default case.
3015 public SwitchLabel (Expression expr, Location l)
3021 public Expression Label {
3027 public Location Location {
3031 public object Converted {
3037 public Label GetILLabel (EmitContext ec)
3040 il_label = ec.DefineLabel ();
3041 il_label_set = true;
3046 public Label GetILLabelCode (EmitContext ec)
3048 if (!il_label_code_set){
3049 il_label_code = ec.DefineLabel ();
3050 il_label_code_set = true;
3052 return il_label_code;
3056 // Resolves the expression, reduces it to a literal if possible
3057 // and then converts it to the requested type.
3059 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3061 Expression e = label.Resolve (ec);
3066 Constant c = e as Constant;
3068 ec.Report.Error (150, loc, "A constant value is expected");
3072 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3073 converted = NullStringCase;
3077 if (allow_nullable && c.GetValue () == null) {
3078 converted = NullStringCase;
3082 c = c.ImplicitConversionRequired (ec, required_type, loc);
3086 converted = c.GetValue ();
3090 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3093 if (converted == null)
3095 else if (converted == NullStringCase)
3098 label = converted.ToString ();
3100 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3101 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3104 public SwitchLabel Clone (CloneContext clonectx)
3106 return new SwitchLabel (label.Clone (clonectx), loc);
3110 public class SwitchSection {
3111 // An array of SwitchLabels.
3112 public readonly List<SwitchLabel> Labels;
3113 public readonly Block Block;
3115 public SwitchSection (List<SwitchLabel> labels, Block block)
3121 public SwitchSection Clone (CloneContext clonectx)
3123 var cloned_labels = new List<SwitchLabel> ();
3125 foreach (SwitchLabel sl in cloned_labels)
3126 cloned_labels.Add (sl.Clone (clonectx));
3128 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3132 public class Switch : Statement {
3133 public List<SwitchSection> Sections;
3134 public Expression Expr;
3137 /// Maps constants whose type type SwitchType to their SwitchLabels.
3139 public IDictionary<object, SwitchLabel> Elements;
3142 /// The governing switch type
3144 public TypeSpec SwitchType;
3149 Label default_target;
3151 Expression new_expr;
3154 SwitchSection constant_section;
3155 SwitchSection default_section;
3157 ExpressionStatement string_dictionary;
3158 FieldExpr switch_cache_field;
3159 static int unique_counter;
3162 // Nullable Types support
3164 Nullable.Unwrap unwrap;
3166 protected bool HaveUnwrap {
3167 get { return unwrap != null; }
3171 // The types allowed to be implicitly cast from
3172 // on the governing type
3174 static TypeSpec [] allowed_types;
3176 public Switch (Expression e, List<SwitchSection> sects, Location l)
3183 public bool GotDefault {
3185 return default_section != null;
3189 public Label DefaultTarget {
3191 return default_target;
3196 // Determines the governing type for a switch. The returned
3197 // expression might be the expression from the switch, or an
3198 // expression that includes any potential conversions to the
3199 // integral types or to string.
3201 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3203 TypeSpec t = expr.Type;
3205 if (t == TypeManager.byte_type ||
3206 t == TypeManager.sbyte_type ||
3207 t == TypeManager.ushort_type ||
3208 t == TypeManager.short_type ||
3209 t == TypeManager.uint32_type ||
3210 t == TypeManager.int32_type ||
3211 t == TypeManager.uint64_type ||
3212 t == TypeManager.int64_type ||
3213 t == TypeManager.char_type ||
3214 t == TypeManager.string_type ||
3215 t == TypeManager.bool_type ||
3216 TypeManager.IsEnumType (t))
3219 if (allowed_types == null){
3220 allowed_types = new TypeSpec [] {
3221 TypeManager.sbyte_type,
3222 TypeManager.byte_type,
3223 TypeManager.short_type,
3224 TypeManager.ushort_type,
3225 TypeManager.int32_type,
3226 TypeManager.uint32_type,
3227 TypeManager.int64_type,
3228 TypeManager.uint64_type,
3229 TypeManager.char_type,
3230 TypeManager.string_type
3235 // Try to find a *user* defined implicit conversion.
3237 // If there is no implicit conversion, or if there are multiple
3238 // conversions, we have to report an error
3240 Expression converted = null;
3241 foreach (TypeSpec tt in allowed_types){
3244 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3249 // Ignore over-worked ImplicitUserConversions that do
3250 // an implicit conversion in addition to the user conversion.
3252 if (!(e is UserCast))
3255 if (converted != null){
3256 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3266 // Performs the basic sanity checks on the switch statement
3267 // (looks for duplicate keys and non-constant expressions).
3269 // It also returns a hashtable with the keys that we will later
3270 // use to compute the switch tables
3272 bool CheckSwitch (ResolveContext ec)
3275 Elements = new Dictionary<object, SwitchLabel> ();
3277 foreach (SwitchSection ss in Sections){
3278 foreach (SwitchLabel sl in ss.Labels){
3279 if (sl.Label == null){
3280 if (default_section != null){
3281 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3284 default_section = ss;
3288 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3293 object key = sl.Converted;
3294 if (key == SwitchLabel.NullStringCase)
3295 has_null_case = true;
3298 Elements.Add (key, sl);
3299 } catch (ArgumentException) {
3300 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3308 void EmitObjectInteger (EmitContext ec, object k)
3311 ec.EmitInt ((int) k);
3312 else if (k is Constant) {
3313 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3316 ec.EmitInt (unchecked ((int) (uint) k));
3319 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3321 ec.EmitInt ((int) (long) k);
3322 ec.Emit (OpCodes.Conv_I8);
3325 ec.EmitLong ((long) k);
3327 else if (k is ulong)
3329 ulong ul = (ulong) k;
3332 ec.EmitInt (unchecked ((int) ul));
3333 ec.Emit (OpCodes.Conv_U8);
3337 ec.EmitLong (unchecked ((long) ul));
3341 ec.EmitInt ((int) ((char) k));
3342 else if (k is sbyte)
3343 ec.EmitInt ((int) ((sbyte) k));
3345 ec.EmitInt ((int) ((byte) k));
3346 else if (k is short)
3347 ec.EmitInt ((int) ((short) k));
3348 else if (k is ushort)
3349 ec.EmitInt ((int) ((ushort) k));
3351 ec.EmitInt (((bool) k) ? 1 : 0);
3353 throw new Exception ("Unhandled case");
3356 // structure used to hold blocks of keys while calculating table switch
3357 class KeyBlock : IComparable
3359 public KeyBlock (long _first)
3361 first = last = _first;
3365 public List<object> element_keys;
3366 // how many items are in the bucket
3367 public int Size = 1;
3370 get { return (int) (last - first + 1); }
3372 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3374 return kb_last.last - kb_first.first + 1;
3376 public int CompareTo (object obj)
3378 KeyBlock kb = (KeyBlock) obj;
3379 int nLength = Length;
3380 int nLengthOther = kb.Length;
3381 if (nLengthOther == nLength)
3382 return (int) (kb.first - first);
3383 return nLength - nLengthOther;
3388 /// This method emits code for a lookup-based switch statement (non-string)
3389 /// Basically it groups the cases into blocks that are at least half full,
3390 /// and then spits out individual lookup opcodes for each block.
3391 /// It emits the longest blocks first, and short blocks are just
3392 /// handled with direct compares.
3394 /// <param name="ec"></param>
3395 /// <param name="val"></param>
3396 /// <returns></returns>
3397 void TableSwitchEmit (EmitContext ec, Expression val)
3399 int element_count = Elements.Count;
3400 object [] element_keys = new object [element_count];
3401 Elements.Keys.CopyTo (element_keys, 0);
3402 Array.Sort (element_keys);
3404 // initialize the block list with one element per key
3405 var key_blocks = new List<KeyBlock> (element_count);
3406 foreach (object key in element_keys)
3407 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3409 KeyBlock current_kb;
3410 // iteratively merge the blocks while they are at least half full
3411 // there's probably a really cool way to do this with a tree...
3412 while (key_blocks.Count > 1)
3414 var key_blocks_new = new List<KeyBlock> ();
3415 current_kb = (KeyBlock) key_blocks [0];
3416 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3418 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3419 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3422 current_kb.last = kb.last;
3423 current_kb.Size += kb.Size;
3427 // start a new block
3428 key_blocks_new.Add (current_kb);
3432 key_blocks_new.Add (current_kb);
3433 if (key_blocks.Count == key_blocks_new.Count)
3435 key_blocks = key_blocks_new;
3438 // initialize the key lists
3439 foreach (KeyBlock kb in key_blocks)
3440 kb.element_keys = new List<object> ();
3442 // fill the key lists
3444 if (key_blocks.Count > 0) {
3445 current_kb = (KeyBlock) key_blocks [0];
3446 foreach (object key in element_keys)
3448 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3449 System.Convert.ToInt64 (key) > current_kb.last;
3451 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3452 current_kb.element_keys.Add (key);
3456 // sort the blocks so we can tackle the largest ones first
3459 // okay now we can start...
3460 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3461 Label lbl_default = default_target;
3463 Type type_keys = null;
3464 if (element_keys.Length > 0)
3465 type_keys = element_keys [0].GetType (); // used for conversions
3467 TypeSpec compare_type;
3469 if (TypeManager.IsEnumType (SwitchType))
3470 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3472 compare_type = SwitchType;
3474 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3476 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3477 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3480 foreach (object key in kb.element_keys) {
3481 SwitchLabel sl = (SwitchLabel) Elements [key];
3482 if (key is int && (int) key == 0) {
3483 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3486 EmitObjectInteger (ec, key);
3487 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3493 // TODO: if all the keys in the block are the same and there are
3494 // no gaps/defaults then just use a range-check.
3495 if (compare_type == TypeManager.int64_type ||
3496 compare_type == TypeManager.uint64_type)
3498 // TODO: optimize constant/I4 cases
3500 // check block range (could be > 2^31)
3502 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3503 ec.Emit (OpCodes.Blt, lbl_default);
3505 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3506 ec.Emit (OpCodes.Bgt, lbl_default);
3512 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3513 ec.Emit (OpCodes.Sub);
3515 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3521 int first = (int) kb.first;
3525 ec.Emit (OpCodes.Sub);
3529 ec.EmitInt (-first);
3530 ec.Emit (OpCodes.Add);
3534 // first, build the list of labels for the switch
3536 int cJumps = kb.Length;
3537 Label [] switch_labels = new Label [cJumps];
3538 for (int iJump = 0; iJump < cJumps; iJump++)
3540 object key = kb.element_keys [iKey];
3541 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3543 SwitchLabel sl = (SwitchLabel) Elements [key];
3544 switch_labels [iJump] = sl.GetILLabel (ec);
3548 switch_labels [iJump] = lbl_default;
3550 // emit the switch opcode
3551 ec.Emit (OpCodes.Switch, switch_labels);
3554 // mark the default for this block
3556 ec.MarkLabel (lbl_default);
3559 // TODO: find the default case and emit it here,
3560 // to prevent having to do the following jump.
3561 // make sure to mark other labels in the default section
3563 // the last default just goes to the end
3564 if (element_keys.Length > 0)
3565 ec.Emit (OpCodes.Br, lbl_default);
3567 // now emit the code for the sections
3568 bool found_default = false;
3570 foreach (SwitchSection ss in Sections) {
3571 foreach (SwitchLabel sl in ss.Labels) {
3572 if (sl.Converted == SwitchLabel.NullStringCase) {
3573 ec.MarkLabel (null_target);
3574 } else if (sl.Label == null) {
3575 ec.MarkLabel (lbl_default);
3576 found_default = true;
3578 ec.MarkLabel (null_target);
3580 ec.MarkLabel (sl.GetILLabel (ec));
3581 ec.MarkLabel (sl.GetILLabelCode (ec));
3586 if (!found_default) {
3587 ec.MarkLabel (lbl_default);
3588 if (!has_null_case) {
3589 ec.MarkLabel (null_target);
3593 ec.MarkLabel (lbl_end);
3596 SwitchSection FindSection (SwitchLabel label)
3598 foreach (SwitchSection ss in Sections){
3599 foreach (SwitchLabel sl in ss.Labels){
3608 public static void Reset ()
3611 allowed_types = null;
3614 public override bool Resolve (BlockContext ec)
3616 Expr = Expr.Resolve (ec);
3620 new_expr = SwitchGoverningType (ec, Expr);
3622 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3623 unwrap = Nullable.Unwrap.Create (Expr, false);
3627 new_expr = SwitchGoverningType (ec, unwrap);
3630 if (new_expr == null){
3631 ec.Report.Error (151, loc,
3632 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3633 TypeManager.CSharpName (Expr.Type));
3638 SwitchType = new_expr.Type;
3640 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3641 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3645 if (!CheckSwitch (ec))
3649 Elements.Remove (SwitchLabel.NullStringCase);
3651 Switch old_switch = ec.Switch;
3653 ec.Switch.SwitchType = SwitchType;
3655 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3656 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3658 var constant = new_expr as Constant;
3659 if (constant != null) {
3661 object key = constant.GetValue ();
3663 if (Elements.TryGetValue (key, out label))
3664 constant_section = FindSection (label);
3666 if (constant_section == null)
3667 constant_section = default_section;
3672 foreach (SwitchSection ss in Sections){
3674 ec.CurrentBranching.CreateSibling (
3675 null, FlowBranching.SiblingType.SwitchSection);
3679 if (is_constant && (ss != constant_section)) {
3680 // If we're a constant switch, we're only emitting
3681 // one single section - mark all the others as
3683 ec.CurrentBranching.CurrentUsageVector.Goto ();
3684 if (!ss.Block.ResolveUnreachable (ec, true)) {
3688 if (!ss.Block.Resolve (ec))
3693 if (default_section == null)
3694 ec.CurrentBranching.CreateSibling (
3695 null, FlowBranching.SiblingType.SwitchSection);
3697 ec.EndFlowBranching ();
3698 ec.Switch = old_switch;
3700 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3705 if (SwitchType == TypeManager.string_type && !is_constant) {
3706 // TODO: Optimize single case, and single+default case
3707 ResolveStringSwitchMap (ec);
3713 void ResolveStringSwitchMap (ResolveContext ec)
3715 FullNamedExpression string_dictionary_type;
3716 if (TypeManager.generic_ienumerable_type != null) {
3717 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3718 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3720 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3722 new TypeExpression (TypeManager.string_type, loc),
3723 new TypeExpression (TypeManager.int32_type, loc)), loc);
3725 MemberAccess system_collections_generic = new MemberAccess (
3726 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3728 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3731 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3732 Field field = new Field (ctype, string_dictionary_type,
3733 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3734 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3735 if (!field.Define ())
3737 ctype.AddField (field);
3739 var init = new List<Expression> ();
3742 string value = null;
3743 foreach (SwitchSection section in Sections) {
3744 int last_count = init.Count;
3745 foreach (SwitchLabel sl in section.Labels) {
3746 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3749 value = (string) sl.Converted;
3750 var init_args = new List<Expression> (2);
3751 init_args.Add (new StringLiteral (value, sl.Location));
3752 init_args.Add (new IntConstant (counter, loc));
3753 init.Add (new CollectionElementInitializer (init_args, loc));
3757 // Don't add empty sections
3759 if (last_count == init.Count)
3762 Elements.Add (counter, section.Labels [0]);
3766 Arguments args = new Arguments (1);
3767 args.Add (new Argument (new IntConstant (init.Count, loc)));
3768 Expression initializer = new NewInitialize (string_dictionary_type, args,
3769 new CollectionOrObjectInitializers (init, loc), loc);
3771 switch_cache_field = new FieldExpr (field, loc);
3772 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3775 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3777 Label l_initialized = ec.DefineLabel ();
3780 // Skip initialization when value is null
3782 value.EmitBranchable (ec, null_target, false);
3785 // Check if string dictionary is initialized and initialize
3787 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3788 string_dictionary.EmitStatement (ec);
3789 ec.MarkLabel (l_initialized);
3791 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3793 ResolveContext rc = new ResolveContext (ec.MemberContext);
3795 if (TypeManager.generic_ienumerable_type != null) {
3796 Arguments get_value_args = new Arguments (2);
3797 get_value_args.Add (new Argument (value));
3798 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3799 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3800 if (get_item == null)
3804 // A value was not found, go to default case
3806 get_item.EmitBranchable (ec, default_target, false);
3808 Arguments get_value_args = new Arguments (1);
3809 get_value_args.Add (new Argument (value));
3811 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3812 if (get_item == null)
3815 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3816 get_item_object.EmitAssign (ec, get_item, true, false);
3817 ec.Emit (OpCodes.Brfalse, default_target);
3819 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3820 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3822 get_item_int.EmitStatement (ec);
3823 get_item_object.Release (ec);
3826 TableSwitchEmit (ec, string_switch_variable);
3827 string_switch_variable.Release (ec);
3830 protected override void DoEmit (EmitContext ec)
3832 default_target = ec.DefineLabel ();
3833 null_target = ec.DefineLabel ();
3835 // Store variable for comparission purposes
3836 // TODO: Don't duplicate non-captured VariableReference
3837 LocalTemporary value;
3839 value = new LocalTemporary (SwitchType);
3840 unwrap.EmitCheck (ec);
3841 ec.Emit (OpCodes.Brfalse, null_target);
3844 } else if (!is_constant) {
3845 value = new LocalTemporary (SwitchType);
3852 // Setup the codegen context
3854 Label old_end = ec.LoopEnd;
3855 Switch old_switch = ec.Switch;
3857 ec.LoopEnd = ec.DefineLabel ();
3862 if (constant_section != null)
3863 constant_section.Block.Emit (ec);
3864 } else if (string_dictionary != null) {
3865 DoEmitStringSwitch (value, ec);
3867 TableSwitchEmit (ec, value);
3873 // Restore context state.
3874 ec.MarkLabel (ec.LoopEnd);
3877 // Restore the previous context
3879 ec.LoopEnd = old_end;
3880 ec.Switch = old_switch;
3883 protected override void CloneTo (CloneContext clonectx, Statement t)
3885 Switch target = (Switch) t;
3887 target.Expr = Expr.Clone (clonectx);
3888 target.Sections = new List<SwitchSection> ();
3889 foreach (SwitchSection ss in Sections){
3890 target.Sections.Add (ss.Clone (clonectx));
3895 // A place where execution can restart in an iterator
3896 public abstract class ResumableStatement : Statement
3899 protected Label resume_point;
3901 public Label PrepareForEmit (EmitContext ec)
3905 resume_point = ec.DefineLabel ();
3907 return resume_point;
3910 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3914 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3919 // Base class for statements that are implemented in terms of try...finally
3920 public abstract class ExceptionStatement : ResumableStatement
3924 List<ResumableStatement> resume_points;
3925 int first_resume_pc;
3927 protected abstract void EmitPreTryBody (EmitContext ec);
3928 protected abstract void EmitTryBody (EmitContext ec);
3929 protected abstract void EmitFinallyBody (EmitContext ec);
3931 protected sealed override void DoEmit (EmitContext ec)
3933 EmitPreTryBody (ec);
3935 if (resume_points != null) {
3936 ec.EmitInt ((int) Iterator.State.Running);
3937 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3940 ec.BeginExceptionBlock ();
3942 if (resume_points != null) {
3943 ec.MarkLabel (resume_point);
3945 // For normal control flow, we want to fall-through the Switch
3946 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3947 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3948 ec.EmitInt (first_resume_pc);
3949 ec.Emit (OpCodes.Sub);
3951 Label [] labels = new Label [resume_points.Count];
3952 for (int i = 0; i < resume_points.Count; ++i)
3953 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3954 ec.Emit (OpCodes.Switch, labels);
3959 ec.BeginFinallyBlock ();
3961 Label start_finally = ec.DefineLabel ();
3962 if (resume_points != null) {
3963 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3964 ec.Emit (OpCodes.Brfalse_S, start_finally);
3965 ec.Emit (OpCodes.Endfinally);
3968 ec.MarkLabel (start_finally);
3969 EmitFinallyBody (ec);
3971 ec.EndExceptionBlock ();
3974 public void SomeCodeFollows ()
3976 code_follows = true;
3979 public override bool Resolve (BlockContext ec)
3981 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3982 // So, ensure there's some IL code after this statement.
3983 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3984 ec.NeedReturnLabel ();
3986 iter = ec.CurrentIterator;
3990 public void AddResumePoint (ResumableStatement stmt, int pc)
3992 if (resume_points == null) {
3993 resume_points = new List<ResumableStatement> ();
3994 first_resume_pc = pc;
3997 if (pc != first_resume_pc + resume_points.Count)
3998 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4000 resume_points.Add (stmt);
4003 Label dispose_try_block;
4004 bool prepared_for_dispose, emitted_dispose;
4005 public override Label PrepareForDispose (EmitContext ec, Label end)
4007 if (!prepared_for_dispose) {
4008 prepared_for_dispose = true;
4009 dispose_try_block = ec.DefineLabel ();
4011 return dispose_try_block;
4014 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4016 if (emitted_dispose)
4019 emitted_dispose = true;
4021 Label end_of_try = ec.DefineLabel ();
4023 // Ensure that the only way we can get into this code is through a dispatcher
4024 if (have_dispatcher)
4025 ec.Emit (OpCodes.Br, end);
4027 ec.BeginExceptionBlock ();
4029 ec.MarkLabel (dispose_try_block);
4031 Label [] labels = null;
4032 for (int i = 0; i < resume_points.Count; ++i) {
4033 ResumableStatement s = (ResumableStatement) resume_points [i];
4034 Label ret = s.PrepareForDispose (ec, end_of_try);
4035 if (ret.Equals (end_of_try) && labels == null)
4037 if (labels == null) {
4038 labels = new Label [resume_points.Count];
4039 for (int j = 0; j < i; ++j)
4040 labels [j] = end_of_try;
4045 if (labels != null) {
4047 for (j = 1; j < labels.Length; ++j)
4048 if (!labels [0].Equals (labels [j]))
4050 bool emit_dispatcher = j < labels.Length;
4052 if (emit_dispatcher) {
4053 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4054 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4055 ec.EmitInt (first_resume_pc);
4056 ec.Emit (OpCodes.Sub);
4057 ec.Emit (OpCodes.Switch, labels);
4058 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4061 foreach (ResumableStatement s in resume_points)
4062 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4065 ec.MarkLabel (end_of_try);
4067 ec.BeginFinallyBlock ();
4069 EmitFinallyBody (ec);
4071 ec.EndExceptionBlock ();
4075 public class Lock : ExceptionStatement {
4077 public Statement Statement;
4078 TemporaryVariable temp;
4080 public Lock (Expression expr, Statement stmt, Location l)
4087 public override bool Resolve (BlockContext ec)
4089 expr = expr.Resolve (ec);
4093 if (!TypeManager.IsReferenceType (expr.Type)){
4094 ec.Report.Error (185, loc,
4095 "`{0}' is not a reference type as required by the lock statement",
4096 TypeManager.CSharpName (expr.Type));
4100 ec.StartFlowBranching (this);
4101 bool ok = Statement.Resolve (ec);
4102 ec.EndFlowBranching ();
4104 ok &= base.Resolve (ec);
4106 temp = new TemporaryVariable (expr.Type, loc);
4109 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4110 TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4111 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4112 monitor_type, "Enter", loc, TypeManager.object_type);
4113 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4114 monitor_type, "Exit", loc, TypeManager.object_type);
4120 protected override void EmitPreTryBody (EmitContext ec)
4122 temp.EmitAssign (ec, expr);
4124 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4127 protected override void EmitTryBody (EmitContext ec)
4129 Statement.Emit (ec);
4132 protected override void EmitFinallyBody (EmitContext ec)
4135 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4138 protected override void CloneTo (CloneContext clonectx, Statement t)
4140 Lock target = (Lock) t;
4142 target.expr = expr.Clone (clonectx);
4143 target.Statement = Statement.Clone (clonectx);
4147 public class Unchecked : Statement {
4150 public Unchecked (Block b, Location loc)
4157 public override bool Resolve (BlockContext ec)
4159 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4160 return Block.Resolve (ec);
4163 protected override void DoEmit (EmitContext ec)
4165 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4169 protected override void CloneTo (CloneContext clonectx, Statement t)
4171 Unchecked target = (Unchecked) t;
4173 target.Block = clonectx.LookupBlock (Block);
4177 public class Checked : Statement {
4180 public Checked (Block b, Location loc)
4183 b.Unchecked = false;
4187 public override bool Resolve (BlockContext ec)
4189 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4190 return Block.Resolve (ec);
4193 protected override void DoEmit (EmitContext ec)
4195 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4199 protected override void CloneTo (CloneContext clonectx, Statement t)
4201 Checked target = (Checked) t;
4203 target.Block = clonectx.LookupBlock (Block);
4207 public class Unsafe : Statement {
4210 public Unsafe (Block b, Location loc)
4213 Block.Unsafe = true;
4217 public override bool Resolve (BlockContext ec)
4219 if (ec.CurrentIterator != null)
4220 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4222 using (ec.Set (ResolveContext.Options.UnsafeScope))
4223 return Block.Resolve (ec);
4226 protected override void DoEmit (EmitContext ec)
4231 protected override void CloneTo (CloneContext clonectx, Statement t)
4233 Unsafe target = (Unsafe) t;
4235 target.Block = clonectx.LookupBlock (Block);
4242 public class Fixed : Statement {
4244 List<KeyValuePair<LocalInfo, Expression>> declarators;
4245 Statement statement;
4250 abstract class Emitter
4252 protected LocalInfo vi;
4253 protected Expression converted;
4255 protected Emitter (Expression expr, LocalInfo li)
4261 public abstract void Emit (EmitContext ec);
4262 public abstract void EmitExit (EmitContext ec);
4265 class ExpressionEmitter : Emitter {
4266 public ExpressionEmitter (Expression converted, LocalInfo li) :
4267 base (converted, li)
4271 public override void Emit (EmitContext ec) {
4273 // Store pointer in pinned location
4275 converted.Emit (ec);
4279 public override void EmitExit (EmitContext ec)
4281 ec.Emit (OpCodes.Ldc_I4_0);
4282 ec.Emit (OpCodes.Conv_U);
4287 class StringEmitter : Emitter
4289 LocalInfo pinned_string;
4291 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4294 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4295 pinned_string.Pinned = true;
4298 public StringEmitter Resolve (ResolveContext rc)
4300 pinned_string.Resolve (rc);
4302 if (TypeManager.int_get_offset_to_string_data == null) {
4303 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4304 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4310 public override void Emit (EmitContext ec)
4312 pinned_string.ResolveVariable (ec);
4314 converted.Emit (ec);
4315 pinned_string.EmitAssign (ec);
4317 // TODO: Should use Binary::Add
4318 pinned_string.Emit (ec);
4319 ec.Emit (OpCodes.Conv_I);
4321 PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4322 //pe.InstanceExpression = pinned_string;
4323 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4325 ec.Emit (OpCodes.Add);
4329 public override void EmitExit (EmitContext ec)
4331 ec.Emit (OpCodes.Ldnull);
4332 pinned_string.EmitAssign (ec);
4336 public Fixed (Expression type, List<KeyValuePair<LocalInfo, Expression>> decls, Statement stmt, Location l)
4339 declarators = decls;
4344 public Statement Statement {
4345 get { return statement; }
4348 public override bool Resolve (BlockContext ec)
4351 Expression.UnsafeError (ec, loc);
4355 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4356 if (texpr == null) {
4357 if (type is VarExpr)
4358 ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4363 expr_type = texpr.Type;
4365 data = new Emitter [declarators.Count];
4367 if (!expr_type.IsPointer){
4368 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4373 foreach (var p in declarators){
4374 LocalInfo vi = p.Key;
4375 Expression e = p.Value;
4377 vi.VariableInfo.SetAssigned (ec);
4378 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4381 // The rules for the possible declarators are pretty wise,
4382 // but the production on the grammar is more concise.
4384 // So we have to enforce these rules here.
4386 // We do not resolve before doing the case 1 test,
4387 // because the grammar is explicit in that the token &
4388 // is present, so we need to test for this particular case.
4392 ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4396 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4406 if (e.Type.IsArray){
4407 TypeSpec array_type = TypeManager.GetElementType (e.Type);
4410 // Provided that array_type is unmanaged,
4412 if (!TypeManager.VerifyUnmanaged (ec.Compiler, array_type, loc))
4416 // and T* is implicitly convertible to the
4417 // pointer type given in the fixed statement.
4419 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4421 Expression converted = Convert.ImplicitConversionRequired (
4422 ec, array_ptr, vi.VariableType, loc);
4423 if (converted == null)
4427 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4429 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4430 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc), loc),
4431 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc), loc), loc)),
4432 new NullPointer (loc),
4435 converted = converted.Resolve (ec);
4437 data [i] = new ExpressionEmitter (converted, vi);
4446 if (e.Type == TypeManager.string_type){
4447 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4452 // Case 4: fixed buffer
4453 if (e is FixedBufferPtr) {
4454 data [i++] = new ExpressionEmitter (e, vi);
4459 // Case 1: & object.
4461 Unary u = e as Unary;
4462 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4463 IVariableReference vr = u.Expr as IVariableReference;
4464 if (vr == null || !vr.IsFixed) {
4465 data [i] = new ExpressionEmitter (e, vi);
4469 if (data [i++] == null)
4470 ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4472 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4475 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4476 bool ok = statement.Resolve (ec);
4477 bool flow_unreachable = ec.EndFlowBranching ();
4478 has_ret = flow_unreachable;
4483 protected override void DoEmit (EmitContext ec)
4485 for (int i = 0; i < data.Length; i++) {
4489 statement.Emit (ec);
4495 // Clear the pinned variable
4497 for (int i = 0; i < data.Length; i++) {
4498 data [i].EmitExit (ec);
4502 protected override void CloneTo (CloneContext clonectx, Statement t)
4504 Fixed target = (Fixed) t;
4506 target.type = type.Clone (clonectx);
4507 target.declarators = new List<KeyValuePair<LocalInfo, Expression>> (declarators.Count);
4508 foreach (var p in declarators) {
4509 target.declarators.Add (new KeyValuePair<LocalInfo, Expression> (
4510 clonectx.LookupVariable (p.Key), p.Value.Clone (clonectx)));
4513 target.statement = statement.Clone (clonectx);
4517 public class Catch : Statement {
4518 public readonly string Name;
4520 public Block VarBlock;
4522 Expression type_expr;
4525 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4530 VarBlock = var_block;
4534 public TypeSpec CatchType {
4540 public bool IsGeneral {
4542 return type_expr == null;
4546 protected override void DoEmit (EmitContext ec)
4548 if (CatchType != null)
4549 ec.BeginCatchBlock (CatchType);
4551 ec.BeginCatchBlock (TypeManager.object_type);
4553 if (VarBlock != null)
4557 // TODO: Move to resolve
4558 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4559 lvr.Resolve (new ResolveContext (ec.MemberContext));
4561 // Only to make verifier happy
4562 if (TypeManager.IsGenericParameter (lvr.Type))
4563 ec.Emit (OpCodes.Unbox_Any, lvr.Type);
4566 if (lvr.IsHoisted) {
4567 LocalTemporary lt = new LocalTemporary (lvr.Type);
4571 // Variable is at the top of the stack
4572 source = EmptyExpression.Null;
4575 lvr.EmitAssign (ec, source, false, false);
4577 ec.Emit (OpCodes.Pop);
4582 public override bool Resolve (BlockContext ec)
4584 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4585 if (type_expr != null) {
4586 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4592 if (type != TypeManager.exception_type && !TypeSpec.IsBaseClass (type, TypeManager.exception_type, false)){
4593 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4599 if (!Block.Resolve (ec))
4602 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4603 // emit the "unused variable" warnings.
4604 if (VarBlock != null)
4605 return VarBlock.Resolve (ec);
4611 protected override void CloneTo (CloneContext clonectx, Statement t)
4613 Catch target = (Catch) t;
4615 if (type_expr != null)
4616 target.type_expr = type_expr.Clone (clonectx);
4617 if (VarBlock != null)
4618 target.VarBlock = clonectx.LookupBlock (VarBlock);
4619 target.Block = clonectx.LookupBlock (Block);
4623 public class TryFinally : ExceptionStatement {
4627 public TryFinally (Statement stmt, Block fini, Location l)
4634 public override bool Resolve (BlockContext ec)
4638 ec.StartFlowBranching (this);
4640 if (!stmt.Resolve (ec))
4644 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4645 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4646 if (!fini.Resolve (ec))
4650 ec.EndFlowBranching ();
4652 ok &= base.Resolve (ec);
4657 protected override void EmitPreTryBody (EmitContext ec)
4661 protected override void EmitTryBody (EmitContext ec)
4666 protected override void EmitFinallyBody (EmitContext ec)
4671 protected override void CloneTo (CloneContext clonectx, Statement t)
4673 TryFinally target = (TryFinally) t;
4675 target.stmt = (Statement) stmt.Clone (clonectx);
4677 target.fini = clonectx.LookupBlock (fini);
4681 public class TryCatch : Statement {
4683 public List<Catch> Specific;
4684 public Catch General;
4685 bool inside_try_finally, code_follows;
4687 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4690 this.Specific = catch_clauses;
4691 this.inside_try_finally = inside_try_finally;
4693 Catch c = catch_clauses [0];
4696 catch_clauses.RemoveAt (0);
4702 public override bool Resolve (BlockContext ec)
4706 ec.StartFlowBranching (this);
4708 if (!Block.Resolve (ec))
4711 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4713 foreach (Catch c in Specific){
4714 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4716 if (c.Name != null) {
4717 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4719 throw new Exception ();
4721 vi.VariableInfo = null;
4724 if (!c.Resolve (ec)) {
4729 TypeSpec resolved_type = c.CatchType;
4730 for (int ii = 0; ii < last_index; ++ii) {
4731 if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4732 ec.Report.Error (160, c.loc,
4733 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4734 TypeManager.CSharpName (prev_catches [ii]));
4739 prev_catches [last_index++] = resolved_type;
4742 if (General != null) {
4743 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4744 foreach (Catch c in Specific){
4745 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4746 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'");
4751 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4753 if (!General.Resolve (ec))
4757 ec.EndFlowBranching ();
4759 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4760 // So, ensure there's some IL code after this statement
4761 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4762 ec.NeedReturnLabel ();
4767 public void SomeCodeFollows ()
4769 code_follows = true;
4772 protected override void DoEmit (EmitContext ec)
4774 if (!inside_try_finally)
4775 ec.BeginExceptionBlock ();
4779 foreach (Catch c in Specific)
4782 if (General != null)
4785 if (!inside_try_finally)
4786 ec.EndExceptionBlock ();
4789 protected override void CloneTo (CloneContext clonectx, Statement t)
4791 TryCatch target = (TryCatch) t;
4793 target.Block = clonectx.LookupBlock (Block);
4794 if (General != null)
4795 target.General = (Catch) General.Clone (clonectx);
4796 if (Specific != null){
4797 target.Specific = new List<Catch> ();
4798 foreach (Catch c in Specific)
4799 target.Specific.Add ((Catch) c.Clone (clonectx));
4804 // FIXME: Why is it almost exact copy of Using ??
4805 public class UsingTemporary : ExceptionStatement
4807 protected TemporaryVariable local_copy;
4808 Statement statement;
4810 protected Statement dispose_call;
4812 public UsingTemporary (Expression expr, Statement stmt, Location l)
4820 public Expression Expression {
4826 public Statement Statement {
4834 protected virtual bool DoResolve (BlockContext ec)
4836 expr = expr.Resolve (ec);
4840 if (expr.Type != TypeManager.idisposable_type && !expr.Type.ImplementsInterface (TypeManager.idisposable_type)) {
4841 if (TypeManager.IsNullableType (expr.Type)) {
4842 // Will handle it a custom code
4843 } else if (expr.Type == InternalType.Dynamic) {
4844 expr = Convert.ImplicitConversionStandard (ec, expr, TypeManager.idisposable_type, loc);
4846 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4851 var expr_type = expr.Type;
4853 local_copy = new TemporaryVariable (expr_type, loc);
4854 local_copy.Resolve (ec);
4856 if (TypeManager.void_dispose_void == null) {
4857 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4858 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4861 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
4862 dispose_mg.InstanceExpression = TypeManager.IsNullableType (expr_type) ?
4863 new Cast (new TypeExpression (TypeManager.idisposable_type, loc), local_copy, loc).Resolve (ec) :
4866 dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
4868 // Add conditional call when disposing possible null variable
4869 if (!expr_type.IsStruct || TypeManager.IsNullableType (expr_type))
4870 dispose_call = new If (new Binary (Binary.Operator.Inequality, local_copy, new NullLiteral (loc), loc), dispose_call, loc);
4872 return dispose_call.Resolve (ec);
4875 public override bool Resolve (BlockContext ec)
4877 bool ok = DoResolve (ec);
4879 ec.StartFlowBranching (this);
4881 ok &= statement.Resolve (ec);
4883 ec.EndFlowBranching ();
4885 ok &= base.Resolve (ec);
4890 protected override void EmitPreTryBody (EmitContext ec)
4892 local_copy.EmitAssign (ec, expr);
4895 protected override void EmitTryBody (EmitContext ec)
4897 statement.Emit (ec);
4900 protected override void EmitFinallyBody (EmitContext ec)
4902 dispose_call.Emit (ec);
4905 protected override void CloneTo (CloneContext clonectx, Statement t)
4907 UsingTemporary target = (UsingTemporary) t;
4909 target.expr = expr.Clone (clonectx);
4910 target.statement = statement.Clone (clonectx);
4914 public class Using : ExceptionStatement {
4916 public Statement EmbeddedStatement {
4917 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4923 ExpressionStatement assign;
4925 public Using (Expression var, Expression init, Statement stmt, Location l)
4933 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
4935 ec.Report.SymbolRelatedToPreviousError (expr.Type);
4936 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4937 TypeManager.CSharpName (expr.Type));
4940 protected override void EmitPreTryBody (EmitContext ec)
4942 assign.EmitStatement (ec);
4945 protected override void EmitTryBody (EmitContext ec)
4950 protected override void EmitFinallyBody (EmitContext ec)
4952 Label skip = ec.DefineLabel ();
4954 bool emit_null_check = !TypeManager.IsValueType (var.Type);
4955 if (emit_null_check) {
4957 ec.Emit (OpCodes.Brfalse, skip);
4960 Invocation.EmitCall (ec, var, TypeManager.void_dispose_void, null, loc);
4962 if (emit_null_check)
4963 ec.MarkLabel (skip);
4966 public override bool Resolve (BlockContext ec)
4968 if (!ResolveVariable (ec))
4971 ec.StartFlowBranching (this);
4973 bool ok = stmt.Resolve (ec);
4975 ec.EndFlowBranching ();
4977 ok &= base.Resolve (ec);
4979 if (TypeManager.void_dispose_void == null) {
4980 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4981 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4987 bool ResolveVariable (BlockContext ec)
4989 assign = new SimpleAssign (var, init, loc);
4990 assign = assign.ResolveStatement (ec);
4994 if (assign.Type == TypeManager.idisposable_type || assign.Type.ImplementsInterface (TypeManager.idisposable_type)) {
4999 if (assign.Type == InternalType.Dynamic) {
5000 e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, loc);
5001 var = new TemporaryVariable (e.Type, loc);
5002 assign = new SimpleAssign (var, e, loc).ResolveStatement (ec);
5006 Error_IsNotConvertibleToIDisposable (ec, var);
5010 protected override void CloneTo (CloneContext clonectx, Statement t)
5012 Using target = (Using) t;
5014 target.var = var.Clone (clonectx);
5015 target.init = init.Clone (clonectx);
5016 target.stmt = stmt.Clone (clonectx);
5021 /// Implementation of the foreach C# statement
5023 public class Foreach : Statement {
5025 sealed class ArrayForeach : Statement
5027 class ArrayCounter : TemporaryVariable
5029 StatementExpression increment;
5031 public ArrayCounter (Location loc)
5032 : base (TypeManager.int32_type, loc)
5036 public void ResolveIncrement (BlockContext ec)
5038 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
5039 increment.Resolve (ec);
5042 public void EmitIncrement (EmitContext ec)
5044 increment.Emit (ec);
5048 readonly Foreach for_each;
5049 readonly Statement statement;
5052 TemporaryVariable[] lengths;
5053 Expression [] length_exprs;
5054 ArrayCounter[] counter;
5056 TemporaryVariable copy;
5059 public ArrayForeach (Foreach @foreach, int rank)
5061 for_each = @foreach;
5062 statement = for_each.statement;
5065 counter = new ArrayCounter [rank];
5066 length_exprs = new Expression [rank];
5069 // Only use temporary length variables when dealing with
5070 // multi-dimensional arrays
5073 lengths = new TemporaryVariable [rank];
5076 protected override void CloneTo (CloneContext clonectx, Statement target)
5078 throw new NotImplementedException ();
5081 public override bool Resolve (BlockContext ec)
5083 copy = new TemporaryVariable (for_each.expr.Type, loc);
5086 int rank = length_exprs.Length;
5087 Arguments list = new Arguments (rank);
5088 for (int i = 0; i < rank; i++) {
5089 counter [i] = new ArrayCounter (loc);
5090 counter [i].ResolveIncrement (ec);
5093 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5095 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5096 lengths [i].Resolve (ec);
5098 Arguments args = new Arguments (1);
5099 args.Add (new Argument (new IntConstant (i, loc)));
5100 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5103 list.Add (new Argument (counter [i]));
5106 access = new ElementAccess (copy, list, loc).Resolve (ec);
5110 Expression var_type = for_each.type;
5111 VarExpr ve = var_type as VarExpr;
5113 // Infer implicitly typed local variable from foreach array type
5114 var_type = new TypeExpression (access.Type, ve.Location);
5117 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5118 if (var_type == null)
5121 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5127 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5128 ec.CurrentBranching.CreateSibling ();
5130 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5131 if (for_each.variable == null)
5134 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5135 if (!statement.Resolve (ec))
5137 ec.EndFlowBranching ();
5139 // There's no direct control flow from the end of the embedded statement to the end of the loop
5140 ec.CurrentBranching.CurrentUsageVector.Goto ();
5142 ec.EndFlowBranching ();
5147 protected override void DoEmit (EmitContext ec)
5149 copy.EmitAssign (ec, for_each.expr);
5151 int rank = length_exprs.Length;
5152 Label[] test = new Label [rank];
5153 Label[] loop = new Label [rank];
5155 for (int i = 0; i < rank; i++) {
5156 test [i] = ec.DefineLabel ();
5157 loop [i] = ec.DefineLabel ();
5159 if (lengths != null)
5160 lengths [i].EmitAssign (ec, length_exprs [i]);
5163 IntConstant zero = new IntConstant (0, loc);
5164 for (int i = 0; i < rank; i++) {
5165 counter [i].EmitAssign (ec, zero);
5167 ec.Emit (OpCodes.Br, test [i]);
5168 ec.MarkLabel (loop [i]);
5171 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5173 statement.Emit (ec);
5175 ec.MarkLabel (ec.LoopBegin);
5177 for (int i = rank - 1; i >= 0; i--){
5178 counter [i].EmitIncrement (ec);
5180 ec.MarkLabel (test [i]);
5181 counter [i].Emit (ec);
5183 if (lengths != null)
5184 lengths [i].Emit (ec);
5186 length_exprs [i].Emit (ec);
5188 ec.Emit (OpCodes.Blt, loop [i]);
5191 ec.MarkLabel (ec.LoopEnd);
5195 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5197 class Body : Statement
5200 Expression variable, current, conv;
5201 Statement statement;
5204 public Body (TypeSpec type, Expression variable,
5205 Expression current, Statement statement,
5209 this.variable = variable;
5210 this.current = current;
5211 this.statement = statement;
5215 protected override void CloneTo (CloneContext clonectx, Statement target)
5217 throw new NotImplementedException ();
5220 public override bool Resolve (BlockContext ec)
5222 current = current.Resolve (ec);
5223 if (current == null)
5226 conv = Convert.ExplicitConversion (ec, current, type, loc);
5230 assign = new SimpleAssign (variable, conv, loc);
5231 if (assign.Resolve (ec) == null)
5234 if (!statement.Resolve (ec))
5240 protected override void DoEmit (EmitContext ec)
5242 assign.EmitStatement (ec);
5243 statement.Emit (ec);
5247 class Dispose : UsingTemporary
5249 LocalTemporary dispose;
5251 public Dispose (TemporaryVariable variable, LocalTemporary dispose, Expression expr, Statement statement, Location loc)
5252 : base (expr, statement, loc)
5254 base.local_copy = variable;
5255 this.dispose = dispose;
5258 protected override bool DoResolve (BlockContext ec)
5260 if (TypeManager.void_dispose_void == null) {
5261 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5262 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5265 Expression dispose_var = (Expression) dispose ?? local_copy;
5267 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5268 dispose_mg.InstanceExpression = dispose_var;
5270 dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
5272 if (!dispose_var.Type.IsStruct)
5273 dispose_call = new If (new Binary (Binary.Operator.Inequality, dispose_var, new NullLiteral (loc), loc), dispose_call, loc);
5275 return dispose_call.Resolve (ec);
5278 protected override void EmitFinallyBody (EmitContext ec)
5280 Label call_dispose = ec.DefineLabel ();
5281 if (dispose != null) {
5282 local_copy.Emit (ec, false);
5283 ec.Emit (OpCodes.Isinst, dispose.Type);
5287 base.EmitFinallyBody (ec);
5289 if (dispose != null) {
5290 ec.MarkLabel (call_dispose);
5291 dispose.Release (ec);
5296 Expression variable, expr;
5297 Statement statement;
5298 Expression var_type;
5299 ExpressionStatement init;
5300 bool ambiguous_getenumerator_name;
5302 public CollectionForeach (Expression var_type, Expression var,
5303 Expression expr, Statement stmt, Location l)
5305 this.var_type = var_type;
5306 this.variable = var;
5312 protected override void CloneTo (CloneContext clonectx, Statement target)
5314 throw new NotImplementedException ();
5317 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5319 rc.Report.SymbolRelatedToPreviousError (enumerator);
5320 rc.Report.Error (202, loc,
5321 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5322 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5325 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5328 // Option 1: Try to match by name GetEnumerator first
5330 var mexpr = Expression.MemberLookup (rc, rc.CurrentType, expr.Type, "GetEnumerator", 0, true, loc); // TODO: What if CS0229 ?
5332 var mg = mexpr as MethodGroupExpr;
5334 mg.InstanceExpression = expr;
5335 Arguments args = new Arguments (0);
5336 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5338 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5339 if (ambiguous_getenumerator_name)
5342 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5348 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5350 TypeSpec iface_candidate = null;
5351 for (TypeSpec t = expr.Type; t != null && t != TypeManager.object_type; t = t.BaseType) {
5352 var ifaces = t.Interfaces;
5353 if (ifaces != null) {
5354 foreach (var iface in ifaces) {
5355 if (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) {
5356 if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) {
5357 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5358 rc.Report.Error (1640, loc,
5359 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5360 expr.Type.GetSignatureForError (), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5365 iface_candidate = iface;
5369 if (iface == TypeManager.ienumerable_type && iface_candidate == null) {
5370 iface_candidate = iface;
5376 if (iface_candidate == null) {
5377 rc.Report.Error (1579, loc,
5378 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5379 expr.Type.GetSignatureForError (), "GetEnumerator");
5384 var method = TypeManager.GetPredefinedMethod (iface_candidate,
5385 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null), loc);
5390 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5391 mg.InstanceExpression = expr;
5395 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5397 var ms = MemberCache.FindMember (enumerator.ReturnType,
5398 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5399 BindingRestriction.InstanceOnly) as MethodSpec;
5401 if (ms == null || !ms.IsPublic) {
5402 Error_WrongEnumerator (rc, enumerator);
5406 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5409 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5411 var ps = MemberCache.FindMember (enumerator.ReturnType,
5412 MemberFilter.Property ("Current", null),
5413 BindingRestriction.InstanceOnly) as PropertySpec;
5415 if (ps == null || !ps.IsPublic) {
5416 Error_WrongEnumerator (rc, enumerator);
5423 public override bool Resolve (BlockContext ec)
5425 bool is_dynamic = expr.Type == InternalType.Dynamic;
5427 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5429 var get_enumerator_mg = ResolveGetEnumerator (ec);
5430 if (get_enumerator_mg == null) {
5434 var get_enumerator = get_enumerator_mg.BestCandidate;
5435 var enumerator = new TemporaryVariable (get_enumerator.ReturnType, loc);
5436 enumerator.Resolve (ec);
5438 // Prepare bool MoveNext ()
5439 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5440 if (move_next_mg == null) {
5444 move_next_mg.InstanceExpression = enumerator;
5446 // Prepare ~T~ Current { get; }
5447 var current_prop = ResolveCurrent (ec, get_enumerator);
5448 if (current_prop == null) {
5452 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator }.Resolve (ec);
5453 if (current_pe == null)
5456 VarExpr ve = var_type as VarExpr;
5459 // Source type is dynamic, set element type to dynamic too
5460 var_type = new TypeExpression (InternalType.Dynamic, var_type.Location);
5462 // Infer implicitly typed local variable from foreach enumerable type
5463 var_type = new TypeExpression (current_pe.Type, var_type.Location);
5467 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5468 if (var_type == null)
5471 var init = new Invocation (get_enumerator_mg, null);
5474 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5475 new Body (var_type.Type, variable, current_pe, statement, loc), loc);
5477 var enum_type = enumerator.Type;
5480 // Add Dispose method call when enumerator can be IDisposable
5482 if (!enumerator.Type.ImplementsInterface (TypeManager.idisposable_type)) {
5483 if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) {
5485 // Runtime Dispose check
5487 var tv = new LocalTemporary (TypeManager.idisposable_type);
5488 statement = new Dispose (enumerator, tv, init, statement, loc);
5491 // No Dispose call needed
5493 this.init = new SimpleAssign (enumerator, init);
5494 this.init.Resolve (ec);
5498 // Static Dispose check
5500 statement = new Dispose (enumerator, null, init, statement, loc);
5503 return statement.Resolve (ec);
5506 protected override void DoEmit (EmitContext ec)
5509 init.EmitStatement (ec);
5511 statement.Emit (ec);
5514 #region IErrorHandler Members
5516 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5518 ec.Report.SymbolRelatedToPreviousError (best);
5519 ec.Report.Warning (278, 2, loc,
5520 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5521 expr.Type.GetSignatureForError (), "enumerable",
5522 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5524 ambiguous_getenumerator_name = true;
5528 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5533 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5538 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5547 Expression variable;
5549 Statement statement;
5551 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5552 Statement stmt, Location l)
5555 this.variable = var;
5561 public Statement Statement {
5562 get { return statement; }
5565 public override bool Resolve (BlockContext ec)
5567 expr = expr.Resolve (ec);
5572 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5576 if (expr.Type == TypeManager.string_type) {
5577 statement = new ArrayForeach (this, 1);
5578 } else if (expr.Type is ArrayContainer) {
5579 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5581 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5582 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5583 expr.ExprClassName);
5587 statement = new CollectionForeach (type, variable, expr, statement, loc);
5590 return statement.Resolve (ec);
5593 protected override void DoEmit (EmitContext ec)
5595 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5596 ec.LoopBegin = ec.DefineLabel ();
5597 ec.LoopEnd = ec.DefineLabel ();
5599 statement.Emit (ec);
5601 ec.LoopBegin = old_begin;
5602 ec.LoopEnd = old_end;
5605 protected override void CloneTo (CloneContext clonectx, Statement t)
5607 Foreach target = (Foreach) t;
5609 target.type = type.Clone (clonectx);
5610 target.variable = variable.Clone (clonectx);
5611 target.expr = expr.Clone (clonectx);
5612 target.statement = statement.Clone (clonectx);