2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@seznam.cz)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
15 using System
.Reflection
;
16 using System
.Reflection
.Emit
;
17 using System
.Diagnostics
;
18 using System
.Collections
.Generic
;
20 namespace Mono
.CSharp
{
22 public abstract class Statement
{
26 /// Resolves the statement, true means that all sub-statements
29 public virtual bool Resolve (BlockContext ec
)
35 /// We already know that the statement is unreachable, but we still
36 /// need to resolve it to catch errors.
38 public virtual bool ResolveUnreachable (BlockContext ec
, bool warn
)
41 // This conflicts with csc's way of doing this, but IMHO it's
42 // the right thing to do.
44 // If something is unreachable, we still check whether it's
45 // correct. This means that you cannot use unassigned variables
46 // in unreachable code, for instance.
50 ec
.Report
.Warning (162, 2, loc
, "Unreachable code detected");
52 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Block
, loc
);
53 bool ok
= Resolve (ec
);
54 ec
.KillFlowBranching ();
60 /// Return value indicates whether all code paths emitted return.
62 protected abstract void DoEmit (EmitContext ec
);
64 public virtual void Emit (EmitContext ec
)
71 // This routine must be overrided in derived classes and make copies
72 // of all the data that might be modified if resolved
74 protected abstract void CloneTo (CloneContext clonectx
, Statement target
);
76 public Statement
Clone (CloneContext clonectx
)
78 Statement s
= (Statement
) this.MemberwiseClone ();
79 CloneTo (clonectx
, s
);
83 public virtual Expression
CreateExpressionTree (ResolveContext ec
)
85 ec
.Report
.Error (834, loc
, "A lambda expression with statement body cannot be converted to an expresion tree");
89 public Statement
PerformClone ()
91 CloneContext clonectx
= new CloneContext ();
93 return Clone (clonectx
);
96 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey
);
99 public sealed class EmptyStatement
: Statement
{
101 private EmptyStatement () {}
103 public static readonly EmptyStatement Value
= new EmptyStatement ();
105 public override bool Resolve (BlockContext ec
)
110 public override bool ResolveUnreachable (BlockContext ec
, bool warn
)
115 protected override void DoEmit (EmitContext ec
)
119 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
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 void MutateHoistedGenericType (AnonymousMethodStorey storey
)
154 expr
.MutateHoistedGenericType (storey
);
155 TrueStatement
.MutateHoistedGenericType (storey
);
156 if (FalseStatement
!= null)
157 FalseStatement
.MutateHoistedGenericType (storey
);
160 public override bool Resolve (BlockContext ec
)
164 Report
.Debug (1, "START IF BLOCK", loc
);
166 expr
= expr
.Resolve (ec
);
171 // Dead code elimination
173 if (expr
is Constant
) {
174 bool take
= !((Constant
) expr
).IsDefaultValue
;
177 if (!TrueStatement
.Resolve (ec
))
180 if ((FalseStatement
!= null) &&
181 !FalseStatement
.ResolveUnreachable (ec
, true))
183 FalseStatement
= null;
185 if (!TrueStatement
.ResolveUnreachable (ec
, true))
187 TrueStatement
= null;
189 if ((FalseStatement
!= null) &&
190 !FalseStatement
.Resolve (ec
))
198 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
200 ok
&= TrueStatement
.Resolve (ec
);
202 is_true_ret
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
204 ec
.CurrentBranching
.CreateSibling ();
206 if (FalseStatement
!= null)
207 ok
&= FalseStatement
.Resolve (ec
);
209 ec
.EndFlowBranching ();
211 Report
.Debug (1, "END IF BLOCK", loc
);
216 protected override void DoEmit (EmitContext ec
)
218 ILGenerator ig
= ec
.ig
;
219 Label false_target
= ig
.DefineLabel ();
223 // If we're a boolean constant, Resolve() already
224 // eliminated dead code for us.
226 Constant c
= expr
as Constant
;
228 c
.EmitSideEffect (ec
);
230 if (!c
.IsDefaultValue
)
231 TrueStatement
.Emit (ec
);
232 else if (FalseStatement
!= null)
233 FalseStatement
.Emit (ec
);
238 expr
.EmitBranchable (ec
, false_target
, false);
240 TrueStatement
.Emit (ec
);
242 if (FalseStatement
!= null){
243 bool branch_emitted
= false;
245 end
= ig
.DefineLabel ();
247 ig
.Emit (OpCodes
.Br
, end
);
248 branch_emitted
= true;
251 ig
.MarkLabel (false_target
);
252 FalseStatement
.Emit (ec
);
257 ig
.MarkLabel (false_target
);
261 protected override void CloneTo (CloneContext clonectx
, Statement t
)
265 target
.expr
= expr
.Clone (clonectx
);
266 target
.TrueStatement
= TrueStatement
.Clone (clonectx
);
267 if (FalseStatement
!= null)
268 target
.FalseStatement
= FalseStatement
.Clone (clonectx
);
272 public class Do
: Statement
{
273 public Expression expr
;
274 public Statement EmbeddedStatement
;
276 public Do (Statement statement
, BooleanExpression bool_expr
, Location l
)
279 EmbeddedStatement
= statement
;
283 public override bool Resolve (BlockContext ec
)
287 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
289 bool was_unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
291 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
292 if (!EmbeddedStatement
.Resolve (ec
))
294 ec
.EndFlowBranching ();
296 if (ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
&& !was_unreachable
)
297 ec
.Report
.Warning (162, 2, expr
.Location
, "Unreachable code detected");
299 expr
= expr
.Resolve (ec
);
302 else if (expr
is Constant
){
303 bool infinite
= !((Constant
) expr
).IsDefaultValue
;
305 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
308 ec
.EndFlowBranching ();
313 protected override void DoEmit (EmitContext ec
)
315 ILGenerator ig
= ec
.ig
;
316 Label loop
= ig
.DefineLabel ();
317 Label old_begin
= ec
.LoopBegin
;
318 Label old_end
= ec
.LoopEnd
;
320 ec
.LoopBegin
= ig
.DefineLabel ();
321 ec
.LoopEnd
= ig
.DefineLabel ();
324 EmbeddedStatement
.Emit (ec
);
325 ig
.MarkLabel (ec
.LoopBegin
);
328 // Dead code elimination
330 if (expr
is Constant
){
331 bool res
= !((Constant
) expr
).IsDefaultValue
;
333 expr
.EmitSideEffect (ec
);
335 ec
.ig
.Emit (OpCodes
.Br
, loop
);
337 expr
.EmitBranchable (ec
, loop
, true);
339 ig
.MarkLabel (ec
.LoopEnd
);
341 ec
.LoopBegin
= old_begin
;
342 ec
.LoopEnd
= old_end
;
345 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
347 expr
.MutateHoistedGenericType (storey
);
348 EmbeddedStatement
.MutateHoistedGenericType (storey
);
351 protected override void CloneTo (CloneContext clonectx
, Statement t
)
355 target
.EmbeddedStatement
= EmbeddedStatement
.Clone (clonectx
);
356 target
.expr
= expr
.Clone (clonectx
);
360 public class While
: Statement
{
361 public Expression expr
;
362 public Statement Statement
;
363 bool infinite
, empty
;
365 public While (BooleanExpression bool_expr
, Statement statement
, Location l
)
367 this.expr
= bool_expr
;
368 Statement
= statement
;
372 public override bool Resolve (BlockContext ec
)
376 expr
= expr
.Resolve (ec
);
381 // Inform whether we are infinite or not
383 if (expr
is Constant
){
384 bool value = !((Constant
) expr
).IsDefaultValue
;
387 if (!Statement
.ResolveUnreachable (ec
, true))
395 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
397 ec
.CurrentBranching
.CreateSibling ();
399 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
400 if (!Statement
.Resolve (ec
))
402 ec
.EndFlowBranching ();
404 // There's no direct control flow from the end of the embedded statement to the end of the loop
405 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
407 ec
.EndFlowBranching ();
412 protected override void DoEmit (EmitContext ec
)
415 expr
.EmitSideEffect (ec
);
419 ILGenerator ig
= ec
.ig
;
420 Label old_begin
= ec
.LoopBegin
;
421 Label old_end
= ec
.LoopEnd
;
423 ec
.LoopBegin
= ig
.DefineLabel ();
424 ec
.LoopEnd
= ig
.DefineLabel ();
427 // Inform whether we are infinite or not
429 if (expr
is Constant
){
430 // expr is 'true', since the 'empty' case above handles the 'false' case
431 ig
.MarkLabel (ec
.LoopBegin
);
432 expr
.EmitSideEffect (ec
);
434 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
437 // Inform that we are infinite (ie, `we return'), only
438 // if we do not `break' inside the code.
440 ig
.MarkLabel (ec
.LoopEnd
);
442 Label while_loop
= ig
.DefineLabel ();
444 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
445 ig
.MarkLabel (while_loop
);
449 ig
.MarkLabel (ec
.LoopBegin
);
452 expr
.EmitBranchable (ec
, while_loop
, true);
454 ig
.MarkLabel (ec
.LoopEnd
);
457 ec
.LoopBegin
= old_begin
;
458 ec
.LoopEnd
= old_end
;
461 public override void Emit (EmitContext ec
)
466 protected override void CloneTo (CloneContext clonectx
, Statement t
)
468 While target
= (While
) t
;
470 target
.expr
= expr
.Clone (clonectx
);
471 target
.Statement
= Statement
.Clone (clonectx
);
474 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
476 expr
.MutateHoistedGenericType (storey
);
477 Statement
.MutateHoistedGenericType (storey
);
481 public class For
: Statement
{
483 Statement InitStatement
;
485 public Statement Statement
;
486 bool infinite
, empty
;
488 public For (Statement init_statement
,
489 BooleanExpression test
,
494 InitStatement
= init_statement
;
496 Increment
= increment
;
497 Statement
= statement
;
501 public override bool Resolve (BlockContext ec
)
505 if (InitStatement
!= null){
506 if (!InitStatement
.Resolve (ec
))
511 Test
= Test
.Resolve (ec
);
514 else if (Test
is Constant
){
515 bool value = !((Constant
) Test
).IsDefaultValue
;
518 if (!Statement
.ResolveUnreachable (ec
, true))
520 if ((Increment
!= null) &&
521 !Increment
.ResolveUnreachable (ec
, false))
531 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
533 ec
.CurrentBranching
.CreateSibling ();
535 bool was_unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
537 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
538 if (!Statement
.Resolve (ec
))
540 ec
.EndFlowBranching ();
542 if (Increment
!= null){
543 if (ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
) {
544 if (!Increment
.ResolveUnreachable (ec
, !was_unreachable
))
547 if (!Increment
.Resolve (ec
))
552 // There's no direct control flow from the end of the embedded statement to the end of the loop
553 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
555 ec
.EndFlowBranching ();
560 protected override void DoEmit (EmitContext ec
)
562 if (InitStatement
!= null && InitStatement
!= EmptyStatement
.Value
)
563 InitStatement
.Emit (ec
);
566 Test
.EmitSideEffect (ec
);
570 ILGenerator ig
= ec
.ig
;
571 Label old_begin
= ec
.LoopBegin
;
572 Label old_end
= ec
.LoopEnd
;
573 Label loop
= ig
.DefineLabel ();
574 Label test
= ig
.DefineLabel ();
576 ec
.LoopBegin
= ig
.DefineLabel ();
577 ec
.LoopEnd
= ig
.DefineLabel ();
579 ig
.Emit (OpCodes
.Br
, test
);
583 ig
.MarkLabel (ec
.LoopBegin
);
584 if (Increment
!= EmptyStatement
.Value
)
589 // If test is null, there is no test, and we are just
594 // The Resolve code already catches the case for
595 // Test == Constant (false) so we know that
598 if (Test
is Constant
) {
599 Test
.EmitSideEffect (ec
);
600 ig
.Emit (OpCodes
.Br
, loop
);
602 Test
.EmitBranchable (ec
, loop
, true);
606 ig
.Emit (OpCodes
.Br
, loop
);
607 ig
.MarkLabel (ec
.LoopEnd
);
609 ec
.LoopBegin
= old_begin
;
610 ec
.LoopEnd
= old_end
;
613 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
615 if (InitStatement
!= null)
616 InitStatement
.MutateHoistedGenericType (storey
);
618 Test
.MutateHoistedGenericType (storey
);
619 if (Increment
!= null)
620 Increment
.MutateHoistedGenericType (storey
);
622 Statement
.MutateHoistedGenericType (storey
);
625 protected override void CloneTo (CloneContext clonectx
, Statement t
)
627 For target
= (For
) t
;
629 if (InitStatement
!= null)
630 target
.InitStatement
= InitStatement
.Clone (clonectx
);
632 target
.Test
= Test
.Clone (clonectx
);
633 if (Increment
!= null)
634 target
.Increment
= Increment
.Clone (clonectx
);
635 target
.Statement
= Statement
.Clone (clonectx
);
639 public class StatementExpression
: Statement
{
640 ExpressionStatement expr
;
642 public StatementExpression (ExpressionStatement expr
)
648 public override bool Resolve (BlockContext ec
)
650 expr
= expr
.ResolveStatement (ec
);
654 protected override void DoEmit (EmitContext ec
)
656 expr
.EmitStatement (ec
);
659 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
661 expr
.MutateHoistedGenericType (storey
);
664 public override string ToString ()
666 return "StatementExpression (" + expr
+ ")";
669 protected override void CloneTo (CloneContext clonectx
, Statement t
)
671 StatementExpression target
= (StatementExpression
) t
;
673 target
.expr
= (ExpressionStatement
) expr
.Clone (clonectx
);
677 // A 'return' or a 'yield break'
678 public abstract class ExitStatement
: Statement
680 protected bool unwind_protect
;
681 protected abstract bool DoResolve (BlockContext ec
);
683 public virtual void Error_FinallyClause (Report Report
)
685 Report
.Error (157, loc
, "Control cannot leave the body of a finally clause");
688 public sealed override bool Resolve (BlockContext ec
)
693 unwind_protect
= ec
.CurrentBranching
.AddReturnOrigin (ec
.CurrentBranching
.CurrentUsageVector
, this);
695 ec
.NeedReturnLabel ();
696 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
702 /// Implements the return statement
704 public class Return
: ExitStatement
{
705 protected Expression Expr
;
706 public Return (Expression expr
, Location l
)
712 protected override bool DoResolve (BlockContext ec
)
715 if (ec
.ReturnType
== TypeManager
.void_type
)
718 ec
.Report
.Error (126, loc
,
719 "An object of a type convertible to `{0}' is required for the return statement",
720 TypeManager
.CSharpName (ec
.ReturnType
));
724 if (ec
.CurrentBlock
.Toplevel
.IsIterator
) {
725 ec
.Report
.Error (1622, loc
, "Cannot return a value from iterators. Use the yield return " +
726 "statement to return a value, or yield break to end the iteration");
729 AnonymousExpression am
= ec
.CurrentAnonymousMethod
;
730 if (am
== null && ec
.ReturnType
== TypeManager
.void_type
) {
731 ec
.Report
.Error (127, loc
, "`{0}': A return keyword must not be followed by any expression when method returns void",
732 ec
.GetSignatureForError ());
735 Expr
= Expr
.Resolve (ec
);
739 if (ec
.HasSet (ResolveContext
.Options
.InferReturnType
)) {
740 ec
.ReturnTypeInference
.AddCommonTypeBound (Expr
.Type
);
744 if (Expr
.Type
!= ec
.ReturnType
) {
745 Expr
= Convert
.ImplicitConversionRequired (ec
, Expr
, ec
.ReturnType
, loc
);
749 ec
.Report
.Error (1662, loc
,
750 "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",
751 am
.ContainerType
, am
.GetSignatureForError ());
760 protected override void DoEmit (EmitContext ec
)
766 ec
.ig
.Emit (OpCodes
.Stloc
, ec
.TemporaryReturn ());
770 ec
.ig
.Emit (OpCodes
.Leave
, ec
.ReturnLabel
);
772 ec
.ig
.Emit (OpCodes
.Ret
);
775 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
778 Expr
.MutateHoistedGenericType (storey
);
781 protected override void CloneTo (CloneContext clonectx
, Statement t
)
783 Return target
= (Return
) t
;
784 // It's null for simple return;
786 target
.Expr
= Expr
.Clone (clonectx
);
790 public class Goto
: Statement
{
792 LabeledStatement label
;
795 public override bool Resolve (BlockContext ec
)
797 unwind_protect
= ec
.CurrentBranching
.AddGotoOrigin (ec
.CurrentBranching
.CurrentUsageVector
, this);
798 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
802 public Goto (string label
, Location l
)
808 public string Target
{
809 get { return target; }
812 public void SetResolvedTarget (LabeledStatement label
)
815 label
.AddReference ();
818 protected override void CloneTo (CloneContext clonectx
, Statement target
)
823 protected override void DoEmit (EmitContext ec
)
826 throw new InternalErrorException ("goto emitted before target resolved");
827 Label l
= label
.LabelTarget (ec
);
828 ec
.ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, l
);
831 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
836 public class LabeledStatement
: Statement
{
843 FlowBranching
.UsageVector vectors
;
845 public LabeledStatement (string name
, Location l
)
851 public Label
LabelTarget (EmitContext ec
)
856 label
= ec
.ig
.DefineLabel ();
866 public bool IsDefined
{
867 get { return defined; }
870 public bool HasBeenReferenced
{
871 get { return referenced; }
874 public FlowBranching
.UsageVector JumpOrigins
{
875 get { return vectors; }
878 public void AddUsageVector (FlowBranching
.UsageVector vector
)
880 vector
= vector
.Clone ();
881 vector
.Next
= vectors
;
885 protected override void CloneTo (CloneContext clonectx
, Statement target
)
890 public override bool Resolve (BlockContext ec
)
892 // this flow-branching will be terminated when the surrounding block ends
893 ec
.StartFlowBranching (this);
897 protected override void DoEmit (EmitContext ec
)
899 if (ig
!= null && ig
!= ec
.ig
)
900 throw new InternalErrorException ("cannot happen");
902 ec
.ig
.MarkLabel (label
);
905 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
909 public void AddReference ()
917 /// `goto default' statement
919 public class GotoDefault
: Statement
{
921 public GotoDefault (Location l
)
926 protected override void CloneTo (CloneContext clonectx
, Statement target
)
931 public override bool Resolve (BlockContext ec
)
933 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
935 if (ec
.Switch
== null) {
936 ec
.Report
.Error (153, loc
, "A goto case is only valid inside a switch statement");
940 if (!ec
.Switch
.GotDefault
) {
941 FlowBranchingBlock
.Error_UnknownLabel (loc
, "default", ec
.Report
);
948 protected override void DoEmit (EmitContext ec
)
950 ec
.ig
.Emit (OpCodes
.Br
, ec
.Switch
.DefaultTarget
);
953 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
959 /// `goto case' statement
961 public class GotoCase
: Statement
{
965 public GotoCase (Expression e
, Location l
)
971 public override bool Resolve (BlockContext ec
)
973 if (ec
.Switch
== null){
974 ec
.Report
.Error (153, loc
, "A goto case is only valid inside a switch statement");
978 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
980 expr
= expr
.Resolve (ec
);
984 Constant c
= expr
as Constant
;
986 ec
.Report
.Error (150, expr
.Location
, "A constant value is expected");
990 Type type
= ec
.Switch
.SwitchType
;
991 Constant res
= c
.TryReduce (ec
, type
, c
.Location
);
993 c
.Error_ValueCannotBeConverted (ec
, loc
, type
, true);
997 if (!Convert
.ImplicitStandardConversionExists (c
, type
))
998 ec
.Report
.Warning (469, 2, loc
,
999 "The `goto case' value is not implicitly convertible to type `{0}'",
1000 TypeManager
.CSharpName (type
));
1002 object val
= res
.GetValue ();
1004 val
= SwitchLabel
.NullStringCase
;
1006 if (!ec
.Switch
.Elements
.TryGetValue (val
, out sl
)) {
1007 FlowBranchingBlock
.Error_UnknownLabel (loc
, "case " +
1008 (c
.GetValue () == null ? "null" : val
.ToString ()), ec
.Report
);
1015 protected override void DoEmit (EmitContext ec
)
1017 ec
.ig
.Emit (OpCodes
.Br
, sl
.GetILLabelCode (ec
));
1020 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1022 expr
.MutateHoistedGenericType (storey
);
1025 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1027 GotoCase target
= (GotoCase
) t
;
1029 target
.expr
= expr
.Clone (clonectx
);
1033 public class Throw
: Statement
{
1036 public Throw (Expression expr
, Location l
)
1042 public override bool Resolve (BlockContext ec
)
1045 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1046 return ec
.CurrentBranching
.CheckRethrow (loc
);
1049 expr
= expr
.Resolve (ec
, ResolveFlags
.Type
| ResolveFlags
.VariableOrValue
);
1050 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1055 if (Convert
.ImplicitConversionExists (ec
, expr
, TypeManager
.exception_type
))
1056 expr
= Convert
.ImplicitConversion (ec
, expr
, TypeManager
.exception_type
, loc
);
1058 ec
.Report
.Error (155, expr
.Location
, "The type caught or thrown must be derived from System.Exception");
1063 protected override void DoEmit (EmitContext ec
)
1066 ec
.ig
.Emit (OpCodes
.Rethrow
);
1070 ec
.ig
.Emit (OpCodes
.Throw
);
1074 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1077 expr
.MutateHoistedGenericType (storey
);
1080 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1082 Throw target
= (Throw
) t
;
1085 target
.expr
= expr
.Clone (clonectx
);
1089 public class Break
: Statement
{
1091 public Break (Location l
)
1096 bool unwind_protect
;
1098 public override bool Resolve (BlockContext ec
)
1100 unwind_protect
= ec
.CurrentBranching
.AddBreakOrigin (ec
.CurrentBranching
.CurrentUsageVector
, loc
);
1101 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1105 protected override void DoEmit (EmitContext ec
)
1107 ec
.ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, ec
.LoopEnd
);
1110 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1114 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1120 public class Continue
: Statement
{
1122 public Continue (Location l
)
1127 bool unwind_protect
;
1129 public override bool Resolve (BlockContext ec
)
1131 unwind_protect
= ec
.CurrentBranching
.AddContinueOrigin (ec
.CurrentBranching
.CurrentUsageVector
, loc
);
1132 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1136 protected override void DoEmit (EmitContext ec
)
1138 ec
.ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, ec
.LoopBegin
);
1141 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1145 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1151 public interface ILocalVariable
1153 void Emit (EmitContext ec
);
1154 void EmitAssign (EmitContext ec
);
1155 void EmitAddressOf (EmitContext ec
);
1158 public interface IKnownVariable
{
1159 Block Block { get; }
1160 Location Location { get; }
1164 // The information about a user-perceived local variable
1166 public class LocalInfo
: IKnownVariable
, ILocalVariable
{
1167 public readonly FullNamedExpression Type
;
1169 public Type VariableType
;
1170 public readonly string Name
;
1171 public readonly Location Location
;
1172 public readonly Block Block
;
1174 public VariableInfo VariableInfo
;
1175 HoistedVariable hoisted_variant
;
1184 CompilerGenerated
= 64,
1188 public enum ReadOnlyContext
: byte {
1195 ReadOnlyContext ro_context
;
1196 LocalBuilder builder
;
1198 public LocalInfo (FullNamedExpression type
, string name
, Block block
, Location l
)
1206 public LocalInfo (DeclSpace ds
, Block block
, Location l
)
1208 VariableType
= ds
.IsGeneric
? ds
.CurrentType
: ds
.TypeBuilder
;
1213 public void ResolveVariable (EmitContext ec
)
1215 if (HoistedVariant
!= null)
1218 if (builder
== null) {
1219 builder
= ec
.ig
.DeclareLocal (TypeManager
.TypeToReflectionType (VariableType
), Pinned
);
1223 public void Emit (EmitContext ec
)
1225 ec
.ig
.Emit (OpCodes
.Ldloc
, builder
);
1228 public void EmitAssign (EmitContext ec
)
1230 ec
.ig
.Emit (OpCodes
.Stloc
, builder
);
1233 public void EmitAddressOf (EmitContext ec
)
1235 ec
.ig
.Emit (OpCodes
.Ldloca
, builder
);
1238 public void EmitSymbolInfo (EmitContext ec
)
1240 if (builder
!= null)
1241 ec
.DefineLocalVariable (Name
, builder
);
1245 // Hoisted local variable variant
1247 public HoistedVariable HoistedVariant
{
1249 return hoisted_variant
;
1252 hoisted_variant
= value;
1256 public bool IsThisAssigned (BlockContext ec
, Block block
)
1258 if (VariableInfo
== null)
1259 throw new Exception ();
1261 if (!ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
))
1264 return VariableInfo
.TypeInfo
.IsFullyInitialized (ec
, VariableInfo
, block
.StartLocation
);
1267 public bool IsAssigned (BlockContext ec
)
1269 if (VariableInfo
== null)
1270 throw new Exception ();
1272 return !ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
);
1275 public bool Resolve (ResolveContext ec
)
1277 if (VariableType
!= null)
1280 TypeExpr texpr
= Type
.ResolveAsContextualType (ec
, false);
1284 VariableType
= texpr
.Type
;
1286 if (TypeManager
.IsGenericParameter (VariableType
))
1289 if (VariableType
.IsAbstract
&& VariableType
.IsSealed
) {
1290 FieldBase
.Error_VariableOfStaticClass (Location
, Name
, VariableType
, ec
.Report
);
1294 if (VariableType
.IsPointer
&& !ec
.IsUnsafe
)
1295 Expression
.UnsafeError (ec
, Location
);
1300 public bool IsConstant
{
1301 get { return (flags & Flags.IsConstant) != 0; }
1302 set { flags |= Flags.IsConstant; }
1305 public bool AddressTaken
{
1306 get { return (flags & Flags.AddressTaken) != 0; }
1307 set { flags |= Flags.AddressTaken; }
1310 public bool CompilerGenerated
{
1311 get { return (flags & Flags.CompilerGenerated) != 0; }
1312 set { flags |= Flags.CompilerGenerated; }
1315 public override string ToString ()
1317 return String
.Format ("LocalInfo ({0},{1},{2},{3})",
1318 Name
, Type
, VariableInfo
, Location
);
1322 get { return (flags & Flags.Used) != 0; }
1323 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1326 public bool ReadOnly
{
1327 get { return (flags & Flags.ReadOnly) != 0; }
1330 public void SetReadOnlyContext (ReadOnlyContext context
)
1332 flags
|= Flags
.ReadOnly
;
1333 ro_context
= context
;
1336 public string GetReadOnlyContext ()
1339 throw new InternalErrorException ("Variable is not readonly");
1341 switch (ro_context
) {
1342 case ReadOnlyContext
.Fixed
:
1343 return "fixed variable";
1344 case ReadOnlyContext
.Foreach
:
1345 return "foreach iteration variable";
1346 case ReadOnlyContext
.Using
:
1347 return "using variable";
1349 throw new NotImplementedException ();
1353 // Whether the variable is pinned, if Pinned the variable has been
1354 // allocated in a pinned slot with DeclareLocal.
1356 public bool Pinned
{
1357 get { return (flags & Flags.Pinned) != 0; }
1358 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1361 public bool IsThis
{
1362 get { return (flags & Flags.IsThis) != 0; }
1363 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1366 Block IKnownVariable
.Block
{
1367 get { return Block; }
1370 Location IKnownVariable
.Location
{
1371 get { return Location; }
1374 public LocalInfo
Clone (CloneContext clonectx
)
1377 // Variables in anonymous block are not resolved yet
1379 if (VariableType
== null)
1380 return new LocalInfo ((FullNamedExpression
) Type
.Clone (clonectx
), Name
, clonectx
.LookupBlock (Block
), Location
);
1383 // Variables in method block are resolved
1385 LocalInfo li
= new LocalInfo (null, Name
, clonectx
.LookupBlock (Block
), Location
);
1386 li
.VariableType
= VariableType
;
1392 /// Block represents a C# block.
1396 /// This class is used in a number of places: either to represent
1397 /// explicit blocks that the programmer places or implicit blocks.
1399 /// Implicit blocks are used as labels or to introduce variable
1402 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1403 /// they contain extra information that is not necessary on normal blocks.
1405 public class Block
: Statement
{
1406 public Block Parent
;
1407 public Location StartLocation
;
1408 public Location EndLocation
= Location
.Null
;
1410 public ExplicitBlock Explicit
;
1411 public ToplevelBlock Toplevel
; // TODO: Use Explicit
1418 VariablesInitialized
= 4,
1422 HasCapturedVariable
= 64,
1423 HasCapturedThis
= 1 << 7,
1424 IsExpressionTree
= 1 << 8
1427 protected Flags flags
;
1429 public bool Unchecked
{
1430 get { return (flags & Flags.Unchecked) != 0; }
1431 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1434 public bool Unsafe
{
1435 get { return (flags & Flags.Unsafe) != 0; }
1436 set { flags |= Flags.Unsafe; }
1440 // The statements in this block
1442 protected List
<Statement
> statements
;
1445 // An array of Blocks. We keep track of children just
1446 // to generate the local variable declarations.
1448 // Statements and child statements are handled through the
1451 List
<Block
> children
;
1454 // Labels. (label, block) pairs.
1456 protected Dictionary
<string, LabeledStatement
> labels
;
1459 // Keeps track of (name, type) pairs
1461 Dictionary
<string, LocalInfo
> variables
;
1464 // Keeps track of constants
1465 Dictionary
<string, Expression
> constants
;
1468 // Temporary variables.
1470 List
<LocalInfo
> temporary_variables
;
1473 // If this is a switch section, the enclosing switch block.
1477 protected List
<Statement
> scope_initializers
;
1479 List
<ToplevelBlock
> anonymous_children
;
1481 protected static int id
;
1485 int assignable_slots
;
1486 bool unreachable_shown
;
1489 public Block (Block parent
)
1490 : this (parent
, (Flags
) 0, Location
.Null
, Location
.Null
)
1493 public Block (Block parent
, Flags flags
)
1494 : this (parent
, flags
, Location
.Null
, Location
.Null
)
1497 public Block (Block parent
, Location start
, Location end
)
1498 : this (parent
, (Flags
) 0, start
, end
)
1502 // Useful when TopLevel block is downgraded to normal block
1504 public Block (ToplevelBlock parent
, ToplevelBlock source
)
1505 : this (parent
, source
.flags
, source
.StartLocation
, source
.EndLocation
)
1507 statements
= source
.statements
;
1508 children
= source
.children
;
1509 labels
= source
.labels
;
1510 variables
= source
.variables
;
1511 constants
= source
.constants
;
1512 switch_block
= source
.switch_block
;
1515 public Block (Block parent
, Flags flags
, Location start
, Location end
)
1517 if (parent
!= null) {
1518 parent
.AddChild (this);
1520 // the appropriate constructors will fixup these fields
1521 Toplevel
= parent
.Toplevel
;
1522 Explicit
= parent
.Explicit
;
1525 this.Parent
= parent
;
1527 this.StartLocation
= start
;
1528 this.EndLocation
= end
;
1531 statements
= new List
<Statement
> (4);
1534 public Block
CreateSwitchBlock (Location start
)
1536 // FIXME: should this be implicit?
1537 Block new_block
= new ExplicitBlock (this, start
, start
);
1538 new_block
.switch_block
= this;
1543 get { return this_id; }
1546 public IDictionary
<string, LocalInfo
> Variables
{
1548 if (variables
== null)
1549 variables
= new Dictionary
<string, LocalInfo
> ();
1554 void AddChild (Block b
)
1556 if (children
== null)
1557 children
= new List
<Block
> (1);
1562 public void SetEndLocation (Location loc
)
1567 protected void Error_158 (string name
, Location loc
)
1569 Toplevel
.Report
.Error (158, loc
, "The label `{0}' shadows another label " +
1570 "by the same name in a contained scope", name
);
1574 /// Adds a label to the current block.
1578 /// false if the name already exists in this block. true
1582 public bool AddLabel (LabeledStatement target
)
1584 if (switch_block
!= null)
1585 return switch_block
.AddLabel (target
);
1587 string name
= target
.Name
;
1590 while (cur
!= null) {
1591 LabeledStatement s
= cur
.DoLookupLabel (name
);
1593 Toplevel
.Report
.SymbolRelatedToPreviousError (s
.loc
, s
.Name
);
1594 Toplevel
.Report
.Error (140, target
.loc
, "The label `{0}' is a duplicate", name
);
1598 if (this == Explicit
)
1604 while (cur
!= null) {
1605 if (cur
.DoLookupLabel (name
) != null) {
1606 Error_158 (name
, target
.loc
);
1610 if (children
!= null) {
1611 foreach (Block b
in children
) {
1612 LabeledStatement s
= b
.DoLookupLabel (name
);
1616 Toplevel
.Report
.SymbolRelatedToPreviousError (s
.loc
, s
.Name
);
1617 Error_158 (name
, target
.loc
);
1625 Toplevel
.CheckError158 (name
, target
.loc
);
1628 labels
= new Dictionary
<string, LabeledStatement
> ();
1630 labels
.Add (name
, target
);
1634 public LabeledStatement
LookupLabel (string name
)
1636 LabeledStatement s
= DoLookupLabel (name
);
1640 if (children
== null)
1643 foreach (Block child
in children
) {
1644 if (Explicit
!= child
.Explicit
)
1647 s
= child
.LookupLabel (name
);
1655 LabeledStatement
DoLookupLabel (string name
)
1657 if (switch_block
!= null)
1658 return switch_block
.LookupLabel (name
);
1661 if (labels
.ContainsKey (name
))
1662 return labels
[name
];
1667 public bool CheckInvariantMeaningInBlock (string name
, Expression e
, Location loc
)
1670 IKnownVariable kvi
= b
.Explicit
.GetKnownVariable (name
);
1671 while (kvi
== null) {
1672 b
= b
.Explicit
.Parent
;
1675 kvi
= b
.Explicit
.GetKnownVariable (name
);
1681 // Is kvi.Block nested inside 'b'
1682 if (b
.Explicit
!= kvi
.Block
.Explicit
) {
1684 // If a variable by the same name it defined in a nested block of this
1685 // block, we violate the invariant meaning in a block.
1688 Toplevel
.Report
.SymbolRelatedToPreviousError (kvi
.Location
, name
);
1689 Toplevel
.Report
.Error (135, loc
, "`{0}' conflicts with a declaration in a child block", name
);
1694 // It's ok if the definition is in a nested subblock of b, but not
1695 // nested inside this block -- a definition in a sibling block
1696 // should not affect us.
1702 // Block 'b' and kvi.Block are the same textual block.
1703 // However, different variables are extant.
1705 // Check if the variable is in scope in both blocks. We use
1706 // an indirect check that depends on AddVariable doing its
1707 // part in maintaining the invariant-meaning-in-block property.
1709 if (e
is VariableReference
|| (e
is Constant
&& b
.GetLocalInfo (name
) != null))
1712 if (this is ToplevelBlock
) {
1713 Toplevel
.Report
.SymbolRelatedToPreviousError (kvi
.Location
, name
);
1714 e
.Error_VariableIsUsedBeforeItIsDeclared (Toplevel
.Report
, name
);
1719 // Even though we detected the error when the name is used, we
1720 // treat it as if the variable declaration was in error.
1722 Toplevel
.Report
.SymbolRelatedToPreviousError (loc
, name
);
1723 Error_AlreadyDeclared (kvi
.Location
, name
, "parent or current");
1727 protected virtual bool CheckParentConflictName (ToplevelBlock block
, string name
, Location l
)
1729 LocalInfo vi
= GetLocalInfo (name
);
1731 block
.Report
.SymbolRelatedToPreviousError (vi
.Location
, name
);
1732 if (Explicit
== vi
.Block
.Explicit
) {
1733 Error_AlreadyDeclared (l
, name
, null);
1735 Error_AlreadyDeclared (l
, name
, this is ToplevelBlock
?
1736 "parent or current" : "parent");
1741 if (block
!= null) {
1742 Expression e
= block
.GetParameterReference (name
, Location
.Null
);
1744 ParameterReference pr
= e
as ParameterReference
;
1745 if (this is Linq
.QueryBlock
&& (pr
!= null && pr
.Parameter
is Linq
.QueryBlock
.ImplicitQueryParameter
|| e
is MemberAccess
))
1746 Error_AlreadyDeclared (loc
, name
);
1748 Error_AlreadyDeclared (loc
, name
, "parent or current");
1756 public LocalInfo
AddVariable (Expression type
, string name
, Location l
)
1758 if (!CheckParentConflictName (Toplevel
, name
, l
))
1761 if (Toplevel
.GenericMethod
!= null) {
1762 foreach (TypeParameter tp
in Toplevel
.GenericMethod
.CurrentTypeParameters
) {
1763 if (tp
.Name
== name
) {
1764 Toplevel
.Report
.SymbolRelatedToPreviousError (tp
);
1765 Error_AlreadyDeclaredTypeParameter (Toplevel
.Report
, loc
, name
, "local variable");
1771 IKnownVariable kvi
= Explicit
.GetKnownVariable (name
);
1773 Toplevel
.Report
.SymbolRelatedToPreviousError (kvi
.Location
, name
);
1774 Error_AlreadyDeclared (l
, name
, "child");
1778 LocalInfo vi
= new LocalInfo ((FullNamedExpression
) type
, name
, this, l
);
1781 if ((flags
& Flags
.VariablesInitialized
) != 0)
1782 throw new InternalErrorException ("block has already been resolved");
1787 protected virtual void AddVariable (LocalInfo li
)
1789 Variables
.Add (li
.Name
, li
);
1790 Explicit
.AddKnownVariable (li
.Name
, li
);
1793 protected virtual void Error_AlreadyDeclared (Location loc
, string var, string reason
)
1795 if (reason
== null) {
1796 Error_AlreadyDeclared (loc
, var);
1800 Toplevel
.Report
.Error (136, loc
, "A local variable named `{0}' cannot be declared " +
1801 "in this scope because it would give a different meaning " +
1802 "to `{0}', which is already used in a `{1}' scope " +
1803 "to denote something else", var, reason
);
1806 protected virtual void Error_AlreadyDeclared (Location loc
, string name
)
1808 Toplevel
.Report
.Error (128, loc
,
1809 "A local variable named `{0}' is already defined in this scope", name
);
1812 public virtual void Error_AlreadyDeclaredTypeParameter (Report r
, Location loc
, string name
, string conflict
)
1814 r
.Error (412, loc
, "The type parameter name `{0}' is the same as `{1}'",
1818 public bool AddConstant (Expression type
, string name
, Expression
value, Location l
)
1820 if (AddVariable (type
, name
, l
) == null)
1823 if (constants
== null)
1824 constants
= new Dictionary
<string, Expression
> ();
1826 constants
.Add (name
, value);
1828 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1833 static int next_temp_id
= 0;
1835 public LocalInfo
AddTemporaryVariable (TypeExpr te
, Location loc
)
1837 Report
.Debug (64, "ADD TEMPORARY", this, Toplevel
, loc
);
1839 if (temporary_variables
== null)
1840 temporary_variables
= new List
<LocalInfo
> ();
1842 int id
= ++next_temp_id
;
1843 string name
= "$s_" + id
.ToString ();
1845 LocalInfo li
= new LocalInfo (te
, name
, this, loc
);
1846 li
.CompilerGenerated
= true;
1847 temporary_variables
.Add (li
);
1851 public LocalInfo
GetLocalInfo (string name
)
1854 for (Block b
= this; b
!= null; b
= b
.Parent
) {
1855 if (b
.variables
!= null) {
1856 if (b
.variables
.TryGetValue (name
, out ret
))
1864 public Expression
GetVariableType (string name
)
1866 LocalInfo vi
= GetLocalInfo (name
);
1867 return vi
== null ? null : vi
.Type
;
1870 public Expression
GetConstantExpression (string name
)
1873 for (Block b
= this; b
!= null; b
= b
.Parent
) {
1874 if (b
.constants
!= null) {
1875 if (b
.constants
.TryGetValue (name
, out ret
))
1883 // It should be used by expressions which require to
1884 // register a statement during resolve process.
1886 public void AddScopeStatement (Statement s
)
1888 if (scope_initializers
== null)
1889 scope_initializers
= new List
<Statement
> ();
1891 scope_initializers
.Add (s
);
1894 public void AddStatement (Statement s
)
1897 flags
|= Flags
.BlockUsed
;
1901 get { return (flags & Flags.BlockUsed) != 0; }
1906 flags
|= Flags
.BlockUsed
;
1909 public bool HasRet
{
1910 get { return (flags & Flags.HasRet) != 0; }
1913 public int AssignableSlots
{
1916 // if ((flags & Flags.VariablesInitialized) == 0)
1917 // throw new Exception ("Variables have not been initialized yet");
1918 return assignable_slots
;
1922 public IList
<ToplevelBlock
> AnonymousChildren
{
1923 get { return anonymous_children; }
1926 public void AddAnonymousChild (ToplevelBlock b
)
1928 if (anonymous_children
== null)
1929 anonymous_children
= new List
<ToplevelBlock
> ();
1931 anonymous_children
.Add (b
);
1934 void DoResolveConstants (BlockContext ec
)
1936 if (constants
== null)
1939 if (variables
== null)
1940 throw new InternalErrorException ("cannot happen");
1942 foreach (var de
in variables
) {
1943 string name
= de
.Key
;
1944 LocalInfo vi
= de
.Value
;
1945 Type variable_type
= vi
.VariableType
;
1947 if (variable_type
== null) {
1948 if (vi
.Type
is VarExpr
)
1949 ec
.Report
.Error (822, vi
.Type
.Location
, "An implicitly typed local variable cannot be a constant");
1955 if (!constants
.TryGetValue (name
, out cv
))
1958 // Don't let 'const int Foo = Foo;' succeed.
1959 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1960 // which in turn causes the 'must be constant' error to be triggered.
1961 constants
.Remove (name
);
1963 if (!Const
.IsConstantTypeValid (variable_type
)) {
1964 Const
.Error_InvalidConstantType (variable_type
, loc
, ec
.Report
);
1968 ec
.CurrentBlock
= this;
1970 using (ec
.With (ResolveContext
.Options
.ConstantCheckState
, (flags
& Flags
.Unchecked
) == 0)) {
1971 e
= cv
.Resolve (ec
);
1976 Constant ce
= e
as Constant
;
1978 e
.Error_ExpressionMustBeConstant (ec
, vi
.Location
, name
);
1982 e
= ce
.ConvertImplicitly (ec
, variable_type
);
1984 if (TypeManager
.IsReferenceType (variable_type
))
1985 ce
.Error_ConstantCanBeInitializedWithNullOnly (ec
, variable_type
, vi
.Location
, vi
.Name
);
1987 ce
.Error_ValueCannotBeConverted (ec
, vi
.Location
, variable_type
, false);
1991 constants
.Add (name
, e
);
1992 vi
.IsConstant
= true;
1996 protected void ResolveMeta (BlockContext ec
, int offset
)
1998 Report
.Debug (64, "BLOCK RESOLVE META", this, Parent
);
2000 // If some parent block was unsafe, we remain unsafe even if this block
2001 // isn't explicitly marked as such.
2002 using (ec
.With (ResolveContext
.Options
.UnsafeScope
, ec
.IsUnsafe
| Unsafe
)) {
2003 flags
|= Flags
.VariablesInitialized
;
2005 if (variables
!= null) {
2006 foreach (LocalInfo li
in variables
.Values
) {
2007 if (!li
.Resolve (ec
))
2009 li
.VariableInfo
= new VariableInfo (li
, offset
);
2010 offset
+= li
.VariableInfo
.Length
;
2013 assignable_slots
= offset
;
2015 DoResolveConstants (ec
);
2017 if (children
== null)
2019 foreach (Block b
in children
)
2020 b
.ResolveMeta (ec
, offset
);
2025 // Emits the local variable declarations for a block
2027 public virtual void EmitMeta (EmitContext ec
)
2029 if (variables
!= null){
2030 foreach (LocalInfo vi
in variables
.Values
)
2031 vi
.ResolveVariable (ec
);
2034 if (temporary_variables
!= null) {
2035 for (int i
= 0; i
< temporary_variables
.Count
; i
++)
2036 ((LocalInfo
)temporary_variables
[i
]).ResolveVariable(ec
);
2039 if (children
!= null) {
2040 for (int i
= 0; i
< children
.Count
; i
++)
2041 ((Block
)children
[i
]).EmitMeta(ec
);
2045 void UsageWarning (BlockContext ec
)
2047 if (variables
== null || ec
.Report
.WarningLevel
< 3)
2050 foreach (var de
in variables
) {
2051 LocalInfo vi
= de
.Value
;
2054 string name
= de
.Key
;
2056 // vi.VariableInfo can be null for 'catch' variables
2057 if (vi
.VariableInfo
!= null && vi
.VariableInfo
.IsEverAssigned
)
2058 ec
.Report
.Warning (219, 3, vi
.Location
, "The variable `{0}' is assigned but its value is never used", name
);
2060 ec
.Report
.Warning (168, 3, vi
.Location
, "The variable `{0}' is declared but never used", name
);
2065 static void CheckPossibleMistakenEmptyStatement (BlockContext ec
, Statement s
)
2069 // Some statements are wrapped by a Block. Since
2070 // others' internal could be changed, here I treat
2071 // them as possibly wrapped by Block equally.
2072 Block b
= s
as Block
;
2073 if (b
!= null && b
.statements
.Count
== 1)
2074 s
= (Statement
) b
.statements
[0];
2077 body
= ((Lock
) s
).Statement
;
2079 body
= ((For
) s
).Statement
;
2080 else if (s
is Foreach
)
2081 body
= ((Foreach
) s
).Statement
;
2082 else if (s
is While
)
2083 body
= ((While
) s
).Statement
;
2084 else if (s
is Fixed
)
2085 body
= ((Fixed
) s
).Statement
;
2086 else if (s
is Using
)
2087 body
= ((Using
) s
).EmbeddedStatement
;
2088 else if (s
is UsingTemporary
)
2089 body
= ((UsingTemporary
) s
).Statement
;
2093 if (body
== null || body
is EmptyStatement
)
2094 ec
.Report
.Warning (642, 3, s
.loc
, "Possible mistaken empty statement");
2097 public override bool Resolve (BlockContext ec
)
2099 Block prev_block
= ec
.CurrentBlock
;
2102 int errors
= ec
.Report
.Errors
;
2104 ec
.CurrentBlock
= this;
2105 ec
.StartFlowBranching (this);
2107 Report
.Debug (4, "RESOLVE BLOCK", StartLocation
, ec
.CurrentBranching
);
2110 // Compiler generated scope statements
2112 if (scope_initializers
!= null) {
2113 foreach (Statement s
in scope_initializers
)
2118 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2119 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2120 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2121 // responsible for handling the situation.
2123 int statement_count
= statements
.Count
;
2124 for (int ix
= 0; ix
< statement_count
; ix
++){
2125 Statement s
= statements
[ix
];
2126 // Check possible empty statement (CS0642)
2127 if (ix
+ 1 < statement_count
&& ec
.Report
.WarningLevel
>= 3 &&
2128 statements
[ix
+ 1] is ExplicitBlock
)
2129 CheckPossibleMistakenEmptyStatement (ec
, s
);
2132 // Warn if we detect unreachable code.
2135 if (s
is EmptyStatement
)
2138 if (!unreachable_shown
&& !(s
is LabeledStatement
)) {
2139 ec
.Report
.Warning (162, 2, s
.loc
, "Unreachable code detected");
2140 unreachable_shown
= true;
2143 Block c_block
= s
as Block
;
2144 if (c_block
!= null)
2145 c_block
.unreachable
= c_block
.unreachable_shown
= true;
2149 // Note that we're not using ResolveUnreachable() for unreachable
2150 // statements here. ResolveUnreachable() creates a temporary
2151 // flow branching and kills it afterwards. This leads to problems
2152 // if you have two unreachable statements where the first one
2153 // assigns a variable and the second one tries to access it.
2156 if (!s
.Resolve (ec
)) {
2158 if (ec
.IsInProbingMode
)
2161 statements
[ix
] = EmptyStatement
.Value
;
2165 if (unreachable
&& !(s
is LabeledStatement
) && !(s
is Block
))
2166 statements
[ix
] = EmptyStatement
.Value
;
2168 unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
2169 if (unreachable
&& s
is LabeledStatement
)
2170 throw new InternalErrorException ("should not happen");
2173 Report
.Debug (4, "RESOLVE BLOCK DONE", StartLocation
,
2174 ec
.CurrentBranching
, statement_count
);
2176 while (ec
.CurrentBranching
is FlowBranchingLabeled
)
2177 ec
.EndFlowBranching ();
2179 bool flow_unreachable
= ec
.EndFlowBranching ();
2181 ec
.CurrentBlock
= prev_block
;
2183 if (flow_unreachable
)
2184 flags
|= Flags
.HasRet
;
2186 // If we're a non-static `struct' constructor which doesn't have an
2187 // initializer, then we must initialize all of the struct's fields.
2188 if (this == Toplevel
&& !Toplevel
.IsThisAssigned (ec
) && !flow_unreachable
)
2191 if ((labels
!= null) && (ec
.Report
.WarningLevel
>= 2)) {
2192 foreach (LabeledStatement label
in labels
.Values
)
2193 if (!label
.HasBeenReferenced
)
2194 ec
.Report
.Warning (164, 2, label
.loc
, "This label has not been referenced");
2197 if (ok
&& errors
== ec
.Report
.Errors
)
2203 public override bool ResolveUnreachable (BlockContext ec
, bool warn
)
2205 unreachable_shown
= true;
2209 ec
.Report
.Warning (162, 2, loc
, "Unreachable code detected");
2211 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Block
, loc
);
2212 bool ok
= Resolve (ec
);
2213 ec
.KillFlowBranching ();
2218 protected override void DoEmit (EmitContext ec
)
2220 for (int ix
= 0; ix
< statements
.Count
; ix
++){
2221 Statement s
= (Statement
) statements
[ix
];
2226 public override void Emit (EmitContext ec
)
2228 if (scope_initializers
!= null)
2229 EmitScopeInitializers (ec
);
2231 ec
.Mark (StartLocation
);
2234 if (SymbolWriter
.HasSymbolWriter
)
2235 EmitSymbolInfo (ec
);
2238 protected void EmitScopeInitializers (EmitContext ec
)
2240 SymbolWriter
.OpenCompilerGeneratedBlock (ec
.ig
);
2242 using (ec
.With (EmitContext
.Options
.OmitDebugInfo
, true)) {
2243 foreach (Statement s
in scope_initializers
)
2247 SymbolWriter
.CloseCompilerGeneratedBlock (ec
.ig
);
2250 protected virtual void EmitSymbolInfo (EmitContext ec
)
2252 if (variables
!= null) {
2253 foreach (LocalInfo vi
in variables
.Values
) {
2254 vi
.EmitSymbolInfo (ec
);
2259 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
2261 MutateVariables (storey
);
2263 if (scope_initializers
!= null) {
2264 foreach (Statement s
in scope_initializers
)
2265 s
.MutateHoistedGenericType (storey
);
2268 foreach (Statement s
in statements
)
2269 s
.MutateHoistedGenericType (storey
);
2272 void MutateVariables (AnonymousMethodStorey storey
)
2274 if (variables
!= null) {
2275 foreach (LocalInfo vi
in variables
.Values
) {
2276 vi
.VariableType
= storey
.MutateType (vi
.VariableType
);
2280 if (temporary_variables
!= null) {
2281 foreach (LocalInfo vi
in temporary_variables
)
2282 vi
.VariableType
= storey
.MutateType (vi
.VariableType
);
2286 public override string ToString ()
2288 return String
.Format ("{0} ({1}:{2})", GetType (),ID
, StartLocation
);
2291 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2293 Block target
= (Block
) t
;
2295 clonectx
.AddBlockMap (this, target
);
2297 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2298 target
.Explicit
= (ExplicitBlock
) clonectx
.LookupBlock (Explicit
);
2300 target
.Parent
= clonectx
.RemapBlockCopy (Parent
);
2302 if (variables
!= null){
2303 target
.variables
= new Dictionary
<string, LocalInfo
> ();
2305 foreach (var de
in variables
){
2306 LocalInfo newlocal
= de
.Value
.Clone (clonectx
);
2307 target
.variables
[de
.Key
] = newlocal
;
2308 clonectx
.AddVariableMap (de
.Value
, newlocal
);
2312 target
.statements
= new List
<Statement
> (statements
.Count
);
2313 foreach (Statement s
in statements
)
2314 target
.statements
.Add (s
.Clone (clonectx
));
2316 if (target
.children
!= null){
2317 target
.children
= new List
<Block
> (children
.Count
);
2318 foreach (Block b
in children
){
2319 target
.children
.Add (clonectx
.LookupBlock (b
));
2324 // TODO: labels, switch_block, constants (?), anonymous_children
2329 public class ExplicitBlock
: Block
2331 Dictionary
<string, IKnownVariable
> known_variables
;
2332 protected AnonymousMethodStorey am_storey
;
2334 public ExplicitBlock (Block parent
, Location start
, Location end
)
2335 : this (parent
, (Flags
) 0, start
, end
)
2339 public ExplicitBlock (Block parent
, Flags flags
, Location start
, Location end
)
2340 : base (parent
, flags
, start
, end
)
2342 this.Explicit
= this;
2346 // Marks a variable with name @name as being used in this or a child block.
2347 // If a variable name has been used in a child block, it's illegal to
2348 // declare a variable with the same name in the current block.
2350 internal void AddKnownVariable (string name
, IKnownVariable info
)
2352 if (known_variables
== null)
2353 known_variables
= new Dictionary
<string, IKnownVariable
> ();
2355 known_variables
[name
] = info
;
2358 Parent
.Explicit
.AddKnownVariable (name
, info
);
2361 public AnonymousMethodStorey AnonymousMethodStorey
{
2362 get { return am_storey; }
2366 // Creates anonymous method storey in current block
2368 public AnonymousMethodStorey
CreateAnonymousMethodStorey (ResolveContext ec
)
2371 // When referencing a variable in iterator storey from children anonymous method
2373 if (Toplevel
.am_storey
is IteratorStorey
) {
2374 return Toplevel
.am_storey
;
2378 // An iterator has only 1 storey block
2380 if (ec
.CurrentIterator
!= null)
2381 return ec
.CurrentIterator
.Storey
;
2383 if (am_storey
== null) {
2384 MemberBase mc
= ec
.MemberContext
as MemberBase
;
2385 GenericMethod gm
= mc
== null ? null : mc
.GenericMethod
;
2388 // Creates anonymous method storey for this block
2390 am_storey
= new AnonymousMethodStorey (this, ec
.CurrentTypeDefinition
, mc
, gm
, "AnonStorey");
2396 public override void Emit (EmitContext ec
)
2398 if (am_storey
!= null)
2399 am_storey
.EmitStoreyInstantiation (ec
);
2401 bool emit_debug_info
= SymbolWriter
.HasSymbolWriter
&& Parent
!= null && !(am_storey
is IteratorStorey
);
2402 if (emit_debug_info
)
2407 if (emit_debug_info
)
2411 public override void EmitMeta (EmitContext ec
)
2414 // Creates anonymous method storey
2416 if (am_storey
!= null) {
2417 if (ec
.CurrentAnonymousMethod
!= null && ec
.CurrentAnonymousMethod
.Storey
!= null) {
2419 // Creates parent storey reference when hoisted this is accessible
2421 if (am_storey
.OriginalSourceBlock
.Explicit
.HasCapturedThis
) {
2422 ExplicitBlock parent
= Toplevel
.Parent
.Explicit
;
2425 // Hoisted this exists in top-level parent storey only
2427 while (parent
.am_storey
== null || parent
.am_storey
.Parent
is AnonymousMethodStorey
)
2428 parent
= parent
.Parent
.Explicit
;
2430 am_storey
.AddParentStoreyReference (parent
.am_storey
);
2433 am_storey
.ChangeParentStorey (ec
.CurrentAnonymousMethod
.Storey
);
2436 am_storey
.DefineType ();
2437 am_storey
.ResolveType ();
2438 am_storey
.Define ();
2439 am_storey
.Parent
.PartialContainer
.AddCompilerGeneratedClass (am_storey
);
2441 var ref_blocks
= am_storey
.ReferencesFromChildrenBlock
;
2442 if (ref_blocks
!= null) {
2443 foreach (ExplicitBlock ref_block
in ref_blocks
) {
2444 for (ExplicitBlock b
= ref_block
.Explicit
; b
!= this; b
= b
.Parent
.Explicit
) {
2445 if (b
.am_storey
!= null) {
2446 b
.am_storey
.AddParentStoreyReference (am_storey
);
2448 // Stop propagation inside same top block
2449 if (b
.Toplevel
== Toplevel
)
2454 b
.HasCapturedVariable
= true;
2463 internal IKnownVariable
GetKnownVariable (string name
)
2465 if (known_variables
== null)
2469 if (!known_variables
.TryGetValue (name
, out kw
))
2475 public bool HasCapturedThis
2477 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2478 get { return (flags & Flags.HasCapturedThis) != 0; }
2481 public bool HasCapturedVariable
2483 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2484 get { return (flags & Flags.HasCapturedVariable) != 0; }
2487 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2489 ExplicitBlock target
= (ExplicitBlock
) t
;
2490 target
.known_variables
= null;
2491 base.CloneTo (clonectx
, t
);
2495 public class ToplevelParameterInfo
: IKnownVariable
{
2496 public readonly ToplevelBlock Block
;
2497 public readonly int Index
;
2498 public VariableInfo VariableInfo
;
2500 Block IKnownVariable
.Block
{
2501 get { return Block; }
2503 public Parameter Parameter
{
2504 get { return Block.Parameters [Index]; }
2507 public Type ParameterType
{
2508 get { return Block.Parameters.Types [Index]; }
2511 public Location Location
{
2512 get { return Parameter.Location; }
2515 public ToplevelParameterInfo (ToplevelBlock block
, int idx
)
2523 // A toplevel block contains extra information, the split is done
2524 // only to separate information that would otherwise bloat the more
2525 // lightweight Block.
2527 // In particular, this was introduced when the support for Anonymous
2528 // Methods was implemented.
2530 public class ToplevelBlock
: ExplicitBlock
2533 // Block is converted to an expression
2535 sealed class BlockScopeExpression
: Expression
2538 readonly ToplevelBlock block
;
2540 public BlockScopeExpression (Expression child
, ToplevelBlock block
)
2546 public override Expression
CreateExpressionTree (ResolveContext ec
)
2548 throw new NotSupportedException ();
2551 protected override Expression
DoResolve (ResolveContext ec
)
2556 child
= child
.Resolve (ec
);
2560 eclass
= child
.eclass
;
2565 public override void Emit (EmitContext ec
)
2567 block
.EmitMeta (ec
);
2568 block
.EmitScopeInitializers (ec
);
2572 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
2574 type
= storey
.MutateType (type
);
2575 child
.MutateHoistedGenericType (storey
);
2576 block
.MutateHoistedGenericType (storey
);
2580 GenericMethod generic
;
2581 protected ParametersCompiled parameters
;
2582 ToplevelParameterInfo
[] parameter_info
;
2583 LocalInfo this_variable
;
2586 CompilerContext compiler
;
2588 public HoistedVariable HoistedThisVariable
;
2590 public bool Resolved
{
2597 // The parameters for the block.
2599 public ParametersCompiled Parameters
{
2600 get { return parameters; }
2603 public Report Report
{
2604 get { return compiler.Report; }
2607 public GenericMethod GenericMethod
{
2608 get { return generic; }
2611 public ToplevelBlock Container
{
2612 get { return Parent == null ? null : Parent.Toplevel; }
2615 public ToplevelBlock (CompilerContext ctx
, Block parent
, ParametersCompiled parameters
, Location start
) :
2616 this (ctx
, parent
, (Flags
) 0, parameters
, start
)
2620 public ToplevelBlock (CompilerContext ctx
, Block parent
, ParametersCompiled parameters
, GenericMethod generic
, Location start
) :
2621 this (ctx
, parent
, parameters
, start
)
2623 this.generic
= generic
;
2626 public ToplevelBlock (CompilerContext ctx
, ParametersCompiled parameters
, Location start
) :
2627 this (ctx
, null, (Flags
) 0, parameters
, start
)
2631 ToplevelBlock (CompilerContext ctx
, Flags flags
, ParametersCompiled parameters
, Location start
) :
2632 this (ctx
, null, flags
, parameters
, start
)
2636 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2637 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2638 public ToplevelBlock (CompilerContext ctx
, Block parent
, Flags flags
, ParametersCompiled parameters
, Location start
) :
2639 base (null, flags
, start
, Location
.Null
)
2641 this.compiler
= ctx
;
2642 this.Toplevel
= this;
2644 this.parameters
= parameters
;
2645 this.Parent
= parent
;
2647 parent
.AddAnonymousChild (this);
2649 if (!this.parameters
.IsEmpty
)
2650 ProcessParameters ();
2653 public ToplevelBlock (CompilerContext ctx
, Location loc
)
2654 : this (ctx
, null, (Flags
) 0, ParametersCompiled
.EmptyReadOnlyParameters
, loc
)
2658 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2660 ToplevelBlock target
= (ToplevelBlock
) t
;
2661 base.CloneTo (clonectx
, t
);
2663 if (parameters
.Count
!= 0)
2664 target
.parameter_info
= new ToplevelParameterInfo
[parameters
.Count
];
2665 for (int i
= 0; i
< parameters
.Count
; ++i
)
2666 target
.parameter_info
[i
] = new ToplevelParameterInfo (target
, i
);
2669 public bool CheckError158 (string name
, Location loc
)
2671 if (AnonymousChildren
!= null) {
2672 foreach (ToplevelBlock child
in AnonymousChildren
) {
2673 if (!child
.CheckError158 (name
, loc
))
2678 for (ToplevelBlock c
= Container
; c
!= null; c
= c
.Container
) {
2679 if (!c
.DoCheckError158 (name
, loc
))
2686 void ProcessParameters ()
2688 int n
= parameters
.Count
;
2689 parameter_info
= new ToplevelParameterInfo
[n
];
2690 ToplevelBlock top_parent
= Parent
== null ? null : Parent
.Toplevel
;
2691 for (int i
= 0; i
< n
; ++i
) {
2692 parameter_info
[i
] = new ToplevelParameterInfo (this, i
);
2694 Parameter p
= parameters
[i
];
2698 string name
= p
.Name
;
2699 if (CheckParentConflictName (top_parent
, name
, loc
))
2700 AddKnownVariable (name
, parameter_info
[i
]);
2703 // mark this block as "used" so that we create local declarations in a sub-block
2704 // FIXME: This appears to uncover a lot of bugs
2708 bool DoCheckError158 (string name
, Location loc
)
2710 LabeledStatement s
= LookupLabel (name
);
2712 Report
.SymbolRelatedToPreviousError (s
.loc
, s
.Name
);
2713 Error_158 (name
, loc
);
2720 public override Expression
CreateExpressionTree (ResolveContext ec
)
2722 if (statements
.Count
== 1) {
2723 Expression expr
= ((Statement
) statements
[0]).CreateExpressionTree (ec
);
2724 if (scope_initializers
!= null)
2725 expr
= new BlockScopeExpression (expr
, this);
2730 return base.CreateExpressionTree (ec
);
2734 // Reformats this block to be top-level iterator block
2736 public IteratorStorey
ChangeToIterator (Iterator iterator
, ToplevelBlock source
)
2740 // Creates block with original statements
2741 AddStatement (new IteratorStatement (iterator
, new Block (this, source
)));
2743 source
.statements
= new List
<Statement
> (1);
2744 source
.AddStatement (new Return (iterator
, iterator
.Location
));
2745 source
.IsIterator
= false;
2747 IteratorStorey iterator_storey
= new IteratorStorey (iterator
);
2748 source
.am_storey
= iterator_storey
;
2749 return iterator_storey
;
2753 // Returns a parameter reference expression for the given name,
2754 // or null if there is no such parameter
2756 public Expression
GetParameterReference (string name
, Location loc
)
2758 for (ToplevelBlock t
= this; t
!= null; t
= t
.Container
) {
2759 Expression expr
= t
.GetParameterReferenceExpression (name
, loc
);
2767 protected virtual Expression
GetParameterReferenceExpression (string name
, Location loc
)
2769 int idx
= parameters
.GetParameterIndexByName (name
);
2771 null : new ParameterReference (parameter_info
[idx
], loc
);
2775 // Returns the "this" instance variable of this block.
2776 // See AddThisVariable() for more information.
2778 public LocalInfo ThisVariable
{
2779 get { return this_variable; }
2783 // This is used by non-static `struct' constructors which do not have an
2784 // initializer - in this case, the constructor must initialize all of the
2785 // struct's fields. To do this, we add a "this" variable and use the flow
2786 // analysis code to ensure that it's been fully initialized before control
2787 // leaves the constructor.
2789 public LocalInfo
AddThisVariable (DeclSpace ds
, Location l
)
2791 if (this_variable
== null) {
2792 this_variable
= new LocalInfo (ds
, this, l
);
2793 this_variable
.Used
= true;
2794 this_variable
.IsThis
= true;
2796 Variables
.Add ("this", this_variable
);
2799 return this_variable
;
2802 public bool IsIterator
{
2803 get { return (flags & Flags.IsIterator) != 0; }
2804 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2808 // Block has been converted to expression tree
2810 public bool IsExpressionTree
{
2811 get { return (flags & Flags.IsExpressionTree) != 0; }
2814 public bool IsThisAssigned (BlockContext ec
)
2816 return this_variable
== null || this_variable
.IsThisAssigned (ec
, this);
2819 public bool Resolve (FlowBranching parent
, BlockContext rc
, ParametersCompiled ip
, IMethodData md
)
2826 if (rc
.HasSet (ResolveContext
.Options
.ExpressionTreeConversion
))
2827 flags
|= Flags
.IsExpressionTree
;
2830 if (!ResolveMeta (rc
, ip
))
2833 using (rc
.With (ResolveContext
.Options
.DoFlowAnalysis
, true)) {
2834 FlowBranchingToplevel top_level
= rc
.StartFlowBranching (this, parent
);
2839 unreachable
= top_level
.End ();
2841 } catch (Exception
) {
2843 if (rc
.CurrentBlock
!= null) {
2844 ec
.Report
.Error (584, rc
.CurrentBlock
.StartLocation
, "Internal compiler error: Phase Resolve");
2846 ec
.Report
.Error (587, "Internal compiler error: Phase Resolve");
2852 if (rc
.ReturnType
!= TypeManager
.void_type
&& !unreachable
) {
2853 if (rc
.CurrentAnonymousMethod
== null) {
2854 rc
.Report
.Error (161, md
.Location
, "`{0}': not all code paths return a value", md
.GetSignatureForError ());
2856 } else if (!rc
.CurrentAnonymousMethod
.IsIterator
) {
2857 rc
.Report
.Error (1643, rc
.CurrentAnonymousMethod
.Location
, "Not all code paths return a value in anonymous method of type `{0}'",
2858 rc
.CurrentAnonymousMethod
.GetSignatureForError ());
2866 bool ResolveMeta (BlockContext ec
, ParametersCompiled ip
)
2868 int errors
= ec
.Report
.Errors
;
2869 int orig_count
= parameters
.Count
;
2874 // Assert: orig_count != parameter.Count => orig_count == 0
2875 if (orig_count
!= 0 && orig_count
!= parameters
.Count
)
2876 throw new InternalErrorException ("parameter information mismatch");
2878 int offset
= Parent
== null ? 0 : Parent
.AssignableSlots
;
2880 for (int i
= 0; i
< orig_count
; ++i
) {
2881 Parameter
.Modifier mod
= parameters
.FixedParameters
[i
].ModFlags
;
2883 if ((mod
& Parameter
.Modifier
.OUT
) != Parameter
.Modifier
.OUT
)
2886 VariableInfo vi
= new VariableInfo (ip
, i
, offset
);
2887 parameter_info
[i
].VariableInfo
= vi
;
2888 offset
+= vi
.Length
;
2891 ResolveMeta (ec
, offset
);
2893 return ec
.Report
.Errors
== errors
;
2897 // Check whether all `out' parameters have been assigned.
2899 public void CheckOutParameters (FlowBranching
.UsageVector vector
, Location loc
)
2901 if (vector
.IsUnreachable
)
2904 int n
= parameter_info
== null ? 0 : parameter_info
.Length
;
2906 for (int i
= 0; i
< n
; i
++) {
2907 VariableInfo
var = parameter_info
[i
].VariableInfo
;
2912 if (vector
.IsAssigned (var, false))
2915 Report
.Error (177, loc
, "The out parameter `{0}' must be assigned to before control leaves the current method",
2920 public override void Emit (EmitContext ec
)
2922 if (Report
.Errors
> 0)
2930 if (ec
.HasReturnLabel
)
2931 ec
.ReturnLabel
= ec
.ig
.DefineLabel ();
2935 ec
.Mark (EndLocation
);
2937 if (ec
.HasReturnLabel
)
2938 ec
.ig
.MarkLabel (ec
.ReturnLabel
);
2940 if (ec
.return_value
!= null) {
2941 ec
.ig
.Emit (OpCodes
.Ldloc
, ec
.return_value
);
2942 ec
.ig
.Emit (OpCodes
.Ret
);
2945 // If `HasReturnLabel' is set, then we already emitted a
2946 // jump to the end of the method, so we must emit a `ret'
2949 // Unfortunately, System.Reflection.Emit automatically emits
2950 // a leave to the end of a finally block. This is a problem
2951 // if no code is following the try/finally block since we may
2952 // jump to a point after the end of the method.
2953 // As a workaround, we're always creating a return label in
2957 if (ec
.HasReturnLabel
|| !unreachable
) {
2958 if (ec
.ReturnType
!= TypeManager
.void_type
)
2959 ec
.ig
.Emit (OpCodes
.Ldloc
, ec
.TemporaryReturn ());
2960 ec
.ig
.Emit (OpCodes
.Ret
);
2965 } catch (Exception e
){
2966 Console
.WriteLine ("Exception caught by the compiler while emitting:");
2967 Console
.WriteLine (" Block that caused the problem begin at: " + block
.loc
);
2969 Console
.WriteLine (e
.GetType ().FullName
+ ": " + e
.Message
);
2975 public override void EmitMeta (EmitContext ec
)
2977 parameters
.ResolveVariable ();
2979 // Avoid declaring an IL variable for this_variable since it is not accessed
2980 // from the generated IL
2981 if (this_variable
!= null)
2982 Variables
.Remove ("this");
2986 protected override void EmitSymbolInfo (EmitContext ec
)
2988 AnonymousExpression ae
= ec
.CurrentAnonymousMethod
;
2989 if ((ae
!= null) && (ae
.Storey
!= null))
2990 SymbolWriter
.DefineScopeVariable (ae
.Storey
.ID
);
2992 base.EmitSymbolInfo (ec
);
2996 public class SwitchLabel
{
3003 Label il_label_code
;
3004 bool il_label_code_set
;
3006 public static readonly object NullStringCase
= new object ();
3009 // if expr == null, then it is the default case.
3011 public SwitchLabel (Expression expr
, Location l
)
3017 public Expression Label
{
3023 public Location Location
{
3027 public object Converted
{
3033 public Label
GetILLabel (EmitContext ec
)
3036 il_label
= ec
.ig
.DefineLabel ();
3037 il_label_set
= true;
3042 public Label
GetILLabelCode (EmitContext ec
)
3044 if (!il_label_code_set
){
3045 il_label_code
= ec
.ig
.DefineLabel ();
3046 il_label_code_set
= true;
3048 return il_label_code
;
3052 // Resolves the expression, reduces it to a literal if possible
3053 // and then converts it to the requested type.
3055 public bool ResolveAndReduce (ResolveContext ec
, Type required_type
, bool allow_nullable
)
3057 Expression e
= label
.Resolve (ec
);
3062 Constant c
= e
as Constant
;
3064 ec
.Report
.Error (150, loc
, "A constant value is expected");
3068 if (required_type
== TypeManager
.string_type
&& c
.GetValue () == null) {
3069 converted
= NullStringCase
;
3073 if (allow_nullable
&& c
.GetValue () == null) {
3074 converted
= NullStringCase
;
3078 c
= c
.ImplicitConversionRequired (ec
, required_type
, loc
);
3082 converted
= c
.GetValue ();
3086 public void Error_AlreadyOccurs (ResolveContext ec
, Type switch_type
, SwitchLabel collision_with
)
3089 if (converted
== null)
3091 else if (converted
== NullStringCase
)
3094 label
= converted
.ToString ();
3096 ec
.Report
.SymbolRelatedToPreviousError (collision_with
.loc
, null);
3097 ec
.Report
.Error (152, loc
, "The label `case {0}:' already occurs in this switch statement", label
);
3100 public SwitchLabel
Clone (CloneContext clonectx
)
3102 return new SwitchLabel (label
.Clone (clonectx
), loc
);
3106 public class SwitchSection
{
3107 // An array of SwitchLabels.
3108 public readonly List
<SwitchLabel
> Labels
;
3109 public readonly Block Block
;
3111 public SwitchSection (List
<SwitchLabel
> labels
, Block block
)
3117 public SwitchSection
Clone (CloneContext clonectx
)
3119 var cloned_labels
= new List
<SwitchLabel
> ();
3121 foreach (SwitchLabel sl
in cloned_labels
)
3122 cloned_labels
.Add (sl
.Clone (clonectx
));
3124 return new SwitchSection (cloned_labels
, clonectx
.LookupBlock (Block
));
3128 public class Switch
: Statement
{
3129 public List
<SwitchSection
> Sections
;
3130 public Expression Expr
;
3133 /// Maps constants whose type type SwitchType to their SwitchLabels.
3135 public IDictionary
<object, SwitchLabel
> Elements
;
3138 /// The governing switch type
3140 public Type SwitchType
;
3145 Label default_target
;
3147 Expression new_expr
;
3150 SwitchSection constant_section
;
3151 SwitchSection default_section
;
3153 ExpressionStatement string_dictionary
;
3154 FieldExpr switch_cache_field
;
3155 static int unique_counter
;
3158 // Nullable Types support
3160 Nullable
.Unwrap unwrap
;
3162 protected bool HaveUnwrap
{
3163 get { return unwrap != null; }
3167 // The types allowed to be implicitly cast from
3168 // on the governing type
3170 static Type
[] allowed_types
;
3172 public Switch (Expression e
, List
<SwitchSection
> sects
, Location l
)
3179 public bool GotDefault
{
3181 return default_section
!= null;
3185 public Label DefaultTarget
{
3187 return default_target
;
3192 // Determines the governing type for a switch. The returned
3193 // expression might be the expression from the switch, or an
3194 // expression that includes any potential conversions to the
3195 // integral types or to string.
3197 Expression
SwitchGoverningType (ResolveContext ec
, Expression expr
)
3201 if (t
== TypeManager
.byte_type
||
3202 t
== TypeManager
.sbyte_type
||
3203 t
== TypeManager
.ushort_type
||
3204 t
== TypeManager
.short_type
||
3205 t
== TypeManager
.uint32_type
||
3206 t
== TypeManager
.int32_type
||
3207 t
== TypeManager
.uint64_type
||
3208 t
== TypeManager
.int64_type
||
3209 t
== TypeManager
.char_type
||
3210 t
== TypeManager
.string_type
||
3211 t
== TypeManager
.bool_type
||
3212 TypeManager
.IsEnumType (t
))
3215 if (allowed_types
== null){
3216 allowed_types
= new Type
[] {
3217 TypeManager
.sbyte_type
,
3218 TypeManager
.byte_type
,
3219 TypeManager
.short_type
,
3220 TypeManager
.ushort_type
,
3221 TypeManager
.int32_type
,
3222 TypeManager
.uint32_type
,
3223 TypeManager
.int64_type
,
3224 TypeManager
.uint64_type
,
3225 TypeManager
.char_type
,
3226 TypeManager
.string_type
3231 // Try to find a *user* defined implicit conversion.
3233 // If there is no implicit conversion, or if there are multiple
3234 // conversions, we have to report an error
3236 Expression converted
= null;
3237 foreach (Type tt
in allowed_types
){
3240 e
= Convert
.ImplicitUserConversion (ec
, expr
, tt
, loc
);
3245 // Ignore over-worked ImplicitUserConversions that do
3246 // an implicit conversion in addition to the user conversion.
3248 if (!(e
is UserCast
))
3251 if (converted
!= null){
3252 ec
.Report
.ExtraInformation (loc
, "(Ambiguous implicit user defined conversion in previous ");
3262 // Performs the basic sanity checks on the switch statement
3263 // (looks for duplicate keys and non-constant expressions).
3265 // It also returns a hashtable with the keys that we will later
3266 // use to compute the switch tables
3268 bool CheckSwitch (ResolveContext ec
)
3271 Elements
= new Dictionary
<object, SwitchLabel
> ();
3273 foreach (SwitchSection ss
in Sections
){
3274 foreach (SwitchLabel sl
in ss
.Labels
){
3275 if (sl
.Label
== null){
3276 if (default_section
!= null){
3277 sl
.Error_AlreadyOccurs (ec
, SwitchType
, (SwitchLabel
)default_section
.Labels
[0]);
3280 default_section
= ss
;
3284 if (!sl
.ResolveAndReduce (ec
, SwitchType
, HaveUnwrap
)) {
3289 object key
= sl
.Converted
;
3290 if (key
== SwitchLabel
.NullStringCase
)
3291 has_null_case
= true;
3294 Elements
.Add (key
, sl
);
3295 } catch (ArgumentException
) {
3296 sl
.Error_AlreadyOccurs (ec
, SwitchType
, Elements
[key
]);
3304 void EmitObjectInteger (ILGenerator ig
, object k
)
3307 IntConstant
.EmitInt (ig
, (int) k
);
3308 else if (k
is Constant
) {
3309 EmitObjectInteger (ig
, ((Constant
) k
).GetValue ());
3312 IntConstant
.EmitInt (ig
, unchecked ((int) (uint) k
));
3315 if ((long) k
>= int.MinValue
&& (long) k
<= int.MaxValue
)
3317 IntConstant
.EmitInt (ig
, (int) (long) k
);
3318 ig
.Emit (OpCodes
.Conv_I8
);
3321 LongConstant
.EmitLong (ig
, (long) k
);
3323 else if (k
is ulong)
3325 ulong ul
= (ulong) k
;
3328 IntConstant
.EmitInt (ig
, unchecked ((int) ul
));
3329 ig
.Emit (OpCodes
.Conv_U8
);
3333 LongConstant
.EmitLong (ig
, unchecked ((long) ul
));
3337 IntConstant
.EmitInt (ig
, (int) ((char) k
));
3338 else if (k
is sbyte)
3339 IntConstant
.EmitInt (ig
, (int) ((sbyte) k
));
3341 IntConstant
.EmitInt (ig
, (int) ((byte) k
));
3342 else if (k
is short)
3343 IntConstant
.EmitInt (ig
, (int) ((short) k
));
3344 else if (k
is ushort)
3345 IntConstant
.EmitInt (ig
, (int) ((ushort) k
));
3347 IntConstant
.EmitInt (ig
, ((bool) k
) ? 1 : 0);
3349 throw new Exception ("Unhandled case");
3352 // structure used to hold blocks of keys while calculating table switch
3353 class KeyBlock
: IComparable
3355 public KeyBlock (long _first
)
3357 first
= last
= _first
;
3361 public List
<object> element_keys
;
3362 // how many items are in the bucket
3363 public int Size
= 1;
3366 get { return (int) (last - first + 1); }
3368 public static long TotalLength (KeyBlock kb_first
, KeyBlock kb_last
)
3370 return kb_last
.last
- kb_first
.first
+ 1;
3372 public int CompareTo (object obj
)
3374 KeyBlock kb
= (KeyBlock
) obj
;
3375 int nLength
= Length
;
3376 int nLengthOther
= kb
.Length
;
3377 if (nLengthOther
== nLength
)
3378 return (int) (kb
.first
- first
);
3379 return nLength
- nLengthOther
;
3384 /// This method emits code for a lookup-based switch statement (non-string)
3385 /// Basically it groups the cases into blocks that are at least half full,
3386 /// and then spits out individual lookup opcodes for each block.
3387 /// It emits the longest blocks first, and short blocks are just
3388 /// handled with direct compares.
3390 /// <param name="ec"></param>
3391 /// <param name="val"></param>
3392 /// <returns></returns>
3393 void TableSwitchEmit (EmitContext ec
, Expression val
)
3395 int element_count
= Elements
.Count
;
3396 object [] element_keys
= new object [element_count
];
3397 Elements
.Keys
.CopyTo (element_keys
, 0);
3398 Array
.Sort (element_keys
);
3400 // initialize the block list with one element per key
3401 var key_blocks
= new List
<KeyBlock
> (element_count
);
3402 foreach (object key
in element_keys
)
3403 key_blocks
.Add (new KeyBlock (System
.Convert
.ToInt64 (key
)));
3405 KeyBlock current_kb
;
3406 // iteratively merge the blocks while they are at least half full
3407 // there's probably a really cool way to do this with a tree...
3408 while (key_blocks
.Count
> 1)
3410 var key_blocks_new
= new List
<KeyBlock
> ();
3411 current_kb
= (KeyBlock
) key_blocks
[0];
3412 for (int ikb
= 1; ikb
< key_blocks
.Count
; ikb
++)
3414 KeyBlock kb
= (KeyBlock
) key_blocks
[ikb
];
3415 if ((current_kb
.Size
+ kb
.Size
) * 2 >= KeyBlock
.TotalLength (current_kb
, kb
))
3418 current_kb
.last
= kb
.last
;
3419 current_kb
.Size
+= kb
.Size
;
3423 // start a new block
3424 key_blocks_new
.Add (current_kb
);
3428 key_blocks_new
.Add (current_kb
);
3429 if (key_blocks
.Count
== key_blocks_new
.Count
)
3431 key_blocks
= key_blocks_new
;
3434 // initialize the key lists
3435 foreach (KeyBlock kb
in key_blocks
)
3436 kb
.element_keys
= new List
<object> ();
3438 // fill the key lists
3440 if (key_blocks
.Count
> 0) {
3441 current_kb
= (KeyBlock
) key_blocks
[0];
3442 foreach (object key
in element_keys
)
3444 bool next_block
= (key
is UInt64
) ? (ulong) key
> (ulong) current_kb
.last
:
3445 System
.Convert
.ToInt64 (key
) > current_kb
.last
;
3447 current_kb
= (KeyBlock
) key_blocks
[++iBlockCurr
];
3448 current_kb
.element_keys
.Add (key
);
3452 // sort the blocks so we can tackle the largest ones first
3455 // okay now we can start...
3456 ILGenerator ig
= ec
.ig
;
3457 Label lbl_end
= ig
.DefineLabel (); // at the end ;-)
3458 Label lbl_default
= default_target
;
3460 Type type_keys
= null;
3461 if (element_keys
.Length
> 0)
3462 type_keys
= element_keys
[0].GetType (); // used for conversions
3466 if (TypeManager
.IsEnumType (SwitchType
))
3467 compare_type
= TypeManager
.GetEnumUnderlyingType (SwitchType
);
3469 compare_type
= SwitchType
;
3471 for (int iBlock
= key_blocks
.Count
- 1; iBlock
>= 0; --iBlock
)
3473 KeyBlock kb
= ((KeyBlock
) key_blocks
[iBlock
]);
3474 lbl_default
= (iBlock
== 0) ? default_target
: ig
.DefineLabel ();
3477 foreach (object key
in kb
.element_keys
) {
3478 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
3479 if (key
is int && (int) key
== 0) {
3480 val
.EmitBranchable (ec
, sl
.GetILLabel (ec
), false);
3483 EmitObjectInteger (ig
, key
);
3484 ig
.Emit (OpCodes
.Beq
, sl
.GetILLabel (ec
));
3490 // TODO: if all the keys in the block are the same and there are
3491 // no gaps/defaults then just use a range-check.
3492 if (compare_type
== TypeManager
.int64_type
||
3493 compare_type
== TypeManager
.uint64_type
)
3495 // TODO: optimize constant/I4 cases
3497 // check block range (could be > 2^31)
3499 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.first
, type_keys
));
3500 ig
.Emit (OpCodes
.Blt
, lbl_default
);
3502 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.last
, type_keys
));
3503 ig
.Emit (OpCodes
.Bgt
, lbl_default
);
3509 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.first
, type_keys
));
3510 ig
.Emit (OpCodes
.Sub
);
3512 ig
.Emit (OpCodes
.Conv_I4
); // assumes < 2^31 labels!
3518 int first
= (int) kb
.first
;
3521 IntConstant
.EmitInt (ig
, first
);
3522 ig
.Emit (OpCodes
.Sub
);
3526 IntConstant
.EmitInt (ig
, -first
);
3527 ig
.Emit (OpCodes
.Add
);
3531 // first, build the list of labels for the switch
3533 int cJumps
= kb
.Length
;
3534 Label
[] switch_labels
= new Label
[cJumps
];
3535 for (int iJump
= 0; iJump
< cJumps
; iJump
++)
3537 object key
= kb
.element_keys
[iKey
];
3538 if (System
.Convert
.ToInt64 (key
) == kb
.first
+ iJump
)
3540 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
3541 switch_labels
[iJump
] = sl
.GetILLabel (ec
);
3545 switch_labels
[iJump
] = lbl_default
;
3547 // emit the switch opcode
3548 ig
.Emit (OpCodes
.Switch
, switch_labels
);
3551 // mark the default for this block
3553 ig
.MarkLabel (lbl_default
);
3556 // TODO: find the default case and emit it here,
3557 // to prevent having to do the following jump.
3558 // make sure to mark other labels in the default section
3560 // the last default just goes to the end
3561 if (element_keys
.Length
> 0)
3562 ig
.Emit (OpCodes
.Br
, lbl_default
);
3564 // now emit the code for the sections
3565 bool found_default
= false;
3567 foreach (SwitchSection ss
in Sections
) {
3568 foreach (SwitchLabel sl
in ss
.Labels
) {
3569 if (sl
.Converted
== SwitchLabel
.NullStringCase
) {
3570 ig
.MarkLabel (null_target
);
3571 } else if (sl
.Label
== null) {
3572 ig
.MarkLabel (lbl_default
);
3573 found_default
= true;
3575 ig
.MarkLabel (null_target
);
3577 ig
.MarkLabel (sl
.GetILLabel (ec
));
3578 ig
.MarkLabel (sl
.GetILLabelCode (ec
));
3583 if (!found_default
) {
3584 ig
.MarkLabel (lbl_default
);
3585 if (!has_null_case
) {
3586 ig
.MarkLabel (null_target
);
3590 ig
.MarkLabel (lbl_end
);
3593 SwitchSection
FindSection (SwitchLabel label
)
3595 foreach (SwitchSection ss
in Sections
){
3596 foreach (SwitchLabel sl
in ss
.Labels
){
3605 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
3607 foreach (SwitchSection ss
in Sections
)
3608 ss
.Block
.MutateHoistedGenericType (storey
);
3611 public static void Reset ()
3614 allowed_types
= null;
3617 public override bool Resolve (BlockContext ec
)
3619 Expr
= Expr
.Resolve (ec
);
3623 new_expr
= SwitchGoverningType (ec
, Expr
);
3625 if ((new_expr
== null) && TypeManager
.IsNullableType (Expr
.Type
)) {
3626 unwrap
= Nullable
.Unwrap
.Create (Expr
, false);
3630 new_expr
= SwitchGoverningType (ec
, unwrap
);
3633 if (new_expr
== null){
3634 ec
.Report
.Error (151, loc
,
3635 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3636 TypeManager
.CSharpName (Expr
.Type
));
3641 SwitchType
= new_expr
.Type
;
3643 if (RootContext
.Version
== LanguageVersion
.ISO_1
&& SwitchType
== TypeManager
.bool_type
) {
3644 ec
.Report
.FeatureIsNotAvailable (loc
, "switch expression of boolean type");
3648 if (!CheckSwitch (ec
))
3652 Elements
.Remove (SwitchLabel
.NullStringCase
);
3654 Switch old_switch
= ec
.Switch
;
3656 ec
.Switch
.SwitchType
= SwitchType
;
3658 Report
.Debug (1, "START OF SWITCH BLOCK", loc
, ec
.CurrentBranching
);
3659 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Switch
, loc
);
3661 var constant
= new_expr
as Constant
;
3662 if (constant
!= null) {
3664 object key
= constant
.GetValue ();
3666 if (Elements
.TryGetValue (key
, out label
))
3667 constant_section
= FindSection (label
);
3669 if (constant_section
== null)
3670 constant_section
= default_section
;
3675 foreach (SwitchSection ss
in Sections
){
3677 ec
.CurrentBranching
.CreateSibling (
3678 null, FlowBranching
.SiblingType
.SwitchSection
);
3682 if (is_constant
&& (ss
!= constant_section
)) {
3683 // If we're a constant switch, we're only emitting
3684 // one single section - mark all the others as
3686 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
3687 if (!ss
.Block
.ResolveUnreachable (ec
, true)) {
3691 if (!ss
.Block
.Resolve (ec
))
3696 if (default_section
== null)
3697 ec
.CurrentBranching
.CreateSibling (
3698 null, FlowBranching
.SiblingType
.SwitchSection
);
3700 ec
.EndFlowBranching ();
3701 ec
.Switch
= old_switch
;
3703 Report
.Debug (1, "END OF SWITCH BLOCK", loc
, ec
.CurrentBranching
);
3708 if (SwitchType
== TypeManager
.string_type
&& !is_constant
) {
3709 // TODO: Optimize single case, and single+default case
3710 ResolveStringSwitchMap (ec
);
3716 void ResolveStringSwitchMap (ResolveContext ec
)
3718 FullNamedExpression string_dictionary_type
;
3719 if (TypeManager
.generic_ienumerable_type
!= null) {
3720 MemberAccess system_collections_generic
= new MemberAccess (new MemberAccess (
3721 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "System", loc
), "Collections", loc
), "Generic", loc
);
3723 string_dictionary_type
= new MemberAccess (system_collections_generic
, "Dictionary",
3725 new TypeExpression (TypeManager
.string_type
, loc
),
3726 new TypeExpression (TypeManager
.int32_type
, loc
)), loc
);
3728 MemberAccess system_collections_generic
= new MemberAccess (
3729 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "System", loc
), "Collections", loc
);
3731 string_dictionary_type
= new MemberAccess (system_collections_generic
, "Hashtable", loc
);
3734 Field field
= new Field (ec
.CurrentTypeDefinition
, string_dictionary_type
,
3735 Modifiers
.STATIC
| Modifiers
.PRIVATE
| Modifiers
.COMPILER_GENERATED
,
3736 new MemberName (CompilerGeneratedClass
.MakeName (null, "f", "switch$map", unique_counter
++), loc
), null);
3737 if (!field
.Define ())
3739 ec
.CurrentTypeDefinition
.PartialContainer
.AddField (field
);
3741 var init
= new List
<Expression
> ();
3744 string value = null;
3745 foreach (SwitchSection section
in Sections
) {
3746 int last_count
= init
.Count
;
3747 foreach (SwitchLabel sl
in section
.Labels
) {
3748 if (sl
.Label
== null || sl
.Converted
== SwitchLabel
.NullStringCase
)
3751 value = (string) sl
.Converted
;
3752 var init_args
= new List
<Expression
> (2);
3753 init_args
.Add (new StringLiteral (value, sl
.Location
));
3754 init_args
.Add (new IntConstant (counter
, loc
));
3755 init
.Add (new CollectionElementInitializer (init_args
, loc
));
3759 // Don't add empty sections
3761 if (last_count
== init
.Count
)
3764 Elements
.Add (counter
, section
.Labels
[0]);
3768 Arguments args
= new Arguments (1);
3769 args
.Add (new Argument (new IntConstant (init
.Count
, loc
)));
3770 Expression initializer
= new NewInitialize (string_dictionary_type
, args
,
3771 new CollectionOrObjectInitializers (init
, loc
), loc
);
3773 switch_cache_field
= new FieldExpr (field
, loc
);
3774 string_dictionary
= new SimpleAssign (switch_cache_field
, initializer
.Resolve (ec
));
3777 void DoEmitStringSwitch (LocalTemporary
value, EmitContext ec
)
3779 ILGenerator ig
= ec
.ig
;
3780 Label l_initialized
= ig
.DefineLabel ();
3783 // Skip initialization when value is null
3785 value.EmitBranchable (ec
, null_target
, false);
3788 // Check if string dictionary is initialized and initialize
3790 switch_cache_field
.EmitBranchable (ec
, l_initialized
, true);
3791 string_dictionary
.EmitStatement (ec
);
3792 ig
.MarkLabel (l_initialized
);
3794 LocalTemporary string_switch_variable
= new LocalTemporary (TypeManager
.int32_type
);
3796 ResolveContext rc
= new ResolveContext (ec
.MemberContext
);
3798 if (TypeManager
.generic_ienumerable_type
!= null) {
3799 Arguments get_value_args
= new Arguments (2);
3800 get_value_args
.Add (new Argument (value));
3801 get_value_args
.Add (new Argument (string_switch_variable
, Argument
.AType
.Out
));
3802 Expression get_item
= new Invocation (new MemberAccess (switch_cache_field
, "TryGetValue", loc
), get_value_args
).Resolve (rc
);
3803 if (get_item
== null)
3807 // A value was not found, go to default case
3809 get_item
.EmitBranchable (ec
, default_target
, false);
3811 Arguments get_value_args
= new Arguments (1);
3812 get_value_args
.Add (new Argument (value));
3814 Expression get_item
= new IndexerAccess (new ElementAccess (switch_cache_field
, get_value_args
), loc
).Resolve (rc
);
3815 if (get_item
== null)
3818 LocalTemporary get_item_object
= new LocalTemporary (TypeManager
.object_type
);
3819 get_item_object
.EmitAssign (ec
, get_item
, true, false);
3820 ec
.ig
.Emit (OpCodes
.Brfalse
, default_target
);
3822 ExpressionStatement get_item_int
= (ExpressionStatement
) new SimpleAssign (string_switch_variable
,
3823 new Cast (new TypeExpression (TypeManager
.int32_type
, loc
), get_item_object
, loc
)).Resolve (rc
);
3825 get_item_int
.EmitStatement (ec
);
3826 get_item_object
.Release (ec
);
3829 TableSwitchEmit (ec
, string_switch_variable
);
3830 string_switch_variable
.Release (ec
);
3833 protected override void DoEmit (EmitContext ec
)
3835 ILGenerator ig
= ec
.ig
;
3837 default_target
= ig
.DefineLabel ();
3838 null_target
= ig
.DefineLabel ();
3840 // Store variable for comparission purposes
3841 // TODO: Don't duplicate non-captured VariableReference
3842 LocalTemporary
value;
3844 value = new LocalTemporary (SwitchType
);
3845 unwrap
.EmitCheck (ec
);
3846 ig
.Emit (OpCodes
.Brfalse
, null_target
);
3849 } else if (!is_constant
) {
3850 value = new LocalTemporary (SwitchType
);
3857 // Setup the codegen context
3859 Label old_end
= ec
.LoopEnd
;
3860 Switch old_switch
= ec
.Switch
;
3862 ec
.LoopEnd
= ig
.DefineLabel ();
3867 if (constant_section
!= null)
3868 constant_section
.Block
.Emit (ec
);
3869 } else if (string_dictionary
!= null) {
3870 DoEmitStringSwitch (value, ec
);
3872 TableSwitchEmit (ec
, value);
3878 // Restore context state.
3879 ig
.MarkLabel (ec
.LoopEnd
);
3882 // Restore the previous context
3884 ec
.LoopEnd
= old_end
;
3885 ec
.Switch
= old_switch
;
3888 protected override void CloneTo (CloneContext clonectx
, Statement t
)
3890 Switch target
= (Switch
) t
;
3892 target
.Expr
= Expr
.Clone (clonectx
);
3893 target
.Sections
= new List
<SwitchSection
> ();
3894 foreach (SwitchSection ss
in Sections
){
3895 target
.Sections
.Add (ss
.Clone (clonectx
));
3900 // A place where execution can restart in an iterator
3901 public abstract class ResumableStatement
: Statement
3904 protected Label resume_point
;
3906 public Label
PrepareForEmit (EmitContext ec
)
3910 resume_point
= ec
.ig
.DefineLabel ();
3912 return resume_point
;
3915 public virtual Label
PrepareForDispose (EmitContext ec
, Label end
)
3919 public virtual void EmitForDispose (EmitContext ec
, Iterator iterator
, Label end
, bool have_dispatcher
)
3924 // Base class for statements that are implemented in terms of try...finally
3925 public abstract class ExceptionStatement
: ResumableStatement
3929 List
<ResumableStatement
> resume_points
;
3930 int first_resume_pc
;
3932 protected abstract void EmitPreTryBody (EmitContext ec
);
3933 protected abstract void EmitTryBody (EmitContext ec
);
3934 protected abstract void EmitFinallyBody (EmitContext ec
);
3936 protected sealed override void DoEmit (EmitContext ec
)
3938 ILGenerator ig
= ec
.ig
;
3940 EmitPreTryBody (ec
);
3942 if (resume_points
!= null) {
3943 IntConstant
.EmitInt (ig
, (int) Iterator
.State
.Running
);
3944 ig
.Emit (OpCodes
.Stloc
, iter
.CurrentPC
);
3947 ig
.BeginExceptionBlock ();
3949 if (resume_points
!= null) {
3950 ig
.MarkLabel (resume_point
);
3952 // For normal control flow, we want to fall-through the Switch
3953 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3954 ig
.Emit (OpCodes
.Ldloc
, iter
.CurrentPC
);
3955 IntConstant
.EmitInt (ig
, first_resume_pc
);
3956 ig
.Emit (OpCodes
.Sub
);
3958 Label
[] labels
= new Label
[resume_points
.Count
];
3959 for (int i
= 0; i
< resume_points
.Count
; ++i
)
3960 labels
[i
] = ((ResumableStatement
) resume_points
[i
]).PrepareForEmit (ec
);
3961 ig
.Emit (OpCodes
.Switch
, labels
);
3966 ig
.BeginFinallyBlock ();
3968 Label start_finally
= ec
.ig
.DefineLabel ();
3969 if (resume_points
!= null) {
3970 ig
.Emit (OpCodes
.Ldloc
, iter
.SkipFinally
);
3971 ig
.Emit (OpCodes
.Brfalse_S
, start_finally
);
3972 ig
.Emit (OpCodes
.Endfinally
);
3975 ig
.MarkLabel (start_finally
);
3976 EmitFinallyBody (ec
);
3978 ig
.EndExceptionBlock ();
3981 public void SomeCodeFollows ()
3983 code_follows
= true;
3986 public override bool Resolve (BlockContext ec
)
3988 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3989 // So, ensure there's some IL code after this statement.
3990 if (!code_follows
&& resume_points
== null && ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
)
3991 ec
.NeedReturnLabel ();
3993 iter
= ec
.CurrentIterator
;
3997 public void AddResumePoint (ResumableStatement stmt
, int pc
)
3999 if (resume_points
== null) {
4000 resume_points
= new List
<ResumableStatement
> ();
4001 first_resume_pc
= pc
;
4004 if (pc
!= first_resume_pc
+ resume_points
.Count
)
4005 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4007 resume_points
.Add (stmt
);
4010 Label dispose_try_block
;
4011 bool prepared_for_dispose
, emitted_dispose
;
4012 public override Label
PrepareForDispose (EmitContext ec
, Label end
)
4014 if (!prepared_for_dispose
) {
4015 prepared_for_dispose
= true;
4016 dispose_try_block
= ec
.ig
.DefineLabel ();
4018 return dispose_try_block
;
4021 public override void EmitForDispose (EmitContext ec
, Iterator iterator
, Label end
, bool have_dispatcher
)
4023 if (emitted_dispose
)
4026 emitted_dispose
= true;
4028 ILGenerator ig
= ec
.ig
;
4030 Label end_of_try
= ig
.DefineLabel ();
4032 // Ensure that the only way we can get into this code is through a dispatcher
4033 if (have_dispatcher
)
4034 ig
.Emit (OpCodes
.Br
, end
);
4036 ig
.BeginExceptionBlock ();
4038 ig
.MarkLabel (dispose_try_block
);
4040 Label
[] labels
= null;
4041 for (int i
= 0; i
< resume_points
.Count
; ++i
) {
4042 ResumableStatement s
= (ResumableStatement
) resume_points
[i
];
4043 Label ret
= s
.PrepareForDispose (ec
, end_of_try
);
4044 if (ret
.Equals (end_of_try
) && labels
== null)
4046 if (labels
== null) {
4047 labels
= new Label
[resume_points
.Count
];
4048 for (int j
= 0; j
< i
; ++j
)
4049 labels
[j
] = end_of_try
;
4054 if (labels
!= null) {
4056 for (j
= 1; j
< labels
.Length
; ++j
)
4057 if (!labels
[0].Equals (labels
[j
]))
4059 bool emit_dispatcher
= j
< labels
.Length
;
4061 if (emit_dispatcher
) {
4062 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4063 ig
.Emit (OpCodes
.Ldloc
, iterator
.CurrentPC
);
4064 IntConstant
.EmitInt (ig
, first_resume_pc
);
4065 ig
.Emit (OpCodes
.Sub
);
4066 ig
.Emit (OpCodes
.Switch
, labels
);
4067 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4070 foreach (ResumableStatement s
in resume_points
)
4071 s
.EmitForDispose (ec
, iterator
, end_of_try
, emit_dispatcher
);
4074 ig
.MarkLabel (end_of_try
);
4076 ig
.BeginFinallyBlock ();
4078 EmitFinallyBody (ec
);
4080 ig
.EndExceptionBlock ();
4084 public class Lock
: ExceptionStatement
{
4086 public Statement Statement
;
4087 TemporaryVariable temp
;
4089 public Lock (Expression expr
, Statement stmt
, Location l
)
4096 public override bool Resolve (BlockContext ec
)
4098 expr
= expr
.Resolve (ec
);
4102 if (!TypeManager
.IsReferenceType (expr
.Type
)){
4103 ec
.Report
.Error (185, loc
,
4104 "`{0}' is not a reference type as required by the lock statement",
4105 TypeManager
.CSharpName (expr
.Type
));
4109 ec
.StartFlowBranching (this);
4110 bool ok
= Statement
.Resolve (ec
);
4111 ec
.EndFlowBranching ();
4113 ok
&= base.Resolve (ec
);
4115 // Avoid creating libraries that reference the internal
4118 if (t
== TypeManager
.null_type
)
4119 t
= TypeManager
.object_type
;
4121 temp
= new TemporaryVariable (t
, loc
);
4124 if (TypeManager
.void_monitor_enter_object
== null || TypeManager
.void_monitor_exit_object
== null) {
4125 Type monitor_type
= TypeManager
.CoreLookupType (ec
.Compiler
, "System.Threading", "Monitor", MemberKind
.Class
, true);
4126 TypeManager
.void_monitor_enter_object
= TypeManager
.GetPredefinedMethod (
4127 monitor_type
, "Enter", loc
, TypeManager
.object_type
);
4128 TypeManager
.void_monitor_exit_object
= TypeManager
.GetPredefinedMethod (
4129 monitor_type
, "Exit", loc
, TypeManager
.object_type
);
4135 protected override void EmitPreTryBody (EmitContext ec
)
4137 ILGenerator ig
= ec
.ig
;
4139 temp
.EmitAssign (ec
, expr
);
4141 ig
.Emit (OpCodes
.Call
, (MethodInfo
) TypeManager
.void_monitor_enter_object
.MetaInfo
);
4144 protected override void EmitTryBody (EmitContext ec
)
4146 Statement
.Emit (ec
);
4149 protected override void EmitFinallyBody (EmitContext ec
)
4152 ec
.ig
.Emit (OpCodes
.Call
, (MethodInfo
) TypeManager
.void_monitor_exit_object
.MetaInfo
);
4155 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4157 expr
.MutateHoistedGenericType (storey
);
4158 temp
.MutateHoistedGenericType (storey
);
4159 Statement
.MutateHoistedGenericType (storey
);
4162 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4164 Lock target
= (Lock
) t
;
4166 target
.expr
= expr
.Clone (clonectx
);
4167 target
.Statement
= Statement
.Clone (clonectx
);
4171 public class Unchecked
: Statement
{
4174 public Unchecked (Block b
)
4180 public override bool Resolve (BlockContext ec
)
4182 using (ec
.With (ResolveContext
.Options
.AllCheckStateFlags
, false))
4183 return Block
.Resolve (ec
);
4186 protected override void DoEmit (EmitContext ec
)
4188 using (ec
.With (EmitContext
.Options
.AllCheckStateFlags
, false))
4192 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4194 Block
.MutateHoistedGenericType (storey
);
4197 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4199 Unchecked target
= (Unchecked
) t
;
4201 target
.Block
= clonectx
.LookupBlock (Block
);
4205 public class Checked
: Statement
{
4208 public Checked (Block b
)
4211 b
.Unchecked
= false;
4214 public override bool Resolve (BlockContext ec
)
4216 using (ec
.With (ResolveContext
.Options
.AllCheckStateFlags
, true))
4217 return Block
.Resolve (ec
);
4220 protected override void DoEmit (EmitContext ec
)
4222 using (ec
.With (EmitContext
.Options
.AllCheckStateFlags
, true))
4226 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4228 Block
.MutateHoistedGenericType (storey
);
4231 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4233 Checked target
= (Checked
) t
;
4235 target
.Block
= clonectx
.LookupBlock (Block
);
4239 public class Unsafe
: Statement
{
4242 public Unsafe (Block b
)
4245 Block
.Unsafe
= true;
4246 loc
= b
.StartLocation
;
4249 public override bool Resolve (BlockContext ec
)
4251 if (ec
.CurrentIterator
!= null)
4252 ec
.Report
.Error (1629, loc
, "Unsafe code may not appear in iterators");
4254 using (ec
.Set (ResolveContext
.Options
.UnsafeScope
))
4255 return Block
.Resolve (ec
);
4258 protected override void DoEmit (EmitContext ec
)
4263 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4265 Block
.MutateHoistedGenericType (storey
);
4268 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4270 Unsafe target
= (Unsafe
) t
;
4272 target
.Block
= clonectx
.LookupBlock (Block
);
4279 class Fixed
: Statement
{
4281 List
<KeyValuePair
<LocalInfo
, Expression
>> declarators
;
4282 Statement statement
;
4287 abstract class Emitter
4289 protected LocalInfo vi
;
4290 protected Expression converted
;
4292 protected Emitter (Expression expr
, LocalInfo li
)
4298 public abstract void Emit (EmitContext ec
);
4299 public abstract void EmitExit (EmitContext ec
);
4302 class ExpressionEmitter
: Emitter
{
4303 public ExpressionEmitter (Expression converted
, LocalInfo li
) :
4304 base (converted
, li
)
4308 public override void Emit (EmitContext ec
) {
4310 // Store pointer in pinned location
4312 converted
.Emit (ec
);
4316 public override void EmitExit (EmitContext ec
)
4318 ec
.ig
.Emit (OpCodes
.Ldc_I4_0
);
4319 ec
.ig
.Emit (OpCodes
.Conv_U
);
4324 class StringEmitter
: Emitter
4326 LocalInfo pinned_string
;
4328 public StringEmitter (Expression expr
, LocalInfo li
, Location loc
):
4331 pinned_string
= new LocalInfo (new TypeExpression (TypeManager
.string_type
, loc
), null, null, loc
);
4332 pinned_string
.Pinned
= true;
4335 public StringEmitter
Resolve (ResolveContext rc
)
4337 pinned_string
.Resolve (rc
);
4339 if (TypeManager
.int_get_offset_to_string_data
== null) {
4340 TypeManager
.int_get_offset_to_string_data
= TypeManager
.GetPredefinedProperty (
4341 TypeManager
.runtime_helpers_type
, "OffsetToStringData", pinned_string
.Location
, TypeManager
.int32_type
);
4347 public override void Emit (EmitContext ec
)
4349 pinned_string
.ResolveVariable (ec
);
4351 converted
.Emit (ec
);
4352 pinned_string
.EmitAssign (ec
);
4354 // TODO: Should use Binary::Add
4355 pinned_string
.Emit (ec
);
4356 ec
.ig
.Emit (OpCodes
.Conv_I
);
4358 PropertyExpr pe
= new PropertyExpr (pinned_string
.VariableType
, TypeManager
.int_get_offset_to_string_data
, pinned_string
.Location
);
4359 //pe.InstanceExpression = pinned_string;
4360 pe
.Resolve (new ResolveContext (ec
.MemberContext
)).Emit (ec
);
4362 ec
.ig
.Emit (OpCodes
.Add
);
4366 public override void EmitExit (EmitContext ec
)
4368 ec
.ig
.Emit (OpCodes
.Ldnull
);
4369 pinned_string
.EmitAssign (ec
);
4373 public Fixed (Expression type
, List
<KeyValuePair
<LocalInfo
, Expression
>> decls
, Statement stmt
, Location l
)
4376 declarators
= decls
;
4381 public Statement Statement
{
4382 get { return statement; }
4385 public override bool Resolve (BlockContext ec
)
4388 Expression
.UnsafeError (ec
, loc
);
4392 TypeExpr texpr
= type
.ResolveAsContextualType (ec
, false);
4393 if (texpr
== null) {
4394 if (type
is VarExpr
)
4395 ec
.Report
.Error (821, type
.Location
, "A fixed statement cannot use an implicitly typed local variable");
4400 expr_type
= texpr
.Type
;
4402 data
= new Emitter
[declarators
.Count
];
4404 if (!expr_type
.IsPointer
){
4405 ec
.Report
.Error (209, loc
, "The type of locals declared in a fixed statement must be a pointer type");
4410 foreach (var p
in declarators
){
4411 LocalInfo vi
= p
.Key
;
4412 Expression e
= p
.Value
;
4414 vi
.VariableInfo
.SetAssigned (ec
);
4415 vi
.SetReadOnlyContext (LocalInfo
.ReadOnlyContext
.Fixed
);
4418 // The rules for the possible declarators are pretty wise,
4419 // but the production on the grammar is more concise.
4421 // So we have to enforce these rules here.
4423 // We do not resolve before doing the case 1 test,
4424 // because the grammar is explicit in that the token &
4425 // is present, so we need to test for this particular case.
4429 ec
.Report
.Error (254, loc
, "The right hand side of a fixed statement assignment may not be a cast expression");
4433 using (ec
.Set (ResolveContext
.Options
.FixedInitializerScope
)) {
4443 if (e
.Type
.IsArray
){
4444 Type array_type
= TypeManager
.GetElementType (e
.Type
);
4447 // Provided that array_type is unmanaged,
4449 if (!TypeManager
.VerifyUnmanaged (ec
.Compiler
, array_type
, loc
))
4453 // and T* is implicitly convertible to the
4454 // pointer type given in the fixed statement.
4456 ArrayPtr array_ptr
= new ArrayPtr (e
, array_type
, loc
);
4458 Expression converted
= Convert
.ImplicitConversionRequired (
4459 ec
, array_ptr
, vi
.VariableType
, loc
);
4460 if (converted
== null)
4464 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4466 converted
= new Conditional (new BooleanExpression (new Binary (Binary
.Operator
.LogicalOr
,
4467 new Binary (Binary
.Operator
.Equality
, e
, new NullLiteral (loc
), loc
),
4468 new Binary (Binary
.Operator
.Equality
, new MemberAccess (e
, "Length"), new IntConstant (0, loc
), loc
), loc
)),
4469 new NullPointer (loc
),
4472 converted
= converted
.Resolve (ec
);
4474 data
[i
] = new ExpressionEmitter (converted
, vi
);
4483 if (e
.Type
== TypeManager
.string_type
){
4484 data
[i
] = new StringEmitter (e
, vi
, loc
).Resolve (ec
);
4489 // Case 4: fixed buffer
4490 if (e
is FixedBufferPtr
) {
4491 data
[i
++] = new ExpressionEmitter (e
, vi
);
4496 // Case 1: & object.
4498 Unary u
= e
as Unary
;
4499 if (u
!= null && u
.Oper
== Unary
.Operator
.AddressOf
) {
4500 IVariableReference vr
= u
.Expr
as IVariableReference
;
4501 if (vr
== null || !vr
.IsFixed
) {
4502 data
[i
] = new ExpressionEmitter (e
, vi
);
4506 if (data
[i
++] == null)
4507 ec
.Report
.Error (213, vi
.Location
, "You cannot use the fixed statement to take the address of an already fixed expression");
4509 e
= Convert
.ImplicitConversionRequired (ec
, e
, expr_type
, loc
);
4512 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
4513 bool ok
= statement
.Resolve (ec
);
4514 bool flow_unreachable
= ec
.EndFlowBranching ();
4515 has_ret
= flow_unreachable
;
4520 protected override void DoEmit (EmitContext ec
)
4522 for (int i
= 0; i
< data
.Length
; i
++) {
4526 statement
.Emit (ec
);
4532 // Clear the pinned variable
4534 for (int i
= 0; i
< data
.Length
; i
++) {
4535 data
[i
].EmitExit (ec
);
4539 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4541 // Fixed statement cannot be used inside anonymous methods or lambdas
4542 throw new NotSupportedException ();
4545 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4547 Fixed target
= (Fixed
) t
;
4549 target
.type
= type
.Clone (clonectx
);
4550 target
.declarators
= new List
<KeyValuePair
<LocalInfo
, Expression
>> (declarators
.Count
);
4551 foreach (var p
in declarators
) {
4552 target
.declarators
.Add (new KeyValuePair
<LocalInfo
, Expression
> (
4553 clonectx
.LookupVariable (p
.Key
), p
.Value
.Clone (clonectx
)));
4556 target
.statement
= statement
.Clone (clonectx
);
4560 public class Catch
: Statement
{
4561 public readonly string Name
;
4563 public Block VarBlock
;
4565 Expression type_expr
;
4568 public Catch (Expression type
, string name
, Block block
, Block var_block
, Location l
)
4573 VarBlock
= var_block
;
4577 public Type CatchType
{
4583 public bool IsGeneral
{
4585 return type_expr
== null;
4589 protected override void DoEmit (EmitContext ec
)
4591 ILGenerator ig
= ec
.ig
;
4593 if (CatchType
!= null)
4594 ig
.BeginCatchBlock (CatchType
);
4596 ig
.BeginCatchBlock (TypeManager
.object_type
);
4598 if (VarBlock
!= null)
4602 // TODO: Move to resolve
4603 LocalVariableReference lvr
= new LocalVariableReference (Block
, Name
, loc
);
4604 lvr
.Resolve (new ResolveContext (ec
.MemberContext
));
4606 // Only to make verifier happy
4607 if (TypeManager
.IsGenericParameter (lvr
.Type
))
4608 ig
.Emit (OpCodes
.Unbox_Any
, lvr
.Type
);
4611 if (lvr
.IsHoisted
) {
4612 LocalTemporary lt
= new LocalTemporary (lvr
.Type
);
4616 // Variable is at the top of the stack
4617 source
= EmptyExpression
.Null
;
4620 lvr
.EmitAssign (ec
, source
, false, false);
4622 ig
.Emit (OpCodes
.Pop
);
4627 public override bool Resolve (BlockContext ec
)
4629 using (ec
.With (ResolveContext
.Options
.CatchScope
, true)) {
4630 if (type_expr
!= null) {
4631 TypeExpr te
= type_expr
.ResolveAsTypeTerminal (ec
, false);
4637 if (type
!= TypeManager
.exception_type
&& !TypeManager
.IsSubclassOf (type
, TypeManager
.exception_type
)){
4638 ec
.Report
.Error (155, loc
, "The type caught or thrown must be derived from System.Exception");
4644 if (!Block
.Resolve (ec
))
4647 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4648 // emit the "unused variable" warnings.
4649 if (VarBlock
!= null)
4650 return VarBlock
.Resolve (ec
);
4656 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4659 type
= storey
.MutateType (type
);
4660 if (VarBlock
!= null)
4661 VarBlock
.MutateHoistedGenericType (storey
);
4662 Block
.MutateHoistedGenericType (storey
);
4665 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4667 Catch target
= (Catch
) t
;
4669 if (type_expr
!= null)
4670 target
.type_expr
= type_expr
.Clone (clonectx
);
4671 if (VarBlock
!= null)
4672 target
.VarBlock
= clonectx
.LookupBlock (VarBlock
);
4673 target
.Block
= clonectx
.LookupBlock (Block
);
4677 public class TryFinally
: ExceptionStatement
{
4681 public TryFinally (Statement stmt
, Block fini
, Location l
)
4688 public override bool Resolve (BlockContext ec
)
4692 ec
.StartFlowBranching (this);
4694 if (!stmt
.Resolve (ec
))
4698 ec
.CurrentBranching
.CreateSibling (fini
, FlowBranching
.SiblingType
.Finally
);
4699 using (ec
.With (ResolveContext
.Options
.FinallyScope
, true)) {
4700 if (!fini
.Resolve (ec
))
4704 ec
.EndFlowBranching ();
4706 ok
&= base.Resolve (ec
);
4711 protected override void EmitPreTryBody (EmitContext ec
)
4715 protected override void EmitTryBody (EmitContext ec
)
4720 protected override void EmitFinallyBody (EmitContext ec
)
4725 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4727 stmt
.MutateHoistedGenericType (storey
);
4728 fini
.MutateHoistedGenericType (storey
);
4731 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4733 TryFinally target
= (TryFinally
) t
;
4735 target
.stmt
= (Statement
) stmt
.Clone (clonectx
);
4737 target
.fini
= clonectx
.LookupBlock (fini
);
4741 public class TryCatch
: Statement
{
4743 public List
<Catch
> Specific
;
4744 public Catch General
;
4745 bool inside_try_finally
, code_follows
;
4747 public TryCatch (Block block
, List
<Catch
> catch_clauses
, Location l
, bool inside_try_finally
)
4750 this.Specific
= catch_clauses
;
4751 this.inside_try_finally
= inside_try_finally
;
4753 Catch c
= catch_clauses
[0];
4756 catch_clauses
.RemoveAt (0);
4762 public override bool Resolve (BlockContext ec
)
4766 ec
.StartFlowBranching (this);
4768 if (!Block
.Resolve (ec
))
4771 Type
[] prev_catches
= new Type
[Specific
.Count
];
4773 foreach (Catch c
in Specific
){
4774 ec
.CurrentBranching
.CreateSibling (c
.Block
, FlowBranching
.SiblingType
.Catch
);
4776 if (c
.Name
!= null) {
4777 LocalInfo vi
= c
.Block
.GetLocalInfo (c
.Name
);
4779 throw new Exception ();
4781 vi
.VariableInfo
= null;
4784 if (!c
.Resolve (ec
)) {
4789 Type resolved_type
= c
.CatchType
;
4790 for (int ii
= 0; ii
< last_index
; ++ii
) {
4791 if (resolved_type
== prev_catches
[ii
] || TypeManager
.IsSubclassOf (resolved_type
, prev_catches
[ii
])) {
4792 ec
.Report
.Error (160, c
.loc
,
4793 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4794 TypeManager
.CSharpName (prev_catches
[ii
]));
4799 prev_catches
[last_index
++] = resolved_type
;
4802 if (General
!= null) {
4803 if (CodeGen
.Assembly
.WrapNonExceptionThrows
) {
4804 foreach (Catch c
in Specific
){
4805 if (c
.CatchType
== TypeManager
.exception_type
&& PredefinedAttributes
.Get
.RuntimeCompatibility
.IsDefined
) {
4806 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'");
4811 ec
.CurrentBranching
.CreateSibling (General
.Block
, FlowBranching
.SiblingType
.Catch
);
4813 if (!General
.Resolve (ec
))
4817 ec
.EndFlowBranching ();
4819 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4820 // So, ensure there's some IL code after this statement
4821 if (!inside_try_finally
&& !code_follows
&& ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
)
4822 ec
.NeedReturnLabel ();
4827 public void SomeCodeFollows ()
4829 code_follows
= true;
4832 protected override void DoEmit (EmitContext ec
)
4834 ILGenerator ig
= ec
.ig
;
4836 if (!inside_try_finally
)
4837 ig
.BeginExceptionBlock ();
4841 foreach (Catch c
in Specific
)
4844 if (General
!= null)
4847 if (!inside_try_finally
)
4848 ig
.EndExceptionBlock ();
4851 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4853 Block
.MutateHoistedGenericType (storey
);
4855 if (General
!= null)
4856 General
.MutateHoistedGenericType (storey
);
4857 if (Specific
!= null) {
4858 foreach (Catch c
in Specific
)
4859 c
.MutateHoistedGenericType (storey
);
4863 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4865 TryCatch target
= (TryCatch
) t
;
4867 target
.Block
= clonectx
.LookupBlock (Block
);
4868 if (General
!= null)
4869 target
.General
= (Catch
) General
.Clone (clonectx
);
4870 if (Specific
!= null){
4871 target
.Specific
= new List
<Catch
> ();
4872 foreach (Catch c
in Specific
)
4873 target
.Specific
.Add ((Catch
) c
.Clone (clonectx
));
4878 // FIXME: Why is it almost exact copy of Using ??
4879 public class UsingTemporary
: ExceptionStatement
{
4880 TemporaryVariable local_copy
;
4881 public Statement Statement
;
4885 public UsingTemporary (Expression expr
, Statement stmt
, Location l
)
4892 public override bool Resolve (BlockContext ec
)
4894 expr
= expr
.Resolve (ec
);
4898 expr_type
= expr
.Type
;
4900 if (!TypeManager
.ImplementsInterface (expr_type
, TypeManager
.idisposable_type
) &&
4901 Convert
.ImplicitConversion (ec
, expr
, TypeManager
.idisposable_type
, loc
) == null) {
4902 if (!TypeManager
.IsDynamicType (expr_type
)) {
4903 Using
.Error_IsNotConvertibleToIDisposable (ec
, expr
);
4907 expr
= Convert
.ImplicitConversionRequired (ec
, expr
, TypeManager
.idisposable_type
, loc
);
4908 expr_type
= expr
.Type
;
4911 local_copy
= new TemporaryVariable (expr_type
, loc
);
4912 local_copy
.Resolve (ec
);
4914 ec
.StartFlowBranching (this);
4916 bool ok
= Statement
.Resolve (ec
);
4918 ec
.EndFlowBranching ();
4920 ok
&= base.Resolve (ec
);
4922 if (TypeManager
.void_dispose_void
== null) {
4923 TypeManager
.void_dispose_void
= TypeManager
.GetPredefinedMethod (
4924 TypeManager
.idisposable_type
, "Dispose", loc
, Type
.EmptyTypes
);
4930 protected override void EmitPreTryBody (EmitContext ec
)
4932 local_copy
.EmitAssign (ec
, expr
);
4935 protected override void EmitTryBody (EmitContext ec
)
4937 Statement
.Emit (ec
);
4940 protected override void EmitFinallyBody (EmitContext ec
)
4942 ILGenerator ig
= ec
.ig
;
4943 if (!TypeManager
.IsStruct (expr_type
)) {
4944 Label skip
= ig
.DefineLabel ();
4945 local_copy
.Emit (ec
);
4946 ig
.Emit (OpCodes
.Brfalse
, skip
);
4947 local_copy
.Emit (ec
);
4948 ig
.Emit (OpCodes
.Callvirt
, (MethodInfo
) TypeManager
.void_dispose_void
.MetaInfo
);
4949 ig
.MarkLabel (skip
);
4953 Expression ml
= Expression
.MemberLookup (RootContext
.ToplevelTypes
.Compiler
,
4954 ec
.CurrentType
, TypeManager
.idisposable_type
, expr_type
,
4955 "Dispose", Location
.Null
);
4957 if (!(ml
is MethodGroupExpr
)) {
4958 local_copy
.Emit (ec
);
4959 ig
.Emit (OpCodes
.Box
, expr_type
);
4960 ig
.Emit (OpCodes
.Callvirt
, (MethodInfo
) TypeManager
.void_dispose_void
.MetaInfo
);
4964 MethodSpec mi
= null;
4966 foreach (var mk
in ((MethodGroupExpr
) ml
).Methods
) {
4967 if (mk
.Parameters
.IsEmpty
) {
4974 ec
.Report
.Error(-100, Mono
.CSharp
.Location
.Null
, "Internal error: No Dispose method which takes 0 parameters.");
4978 local_copy
.AddressOf (ec
, AddressOp
.Load
);
4979 ig
.Emit (OpCodes
.Call
, (MethodInfo
) mi
.MetaInfo
);
4982 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4984 expr_type
= storey
.MutateType (expr_type
);
4985 local_copy
.MutateHoistedGenericType (storey
);
4986 Statement
.MutateHoistedGenericType (storey
);
4989 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4991 UsingTemporary target
= (UsingTemporary
) t
;
4993 target
.expr
= expr
.Clone (clonectx
);
4994 target
.Statement
= Statement
.Clone (clonectx
);
4998 public class Using
: ExceptionStatement
{
5000 public Statement EmbeddedStatement
{
5001 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
5007 ExpressionStatement assign
;
5009 public Using (Expression
var, Expression init
, Statement stmt
, Location l
)
5017 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec
, Expression expr
)
5019 ec
.Report
.SymbolRelatedToPreviousError (expr
.Type
);
5020 ec
.Report
.Error (1674, expr
.Location
, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5021 TypeManager
.CSharpName (expr
.Type
));
5024 protected override void EmitPreTryBody (EmitContext ec
)
5026 assign
.EmitStatement (ec
);
5029 protected override void EmitTryBody (EmitContext ec
)
5034 protected override void EmitFinallyBody (EmitContext ec
)
5036 ILGenerator ig
= ec
.ig
;
5037 Label skip
= ig
.DefineLabel ();
5039 bool emit_null_check
= !TypeManager
.IsValueType (var.Type
);
5040 if (emit_null_check
) {
5042 ig
.Emit (OpCodes
.Brfalse
, skip
);
5045 Invocation
.EmitCall (ec
, false, var, TypeManager
.void_dispose_void
, null, loc
);
5047 if (emit_null_check
)
5048 ig
.MarkLabel (skip
);
5051 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5053 assign
.MutateHoistedGenericType (storey
);
5054 var.MutateHoistedGenericType (storey
);
5055 stmt
.MutateHoistedGenericType (storey
);
5058 public override bool Resolve (BlockContext ec
)
5060 if (!ResolveVariable (ec
))
5063 ec
.StartFlowBranching (this);
5065 bool ok
= stmt
.Resolve (ec
);
5067 ec
.EndFlowBranching ();
5069 ok
&= base.Resolve (ec
);
5071 if (TypeManager
.void_dispose_void
== null) {
5072 TypeManager
.void_dispose_void
= TypeManager
.GetPredefinedMethod (
5073 TypeManager
.idisposable_type
, "Dispose", loc
, Type
.EmptyTypes
);
5079 bool ResolveVariable (BlockContext ec
)
5081 assign
= new SimpleAssign (var, init
, loc
);
5082 assign
= assign
.ResolveStatement (ec
);
5086 if (assign
.Type
== TypeManager
.idisposable_type
||
5087 TypeManager
.ImplementsInterface (assign
.Type
, TypeManager
.idisposable_type
)) {
5091 Expression e
= Convert
.ImplicitConversionStandard (ec
, assign
, TypeManager
.idisposable_type
, var.Location
);
5093 if (TypeManager
.IsDynamicType (assign
.Type
)) {
5094 e
= Convert
.ImplicitConversionRequired (ec
, assign
, TypeManager
.idisposable_type
, loc
);
5095 var = new TemporaryVariable (e
.Type
, loc
);
5096 assign
= new SimpleAssign (var, e
, loc
).ResolveStatement (ec
);
5100 Error_IsNotConvertibleToIDisposable (ec
, var);
5104 throw new NotImplementedException ("covariance?");
5107 protected override void CloneTo (CloneContext clonectx
, Statement t
)
5109 Using target
= (Using
) t
;
5111 target
.var = var.Clone (clonectx
);
5112 target
.init
= init
.Clone (clonectx
);
5113 target
.stmt
= stmt
.Clone (clonectx
);
5118 /// Implementation of the foreach C# statement
5120 public class Foreach
: Statement
{
5122 sealed class ArrayForeach
: Statement
5124 class ArrayCounter
: TemporaryVariable
5126 StatementExpression increment
;
5128 public ArrayCounter (Location loc
)
5129 : base (TypeManager
.int32_type
, loc
)
5133 public void ResolveIncrement (BlockContext ec
)
5135 increment
= new StatementExpression (new UnaryMutator (UnaryMutator
.Mode
.PostIncrement
, this, loc
));
5136 increment
.Resolve (ec
);
5139 public void EmitIncrement (EmitContext ec
)
5141 increment
.Emit (ec
);
5145 readonly Foreach for_each
;
5146 readonly Statement statement
;
5149 TemporaryVariable
[] lengths
;
5150 Expression
[] length_exprs
;
5151 ArrayCounter
[] counter
;
5153 TemporaryVariable copy
;
5156 public ArrayForeach (Foreach
@foreach, int rank
)
5158 for_each
= @foreach;
5159 statement
= for_each
.statement
;
5162 counter
= new ArrayCounter
[rank
];
5163 length_exprs
= new Expression
[rank
];
5166 // Only use temporary length variables when dealing with
5167 // multi-dimensional arrays
5170 lengths
= new TemporaryVariable
[rank
];
5173 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5175 throw new NotImplementedException ();
5178 public override bool Resolve (BlockContext ec
)
5180 copy
= new TemporaryVariable (for_each
.expr
.Type
, loc
);
5183 int rank
= length_exprs
.Length
;
5184 Arguments list
= new Arguments (rank
);
5185 for (int i
= 0; i
< rank
; i
++) {
5186 counter
[i
] = new ArrayCounter (loc
);
5187 counter
[i
].ResolveIncrement (ec
);
5190 length_exprs
[i
] = new MemberAccess (copy
, "Length").Resolve (ec
);
5192 lengths
[i
] = new TemporaryVariable (TypeManager
.int32_type
, loc
);
5193 lengths
[i
].Resolve (ec
);
5195 Arguments args
= new Arguments (1);
5196 args
.Add (new Argument (new IntConstant (i
, loc
)));
5197 length_exprs
[i
] = new Invocation (new MemberAccess (copy
, "GetLength"), args
).Resolve (ec
);
5200 list
.Add (new Argument (counter
[i
]));
5203 access
= new ElementAccess (copy
, list
).Resolve (ec
);
5207 Expression var_type
= for_each
.type
;
5208 VarExpr ve
= var_type
as VarExpr
;
5210 // Infer implicitly typed local variable from foreach array type
5211 var_type
= new TypeExpression (access
.Type
, ve
.Location
);
5214 var_type
= var_type
.ResolveAsTypeTerminal (ec
, false);
5215 if (var_type
== null)
5218 conv
= Convert
.ExplicitConversion (ec
, access
, var_type
.Type
, loc
);
5224 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
5225 ec
.CurrentBranching
.CreateSibling ();
5227 for_each
.variable
= for_each
.variable
.ResolveLValue (ec
, conv
);
5228 if (for_each
.variable
== null)
5231 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
5232 if (!statement
.Resolve (ec
))
5234 ec
.EndFlowBranching ();
5236 // There's no direct control flow from the end of the embedded statement to the end of the loop
5237 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
5239 ec
.EndFlowBranching ();
5244 protected override void DoEmit (EmitContext ec
)
5246 ILGenerator ig
= ec
.ig
;
5248 copy
.EmitAssign (ec
, for_each
.expr
);
5250 int rank
= length_exprs
.Length
;
5251 Label
[] test
= new Label
[rank
];
5252 Label
[] loop
= new Label
[rank
];
5254 for (int i
= 0; i
< rank
; i
++) {
5255 test
[i
] = ig
.DefineLabel ();
5256 loop
[i
] = ig
.DefineLabel ();
5258 if (lengths
!= null)
5259 lengths
[i
].EmitAssign (ec
, length_exprs
[i
]);
5262 IntConstant zero
= new IntConstant (0, loc
);
5263 for (int i
= 0; i
< rank
; i
++) {
5264 counter
[i
].EmitAssign (ec
, zero
);
5266 ig
.Emit (OpCodes
.Br
, test
[i
]);
5267 ig
.MarkLabel (loop
[i
]);
5270 ((IAssignMethod
) for_each
.variable
).EmitAssign (ec
, conv
, false, false);
5272 statement
.Emit (ec
);
5274 ig
.MarkLabel (ec
.LoopBegin
);
5276 for (int i
= rank
- 1; i
>= 0; i
--){
5277 counter
[i
].EmitIncrement (ec
);
5279 ig
.MarkLabel (test
[i
]);
5280 counter
[i
].Emit (ec
);
5282 if (lengths
!= null)
5283 lengths
[i
].Emit (ec
);
5285 length_exprs
[i
].Emit (ec
);
5287 ig
.Emit (OpCodes
.Blt
, loop
[i
]);
5290 ig
.MarkLabel (ec
.LoopEnd
);
5293 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5295 for_each
.expr
.MutateHoistedGenericType (storey
);
5297 copy
.MutateHoistedGenericType (storey
);
5298 conv
.MutateHoistedGenericType (storey
);
5299 statement
.MutateHoistedGenericType (storey
);
5301 for (int i
= 0; i
< counter
.Length
; i
++) {
5302 counter
[i
].MutateHoistedGenericType (storey
);
5303 if (lengths
!= null)
5304 lengths
[i
].MutateHoistedGenericType (storey
);
5309 sealed class CollectionForeach
: Statement
5311 class CollectionForeachStatement
: Statement
5314 Expression variable
, current
, conv
;
5315 Statement statement
;
5318 public CollectionForeachStatement (Type type
, Expression variable
,
5319 Expression current
, Statement statement
,
5323 this.variable
= variable
;
5324 this.current
= current
;
5325 this.statement
= statement
;
5329 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5331 throw new NotImplementedException ();
5334 public override bool Resolve (BlockContext ec
)
5336 current
= current
.Resolve (ec
);
5337 if (current
== null)
5340 conv
= Convert
.ExplicitConversion (ec
, current
, type
, loc
);
5344 assign
= new SimpleAssign (variable
, conv
, loc
);
5345 if (assign
.Resolve (ec
) == null)
5348 if (!statement
.Resolve (ec
))
5354 protected override void DoEmit (EmitContext ec
)
5356 assign
.EmitStatement (ec
);
5357 statement
.Emit (ec
);
5360 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5362 assign
.MutateHoistedGenericType (storey
);
5363 statement
.MutateHoistedGenericType (storey
);
5367 Expression variable
, expr
;
5368 Statement statement
;
5370 TemporaryVariable enumerator
;
5375 MethodGroupExpr get_enumerator
;
5376 PropertyExpr get_current
;
5377 MethodSpec move_next
;
5378 Expression var_type
;
5379 Type enumerator_type
;
5380 bool enumerator_found
;
5382 public CollectionForeach (Expression var_type
, Expression
var,
5383 Expression expr
, Statement stmt
, Location l
)
5385 this.var_type
= var_type
;
5386 this.variable
= var;
5392 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5394 throw new NotImplementedException ();
5397 bool GetEnumeratorFilter (ResolveContext ec
, MethodSpec mi
)
5399 Type return_type
= mi
.ReturnType
;
5402 // Ok, we can access it, now make sure that we can do something
5403 // with this `GetEnumerator'
5406 if (return_type
== TypeManager
.ienumerator_type
||
5407 TypeManager
.ImplementsInterface (return_type
, TypeManager
.ienumerator_type
)) {
5409 // If it is not an interface, lets try to find the methods ourselves.
5410 // For example, if we have:
5411 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5412 // We can avoid the iface call. This is a runtime perf boost.
5413 // even bigger if we have a ValueType, because we avoid the cost
5416 // We have to make sure that both methods exist for us to take
5417 // this path. If one of the methods does not exist, we will just
5418 // use the interface. Sadly, this complex if statement is the only
5419 // way I could do this without a goto
5422 if (TypeManager
.bool_movenext_void
== null) {
5423 TypeManager
.bool_movenext_void
= TypeManager
.GetPredefinedMethod (
5424 TypeManager
.ienumerator_type
, "MoveNext", loc
, Type
.EmptyTypes
);
5427 if (TypeManager
.ienumerator_getcurrent
== null) {
5428 TypeManager
.ienumerator_getcurrent
= TypeManager
.GetPredefinedProperty (
5429 TypeManager
.ienumerator_type
, "Current", loc
, TypeManager
.object_type
);
5433 // Prefer a generic enumerator over a non-generic one.
5435 if (return_type
.IsInterface
&& TypeManager
.IsGenericType (return_type
)) {
5436 enumerator_type
= return_type
;
5437 if (!FetchGetCurrent (ec
, return_type
))
5438 get_current
= new PropertyExpr (
5439 ec
.CurrentType
, TypeManager
.ienumerator_getcurrent
, loc
);
5440 if (!FetchMoveNext (return_type
))
5441 move_next
= TypeManager
.bool_movenext_void
;
5445 if (return_type
.IsInterface
||
5446 !FetchMoveNext (return_type
) ||
5447 !FetchGetCurrent (ec
, return_type
)) {
5448 enumerator_type
= return_type
;
5449 move_next
= TypeManager
.bool_movenext_void
;
5450 get_current
= new PropertyExpr (
5451 ec
.CurrentType
, TypeManager
.ienumerator_getcurrent
, loc
);
5456 // Ok, so they dont return an IEnumerable, we will have to
5457 // find if they support the GetEnumerator pattern.
5460 if (TypeManager
.HasElementType (return_type
) || !FetchMoveNext (return_type
) || !FetchGetCurrent (ec
, return_type
)) {
5461 ec
.Report
.Error (202, loc
, "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5462 TypeManager
.CSharpName (return_type
), TypeManager
.CSharpSignature (mi
));
5467 enumerator_type
= return_type
;
5473 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5475 bool FetchMoveNext (Type t
)
5477 MemberInfo
[] move_next_list
= TypeManager
.MemberLookup (null, null, t
,
5479 BindingFlags
.Public
| BindingFlags
.Instance
,
5482 if (move_next_list
== null)
5485 foreach (MemberInfo m
in move_next_list
){
5486 MethodInfo mi
= (MethodInfo
) m
;
5488 if ((TypeManager
.GetParameterData (mi
).Count
== 0) &&
5489 TypeManager
.TypeToCoreType (mi
.ReturnType
) == TypeManager
.bool_type
) {
5490 move_next
= Import
.CreateMethod (mi
);
5499 // Retrieves a `public T get_Current ()' method from the Type `t'
5501 bool FetchGetCurrent (ResolveContext ec
, Type t
)
5503 PropertyExpr pe
= Expression
.MemberLookup (ec
.Compiler
,
5504 ec
.CurrentType
, t
, "Current", MemberTypes
.Property
,
5505 Expression
.AllBindingFlags
, loc
) as PropertyExpr
;
5513 void Error_Enumerator (BlockContext ec
)
5515 if (enumerator_found
) {
5519 ec
.Report
.Error (1579, loc
,
5520 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5521 TypeManager
.CSharpName (expr
.Type
));
5524 bool IsOverride (MethodSpec ms
)
5526 MethodInfo m
= (MethodInfo
) ms
.MetaInfo
;
5527 m
= (MethodInfo
) TypeManager
.DropGenericMethodArguments (m
);
5529 if (!m
.IsVirtual
|| ((m
.Attributes
& MethodAttributes
.NewSlot
) != 0))
5531 if (m
is MethodBuilder
)
5534 MethodInfo base_method
= m
.GetBaseDefinition ();
5535 return base_method
!= m
;
5538 bool TryType (ResolveContext ec
, Type t
)
5540 MethodGroupExpr mg
= Expression
.MemberLookup (ec
.Compiler
,
5541 ec
.CurrentType
, t
, "GetEnumerator", MemberTypes
.Method
,
5542 Expression
.AllBindingFlags
, loc
) as MethodGroupExpr
;
5546 MethodSpec result
= null;
5547 MethodSpec tmp_move_next
= null;
5548 PropertyExpr tmp_get_cur
= null;
5549 Type tmp_enumerator_type
= enumerator_type
;
5550 foreach (var mi
in mg
.Methods
) {
5551 if (!mi
.Parameters
.IsEmpty
)
5554 // Check whether GetEnumerator is public
5555 if ((mi
.MetaInfo
.Attributes
& MethodAttributes
.Public
) != MethodAttributes
.Public
)
5558 if (IsOverride (mi
))
5561 enumerator_found
= true;
5563 if (!GetEnumeratorFilter (ec
, mi
))
5566 if (result
!= null) {
5567 if (TypeManager
.IsGenericType (result
.ReturnType
)) {
5568 if (!TypeManager
.IsGenericType (mi
.ReturnType
))
5571 MethodBase mb
= TypeManager
.DropGenericMethodArguments (mi
);
5572 ec
.Report
.SymbolRelatedToPreviousError (t
);
5573 ec
.Report
.Error(1640, loc
, "foreach statement cannot operate on variables of type `{0}' " +
5574 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5575 TypeManager
.CSharpName (t
), TypeManager
.CSharpSignature (mb
));
5579 // Always prefer generics enumerators
5580 if (!TypeManager
.IsGenericType (mi
.ReturnType
)) {
5581 if (TypeManager
.ImplementsInterface (mi
.DeclaringType
, result
.DeclaringType
) ||
5582 TypeManager
.ImplementsInterface (result
.DeclaringType
, mi
.DeclaringType
))
5585 ec
.Report
.SymbolRelatedToPreviousError (result
.MetaInfo
);
5586 ec
.Report
.SymbolRelatedToPreviousError (mi
.MetaInfo
);
5587 ec
.Report
.Warning (278, 2, loc
, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5588 TypeManager
.CSharpName (t
), "enumerable", TypeManager
.CSharpSignature (result
.MetaInfo
), TypeManager
.CSharpSignature (mi
.MetaInfo
));
5593 tmp_move_next
= move_next
;
5594 tmp_get_cur
= get_current
;
5595 tmp_enumerator_type
= enumerator_type
;
5596 if (mi
.DeclaringType
== t
)
5600 if (result
!= null) {
5601 move_next
= tmp_move_next
;
5602 get_current
= tmp_get_cur
;
5603 enumerator_type
= tmp_enumerator_type
;
5604 var mi
= new [] { result }
;
5605 get_enumerator
= new MethodGroupExpr (mi
, enumerator_type
, loc
);
5607 if (t
!= expr
.Type
) {
5608 expr
= Convert
.ExplicitConversion (
5611 throw new InternalErrorException ();
5614 get_enumerator
.InstanceExpression
= expr
;
5615 get_enumerator
.IsBase
= t
!= expr
.Type
;
5623 bool ProbeCollectionType (ResolveContext ec
, Type t
)
5625 int errors
= ec
.Report
.Errors
;
5626 for (Type tt
= t
; tt
!= null && tt
!= TypeManager
.object_type
;){
5627 if (TryType (ec
, tt
))
5632 if (ec
.Report
.Errors
> errors
)
5636 // Now try to find the method in the interfaces
5638 Type
[] ifaces
= TypeManager
.GetInterfaces (t
);
5639 foreach (Type i
in ifaces
){
5640 if (TryType (ec
, i
))
5647 public override bool Resolve (BlockContext ec
)
5649 enumerator_type
= TypeManager
.ienumerator_type
;
5651 bool is_dynamic
= TypeManager
.IsDynamicType (expr
.Type
);
5653 expr
= Convert
.ImplicitConversionRequired (ec
, expr
, TypeManager
.ienumerable_type
, loc
);
5655 if (!ProbeCollectionType (ec
, expr
.Type
)) {
5656 Error_Enumerator (ec
);
5660 VarExpr ve
= var_type
as VarExpr
;
5662 // Infer implicitly typed local variable from foreach enumerable type
5663 var_type
= new TypeExpression (
5664 is_dynamic
? InternalType
.Dynamic
: get_current
.PropertyInfo
.PropertyType
,
5668 var_type
= var_type
.ResolveAsTypeTerminal (ec
, false);
5669 if (var_type
== null)
5672 enumerator
= new TemporaryVariable (enumerator_type
, loc
);
5673 enumerator
.Resolve (ec
);
5675 init
= new Invocation (get_enumerator
, null);
5676 init
= init
.Resolve (ec
);
5680 Expression move_next_expr
;
5682 var mi
= new [] { move_next }
;
5683 MethodGroupExpr mg
= new MethodGroupExpr (mi
, var_type
.Type
, loc
);
5684 mg
.InstanceExpression
= enumerator
;
5686 move_next_expr
= new Invocation (mg
, null);
5689 get_current
.InstanceExpression
= enumerator
;
5691 Statement block
= new CollectionForeachStatement (
5692 var_type
.Type
, variable
, get_current
, statement
, loc
);
5694 loop
= new While (new BooleanExpression (move_next_expr
), block
, loc
);
5697 bool implements_idisposable
= TypeManager
.ImplementsInterface (enumerator_type
, TypeManager
.idisposable_type
);
5698 if (implements_idisposable
|| !enumerator_type
.IsSealed
) {
5699 wrapper
= new DisposableWrapper (this, implements_idisposable
);
5701 wrapper
= new NonDisposableWrapper (this);
5704 return wrapper
.Resolve (ec
);
5707 protected override void DoEmit (EmitContext ec
)
5712 class NonDisposableWrapper
: Statement
{
5713 CollectionForeach parent
;
5715 internal NonDisposableWrapper (CollectionForeach parent
)
5717 this.parent
= parent
;
5720 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5722 throw new NotSupportedException ();
5725 public override bool Resolve (BlockContext ec
)
5727 return parent
.ResolveLoop (ec
);
5730 protected override void DoEmit (EmitContext ec
)
5732 parent
.EmitLoopInit (ec
);
5733 parent
.EmitLoopBody (ec
);
5736 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5738 throw new NotSupportedException ();
5742 sealed class DisposableWrapper
: ExceptionStatement
5744 CollectionForeach parent
;
5745 bool implements_idisposable
;
5747 internal DisposableWrapper (CollectionForeach parent
, bool implements
)
5749 this.parent
= parent
;
5750 this.implements_idisposable
= implements
;
5753 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5755 throw new NotSupportedException ();
5758 public override bool Resolve (BlockContext ec
)
5762 ec
.StartFlowBranching (this);
5764 if (!parent
.ResolveLoop (ec
))
5767 ec
.EndFlowBranching ();
5769 ok
&= base.Resolve (ec
);
5771 if (TypeManager
.void_dispose_void
== null) {
5772 TypeManager
.void_dispose_void
= TypeManager
.GetPredefinedMethod (
5773 TypeManager
.idisposable_type
, "Dispose", loc
, Type
.EmptyTypes
);
5778 protected override void EmitPreTryBody (EmitContext ec
)
5780 parent
.EmitLoopInit (ec
);
5783 protected override void EmitTryBody (EmitContext ec
)
5785 parent
.EmitLoopBody (ec
);
5788 protected override void EmitFinallyBody (EmitContext ec
)
5790 Expression instance
= parent
.enumerator
;
5791 if (!TypeManager
.IsValueType (parent
.enumerator_type
)) {
5792 ILGenerator ig
= ec
.ig
;
5794 parent
.enumerator
.Emit (ec
);
5796 Label call_dispose
= ig
.DefineLabel ();
5798 if (!implements_idisposable
) {
5799 ec
.ig
.Emit (OpCodes
.Isinst
, TypeManager
.idisposable_type
);
5800 LocalTemporary temp
= new LocalTemporary (TypeManager
.idisposable_type
);
5806 ig
.Emit (OpCodes
.Brtrue_S
, call_dispose
);
5808 // using 'endfinally' to empty the evaluation stack
5809 ig
.Emit (OpCodes
.Endfinally
);
5810 ig
.MarkLabel (call_dispose
);
5813 Invocation
.EmitCall (ec
, false, instance
, TypeManager
.void_dispose_void
, null, loc
);
5816 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5818 throw new NotSupportedException ();
5822 bool ResolveLoop (BlockContext ec
)
5824 return loop
.Resolve (ec
);
5827 void EmitLoopInit (EmitContext ec
)
5829 enumerator
.EmitAssign (ec
, init
);
5832 void EmitLoopBody (EmitContext ec
)
5837 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5839 enumerator_type
= storey
.MutateType (enumerator_type
);
5840 init
.MutateHoistedGenericType (storey
);
5841 loop
.MutateHoistedGenericType (storey
);
5846 Expression variable
;
5848 Statement statement
;
5850 public Foreach (Expression type
, LocalVariableReference
var, Expression expr
,
5851 Statement stmt
, Location l
)
5854 this.variable
= var;
5860 public Statement Statement
{
5861 get { return statement; }
5864 public override bool Resolve (BlockContext ec
)
5866 expr
= expr
.Resolve (ec
);
5871 ec
.Report
.Error (186, loc
, "Use of null is not valid in this context");
5875 if (expr
.Type
== TypeManager
.string_type
) {
5876 statement
= new ArrayForeach (this, 1);
5877 } else if (expr
.Type
.IsArray
) {
5878 statement
= new ArrayForeach (this, expr
.Type
.GetArrayRank ());
5880 if (expr
.eclass
== ExprClass
.MethodGroup
|| expr
is AnonymousMethodExpression
) {
5881 ec
.Report
.Error (446, expr
.Location
, "Foreach statement cannot operate on a `{0}'",
5882 expr
.ExprClassName
);
5886 statement
= new CollectionForeach (type
, variable
, expr
, statement
, loc
);
5889 return statement
.Resolve (ec
);
5892 protected override void DoEmit (EmitContext ec
)
5894 ILGenerator ig
= ec
.ig
;
5896 Label old_begin
= ec
.LoopBegin
, old_end
= ec
.LoopEnd
;
5897 ec
.LoopBegin
= ig
.DefineLabel ();
5898 ec
.LoopEnd
= ig
.DefineLabel ();
5900 statement
.Emit (ec
);
5902 ec
.LoopBegin
= old_begin
;
5903 ec
.LoopEnd
= old_end
;
5906 protected override void CloneTo (CloneContext clonectx
, Statement t
)
5908 Foreach target
= (Foreach
) t
;
5910 target
.type
= type
.Clone (clonectx
);
5911 target
.variable
= variable
.Clone (clonectx
);
5912 target
.expr
= expr
.Clone (clonectx
);
5913 target
.statement
= statement
.Clone (clonectx
);
5916 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5918 statement
.MutateHoistedGenericType (storey
);