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