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@gmail.com)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
15 using System
.Collections
.Generic
;
18 using IKVM
.Reflection
.Emit
;
20 using System
.Reflection
.Emit
;
23 namespace Mono
.CSharp
{
25 public abstract class Statement
{
27 protected bool reachable
;
29 public bool IsUnreachable
{
36 /// Resolves the statement, true means that all sub-statements
39 public virtual bool Resolve (BlockContext bc
)
45 /// Return value indicates whether all code paths emitted return.
47 protected abstract void DoEmit (EmitContext ec
);
49 public virtual void Emit (EmitContext ec
)
54 if (ec
.StatementEpilogue
!= null) {
60 // This routine must be overrided in derived classes and make copies
61 // of all the data that might be modified if resolved
63 protected abstract void CloneTo (CloneContext clonectx
, Statement target
);
65 public Statement
Clone (CloneContext clonectx
)
67 Statement s
= (Statement
) this.MemberwiseClone ();
68 CloneTo (clonectx
, s
);
72 public virtual Expression
CreateExpressionTree (ResolveContext ec
)
74 ec
.Report
.Error (834, loc
, "A lambda expression with statement body cannot be converted to an expresion tree");
78 public virtual object Accept (StructuralVisitor visitor
)
80 return visitor
.Visit (this);
84 // Return value indicates whether statement has unreachable end
86 protected abstract bool DoFlowAnalysis (FlowAnalysisContext fc
);
88 public bool FlowAnalysis (FlowAnalysisContext fc
)
91 fc
.UnreachableReported
= false;
92 var res
= DoFlowAnalysis (fc
);
97 // Special handling cases
100 return DoFlowAnalysis (fc
);
103 if (this is EmptyStatement
|| loc
.IsNull
)
106 if (fc
.UnreachableReported
)
109 fc
.Report
.Warning (162, 2, loc
, "Unreachable code detected");
110 fc
.UnreachableReported
= true;
114 public virtual Reachability
MarkReachable (Reachability rc
)
116 if (!rc
.IsUnreachable
)
122 protected void CheckExitBoundaries (BlockContext bc
, Block scope
)
124 if (bc
.CurrentBlock
.ParametersBlock
.Original
!= scope
.ParametersBlock
.Original
) {
125 bc
.Report
.Error (1632, loc
, "Control cannot leave the body of an anonymous method");
129 for (var b
= bc
.CurrentBlock
; b
!= null && b
!= scope
; b
= b
.Parent
) {
130 if (b
.IsFinallyBlock
) {
131 Error_FinallyClauseExit (bc
);
137 protected void Error_FinallyClauseExit (BlockContext bc
)
139 bc
.Report
.Error (157, loc
, "Control cannot leave the body of a finally clause");
143 public sealed class EmptyStatement
: Statement
145 public EmptyStatement (Location loc
)
150 public override bool Resolve (BlockContext ec
)
155 public override void Emit (EmitContext ec
)
159 protected override void DoEmit (EmitContext ec
)
161 throw new NotSupportedException ();
164 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
169 protected override void CloneTo (CloneContext clonectx
, Statement target
)
174 public override object Accept (StructuralVisitor visitor
)
176 return visitor
.Visit (this);
180 public class If
: Statement
{
182 public Statement TrueStatement
;
183 public Statement FalseStatement
;
185 bool true_returns
, false_returns
;
187 public If (Expression bool_expr
, Statement true_statement
, Location l
)
188 : this (bool_expr
, true_statement
, null, l
)
192 public If (Expression bool_expr
,
193 Statement true_statement
,
194 Statement false_statement
,
197 this.expr
= bool_expr
;
198 TrueStatement
= true_statement
;
199 FalseStatement
= false_statement
;
203 public Expression Expr
{
209 public override bool Resolve (BlockContext ec
)
211 expr
= expr
.Resolve (ec
);
213 var ok
= TrueStatement
.Resolve (ec
);
215 if (FalseStatement
!= null) {
216 ok
&= FalseStatement
.Resolve (ec
);
222 protected override void DoEmit (EmitContext ec
)
224 Label false_target
= ec
.DefineLabel ();
228 // If we're a boolean constant, Resolve() already
229 // eliminated dead code for us.
231 Constant c
= expr
as Constant
;
233 c
.EmitSideEffect (ec
);
235 if (!c
.IsDefaultValue
)
236 TrueStatement
.Emit (ec
);
237 else if (FalseStatement
!= null)
238 FalseStatement
.Emit (ec
);
243 expr
.EmitBranchable (ec
, false_target
, false);
245 TrueStatement
.Emit (ec
);
247 if (FalseStatement
!= null){
248 bool branch_emitted
= false;
250 end
= ec
.DefineLabel ();
252 ec
.Emit (OpCodes
.Br
, end
);
253 branch_emitted
= true;
256 ec
.MarkLabel (false_target
);
257 FalseStatement
.Emit (ec
);
262 ec
.MarkLabel (false_target
);
266 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
268 expr
.FlowAnalysisConditional (fc
);
270 var da_false
= new DefiniteAssignmentBitSet (fc
.DefiniteAssignmentOnFalse
);
272 fc
.BranchDefiniteAssignment (fc
.DefiniteAssignmentOnTrue
);
273 var labels
= fc
.CopyLabelStack ();
275 var res
= TrueStatement
.FlowAnalysis (fc
);
277 fc
.SetLabelStack (labels
);
279 if (FalseStatement
== null) {
281 var c
= expr
as Constant
;
282 if (c
!= null && !c
.IsDefaultValue
)
286 fc
.DefiniteAssignment
= da_false
;
288 fc
.DefiniteAssignment
&= da_false
;
294 fc
.DefiniteAssignment
= da_false
;
296 res
= FalseStatement
.FlowAnalysis (fc
);
297 fc
.SetLabelStack (labels
);
301 var da_true
= fc
.DefiniteAssignment
;
303 fc
.DefiniteAssignment
= da_false
;
305 res
&= FalseStatement
.FlowAnalysis (fc
);
307 fc
.SetLabelStack (labels
);
309 if (!TrueStatement
.IsUnreachable
) {
310 if (false_returns
|| FalseStatement
.IsUnreachable
)
311 fc
.DefiniteAssignment
= da_true
;
313 fc
.DefiniteAssignment
&= da_true
;
319 public override Reachability
MarkReachable (Reachability rc
)
321 if (rc
.IsUnreachable
)
324 base.MarkReachable (rc
);
326 var c
= expr
as Constant
;
328 bool take
= !c
.IsDefaultValue
;
330 rc
= TrueStatement
.MarkReachable (rc
);
332 if (FalseStatement
!= null)
333 rc
= FalseStatement
.MarkReachable (rc
);
339 var true_rc
= TrueStatement
.MarkReachable (rc
);
340 true_returns
= true_rc
.IsUnreachable
;
342 if (FalseStatement
== null)
345 var false_rc
= FalseStatement
.MarkReachable (rc
);
346 false_returns
= false_rc
.IsUnreachable
;
348 return true_rc
& false_rc
;
351 protected override void CloneTo (CloneContext clonectx
, Statement t
)
355 target
.expr
= expr
.Clone (clonectx
);
356 target
.TrueStatement
= TrueStatement
.Clone (clonectx
);
357 if (FalseStatement
!= null)
358 target
.FalseStatement
= FalseStatement
.Clone (clonectx
);
361 public override object Accept (StructuralVisitor visitor
)
363 return visitor
.Visit (this);
367 public class Do
: LoopStatement
369 public Expression expr
;
370 bool iterator_reachable
, end_reachable
;
372 public Do (Statement statement
, BooleanExpression bool_expr
, Location doLocation
, Location whileLocation
)
377 WhileLocation
= whileLocation
;
380 public Location WhileLocation
{
384 public override bool Resolve (BlockContext bc
)
386 var ok
= base.Resolve (bc
);
388 expr
= expr
.Resolve (bc
);
393 protected override void DoEmit (EmitContext ec
)
395 Label loop
= ec
.DefineLabel ();
396 Label old_begin
= ec
.LoopBegin
;
397 Label old_end
= ec
.LoopEnd
;
399 ec
.LoopBegin
= ec
.DefineLabel ();
400 ec
.LoopEnd
= ec
.DefineLabel ();
404 ec
.MarkLabel (ec
.LoopBegin
);
406 // Mark start of while condition
407 ec
.Mark (WhileLocation
);
410 // Dead code elimination
412 if (expr
is Constant
) {
413 bool res
= !((Constant
) expr
).IsDefaultValue
;
415 expr
.EmitSideEffect (ec
);
417 ec
.Emit (OpCodes
.Br
, loop
);
419 expr
.EmitBranchable (ec
, loop
, true);
422 ec
.MarkLabel (ec
.LoopEnd
);
424 ec
.LoopBegin
= old_begin
;
425 ec
.LoopEnd
= old_end
;
428 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
430 var res
= Statement
.FlowAnalysis (fc
);
432 expr
.FlowAnalysisConditional (fc
);
434 fc
.DefiniteAssignment
= fc
.DefiniteAssignmentOnFalse
;
436 if (res
&& !iterator_reachable
)
437 return !end_reachable
;
439 if (!end_reachable
) {
440 var c
= expr
as Constant
;
441 if (c
!= null && !c
.IsDefaultValue
)
448 public override Reachability
MarkReachable (Reachability rc
)
450 base.MarkReachable (rc
);
452 var body_rc
= Statement
.MarkReachable (rc
);
454 if (body_rc
.IsUnreachable
&& !iterator_reachable
) {
455 expr
= new UnreachableExpression (expr
);
456 return end_reachable
? rc
: Reachability
.CreateUnreachable ();
459 if (!end_reachable
) {
460 var c
= expr
as Constant
;
461 if (c
!= null && !c
.IsDefaultValue
)
462 return Reachability
.CreateUnreachable ();
468 protected override void CloneTo (CloneContext clonectx
, Statement t
)
472 target
.Statement
= Statement
.Clone (clonectx
);
473 target
.expr
= expr
.Clone (clonectx
);
476 public override object Accept (StructuralVisitor visitor
)
478 return visitor
.Visit (this);
481 public override void SetEndReachable ()
483 end_reachable
= true;
486 public override void SetIteratorReachable ()
488 iterator_reachable
= true;
492 public class While
: LoopStatement
494 public Expression expr
;
495 bool empty
, infinite
, end_reachable
;
496 List
<DefiniteAssignmentBitSet
> end_reachable_das
;
498 public While (BooleanExpression bool_expr
, Statement statement
, Location l
)
501 this.expr
= bool_expr
;
505 public override bool Resolve (BlockContext bc
)
509 expr
= expr
.Resolve (bc
);
513 var c
= expr
as Constant
;
515 empty
= c
.IsDefaultValue
;
519 ok
&= base.Resolve (bc
);
523 protected override void DoEmit (EmitContext ec
)
526 expr
.EmitSideEffect (ec
);
530 Label old_begin
= ec
.LoopBegin
;
531 Label old_end
= ec
.LoopEnd
;
533 ec
.LoopBegin
= ec
.DefineLabel ();
534 ec
.LoopEnd
= ec
.DefineLabel ();
537 // Inform whether we are infinite or not
539 if (expr
is Constant
) {
540 // expr is 'true', since the 'empty' case above handles the 'false' case
541 ec
.MarkLabel (ec
.LoopBegin
);
543 if (ec
.EmitAccurateDebugInfo
)
544 ec
.Emit (OpCodes
.Nop
);
546 expr
.EmitSideEffect (ec
);
548 ec
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
551 // Inform that we are infinite (ie, `we return'), only
552 // if we do not `break' inside the code.
554 ec
.MarkLabel (ec
.LoopEnd
);
556 Label while_loop
= ec
.DefineLabel ();
558 ec
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
559 ec
.MarkLabel (while_loop
);
561 expr
.EmitPrepare (ec
);
565 ec
.MarkLabel (ec
.LoopBegin
);
568 expr
.EmitBranchable (ec
, while_loop
, true);
570 ec
.MarkLabel (ec
.LoopEnd
);
573 ec
.LoopBegin
= old_begin
;
574 ec
.LoopEnd
= old_end
;
577 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
579 expr
.FlowAnalysisConditional (fc
);
581 var da_false
= new DefiniteAssignmentBitSet (fc
.DefiniteAssignmentOnFalse
);
583 fc
.BranchDefiniteAssignment (fc
.DefiniteAssignmentOnTrue
);
585 Statement
.FlowAnalysis (fc
);
588 // Special case infinite while with breaks
590 if (end_reachable_das
!= null) {
591 da_false
= DefiniteAssignmentBitSet
.And (end_reachable_das
);
592 end_reachable_das
= null;
595 fc
.DefiniteAssignment
= da_false
;
597 if (infinite
&& !end_reachable
)
603 public override Reachability
MarkReachable (Reachability rc
)
605 if (rc
.IsUnreachable
)
608 base.MarkReachable (rc
);
611 // Special case unreachable while body
614 Statement
.MarkReachable (Reachability
.CreateUnreachable ());
618 Statement
.MarkReachable (rc
);
621 // When infinite while end is unreachable via break anything what follows is unreachable too
623 if (infinite
&& !end_reachable
)
624 return Reachability
.CreateUnreachable ();
629 protected override void CloneTo (CloneContext clonectx
, Statement t
)
631 While target
= (While
) t
;
633 target
.expr
= expr
.Clone (clonectx
);
634 target
.Statement
= Statement
.Clone (clonectx
);
637 public override object Accept (StructuralVisitor visitor
)
639 return visitor
.Visit (this);
642 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc
)
647 if (end_reachable_das
== null)
648 end_reachable_das
= new List
<DefiniteAssignmentBitSet
> ();
650 end_reachable_das
.Add (fc
.DefiniteAssignment
);
653 public override void SetEndReachable ()
655 end_reachable
= true;
659 public class For
: LoopStatement
661 bool infinite
, empty
, iterator_reachable
, end_reachable
;
662 List
<DefiniteAssignmentBitSet
> end_reachable_das
;
664 public For (Location l
)
670 public Statement Initializer
{
674 public Expression Condition
{
678 public Statement Iterator
{
682 public override bool Resolve (BlockContext bc
)
684 Initializer
.Resolve (bc
);
686 if (Condition
!= null) {
687 Condition
= Condition
.Resolve (bc
);
688 var condition_constant
= Condition
as Constant
;
689 if (condition_constant
!= null) {
690 if (condition_constant
.IsDefaultValue
) {
700 return base.Resolve (bc
) && Iterator
.Resolve (bc
);
703 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
705 Initializer
.FlowAnalysis (fc
);
707 DefiniteAssignmentBitSet da_false
;
708 if (Condition
!= null) {
709 Condition
.FlowAnalysisConditional (fc
);
710 fc
.DefiniteAssignment
= fc
.DefiniteAssignmentOnTrue
;
711 da_false
= new DefiniteAssignmentBitSet (fc
.DefiniteAssignmentOnFalse
);
713 da_false
= fc
.BranchDefiniteAssignment ();
716 Statement
.FlowAnalysis (fc
);
718 Iterator
.FlowAnalysis (fc
);
721 // Special case infinite for with breaks
723 if (end_reachable_das
!= null) {
724 da_false
= DefiniteAssignmentBitSet
.And (end_reachable_das
);
725 end_reachable_das
= null;
728 fc
.DefiniteAssignment
= da_false
;
730 if (infinite
&& !end_reachable
)
736 public override Reachability
MarkReachable (Reachability rc
)
738 base.MarkReachable (rc
);
740 Initializer
.MarkReachable (rc
);
742 var body_rc
= Statement
.MarkReachable (rc
);
743 if (!body_rc
.IsUnreachable
|| iterator_reachable
) {
744 Iterator
.MarkReachable (rc
);
748 // When infinite for end is unreachable via break anything what follows is unreachable too
750 if (infinite
&& !end_reachable
) {
751 return Reachability
.CreateUnreachable ();
757 protected override void DoEmit (EmitContext ec
)
759 if (Initializer
!= null)
760 Initializer
.Emit (ec
);
763 Condition
.EmitSideEffect (ec
);
767 Label old_begin
= ec
.LoopBegin
;
768 Label old_end
= ec
.LoopEnd
;
769 Label loop
= ec
.DefineLabel ();
770 Label test
= ec
.DefineLabel ();
772 ec
.LoopBegin
= ec
.DefineLabel ();
773 ec
.LoopEnd
= ec
.DefineLabel ();
775 ec
.Emit (OpCodes
.Br
, test
);
778 Condition
?.EmitPrepare (ec
);
781 ec
.MarkLabel (ec
.LoopBegin
);
786 // If test is null, there is no test, and we are just
789 if (Condition
!= null) {
790 ec
.Mark (Condition
.Location
);
793 // The Resolve code already catches the case for
794 // Test == Constant (false) so we know that
797 if (Condition
is Constant
) {
798 Condition
.EmitSideEffect (ec
);
799 ec
.Emit (OpCodes
.Br
, loop
);
801 Condition
.EmitBranchable (ec
, loop
, true);
805 ec
.Emit (OpCodes
.Br
, loop
);
806 ec
.MarkLabel (ec
.LoopEnd
);
808 ec
.LoopBegin
= old_begin
;
809 ec
.LoopEnd
= old_end
;
812 protected override void CloneTo (CloneContext clonectx
, Statement t
)
814 For target
= (For
) t
;
816 if (Initializer
!= null)
817 target
.Initializer
= Initializer
.Clone (clonectx
);
818 if (Condition
!= null)
819 target
.Condition
= Condition
.Clone (clonectx
);
820 if (Iterator
!= null)
821 target
.Iterator
= Iterator
.Clone (clonectx
);
822 target
.Statement
= Statement
.Clone (clonectx
);
825 public override object Accept (StructuralVisitor visitor
)
827 return visitor
.Visit (this);
830 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc
)
835 if (end_reachable_das
== null)
836 end_reachable_das
= new List
<DefiniteAssignmentBitSet
> ();
838 end_reachable_das
.Add (fc
.DefiniteAssignment
);
841 public override void SetEndReachable ()
843 end_reachable
= true;
846 public override void SetIteratorReachable ()
848 iterator_reachable
= true;
852 public abstract class LoopStatement
: Statement
854 protected LoopStatement (Statement statement
)
856 Statement
= statement
;
859 public Statement Statement { get; set; }
861 public override bool Resolve (BlockContext bc
)
863 var prev_loop
= bc
.EnclosingLoop
;
864 var prev_los
= bc
.EnclosingLoopOrSwitch
;
865 bc
.EnclosingLoopOrSwitch
= bc
.EnclosingLoop
= this;
866 var ok
= Statement
.Resolve (bc
);
867 bc
.EnclosingLoopOrSwitch
= prev_los
;
868 bc
.EnclosingLoop
= prev_loop
;
874 // Needed by possibly infinite loops statements (for, while) and switch statment
876 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc
)
880 public virtual void SetEndReachable ()
884 public virtual void SetIteratorReachable ()
889 public class StatementExpression
: Statement
891 ExpressionStatement expr
;
893 public StatementExpression (ExpressionStatement expr
)
896 loc
= expr
.StartLocation
;
899 public StatementExpression (ExpressionStatement expr
, Location loc
)
905 public ExpressionStatement Expr
{
911 protected override void CloneTo (CloneContext clonectx
, Statement t
)
913 StatementExpression target
= (StatementExpression
) t
;
914 target
.expr
= (ExpressionStatement
) expr
.Clone (clonectx
);
917 protected override void DoEmit (EmitContext ec
)
919 expr
.EmitStatement (ec
);
922 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
924 expr
.FlowAnalysis (fc
);
928 public override Reachability
MarkReachable (Reachability rc
)
930 base.MarkReachable (rc
);
931 return expr
.MarkReachable (rc
);
934 public override bool Resolve (BlockContext ec
)
936 expr
= expr
.ResolveStatement (ec
);
940 public override object Accept (StructuralVisitor visitor
)
942 return visitor
.Visit (this);
946 public class StatementErrorExpression
: Statement
950 public StatementErrorExpression (Expression expr
)
953 this.loc
= expr
.StartLocation
;
956 public Expression Expr
{
962 public override bool Resolve (BlockContext bc
)
964 expr
.Error_InvalidExpressionStatement (bc
);
968 protected override void DoEmit (EmitContext ec
)
970 throw new NotSupportedException ();
973 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
978 protected override void CloneTo (CloneContext clonectx
, Statement target
)
980 var t
= (StatementErrorExpression
) target
;
982 t
.expr
= expr
.Clone (clonectx
);
985 public override object Accept (StructuralVisitor visitor
)
987 return visitor
.Visit (this);
992 // Simple version of statement list not requiring a block
994 public class StatementList
: Statement
996 List
<Statement
> statements
;
998 public StatementList (Statement first
, Statement second
)
1000 statements
= new List
<Statement
> { first, second }
;
1004 public IList
<Statement
> Statements
{
1011 public void Add (Statement statement
)
1013 statements
.Add (statement
);
1016 public override bool Resolve (BlockContext ec
)
1018 foreach (var s
in statements
)
1024 protected override void DoEmit (EmitContext ec
)
1026 foreach (var s
in statements
)
1030 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
1032 foreach (var s
in statements
)
1033 s
.FlowAnalysis (fc
);
1038 public override Reachability
MarkReachable (Reachability rc
)
1040 base.MarkReachable (rc
);
1042 Reachability res
= rc
;
1043 foreach (var s
in statements
)
1044 res
= s
.MarkReachable (rc
);
1049 protected override void CloneTo (CloneContext clonectx
, Statement target
)
1051 StatementList t
= (StatementList
) target
;
1053 t
.statements
= new List
<Statement
> (statements
.Count
);
1054 foreach (Statement s
in statements
)
1055 t
.statements
.Add (s
.Clone (clonectx
));
1058 public override object Accept (StructuralVisitor visitor
)
1060 return visitor
.Visit (this);
1065 // For statements which require special handling when inside try or catch block
1067 public abstract class ExitStatement
: Statement
1069 protected bool unwind_protect
;
1071 protected abstract bool DoResolve (BlockContext bc
);
1072 protected abstract bool IsLocalExit { get; }
1074 public override bool Resolve (BlockContext bc
)
1076 var res
= DoResolve (bc
);
1080 // We are inside finally scope but is it the scope we are exiting
1082 if (bc
.HasSet (ResolveContext
.Options
.FinallyScope
)) {
1084 for (var b
= bc
.CurrentBlock
; b
!= null; b
= b
.Parent
) {
1085 if (b
.IsFinallyBlock
) {
1086 Error_FinallyClauseExit (bc
);
1090 if (b
is ParametersBlock
)
1096 unwind_protect
= bc
.HasAny (ResolveContext
.Options
.TryScope
| ResolveContext
.Options
.CatchScope
);
1100 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
1105 if (fc
.TryFinally
!= null) {
1106 fc
.TryFinally
.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc
.DefiniteAssignment
));
1108 fc
.ParametersBlock
.CheckControlExit (fc
);
1116 /// Implements the return statement
1118 public class Return
: ExitStatement
1123 public Return (Expression expr
, Location l
)
1131 public Expression Expr
{
1140 protected override bool IsLocalExit
{
1148 protected override bool DoResolve (BlockContext ec
)
1150 var block_return_type
= ec
.ReturnType
;
1153 if (block_return_type
.Kind
== MemberKind
.Void
|| block_return_type
== InternalType
.ErrorType
)
1157 // Return must not be followed by an expression when
1158 // the method return type is Task
1160 if (ec
.CurrentAnonymousMethod
is AsyncInitializer
) {
1161 var storey
= (AsyncTaskStorey
) ec
.CurrentAnonymousMethod
.Storey
;
1162 var s_return_type
= storey
.ReturnType
;
1163 if (s_return_type
== ec
.Module
.PredefinedTypes
.Task
.TypeSpec
) {
1165 // Extra trick not to emit ret/leave inside awaiter body
1167 expr
= EmptyExpression
.Null
;
1171 if (s_return_type
.IsGenericTask
|| (s_return_type
.Arity
== 1 && s_return_type
.IsCustomTaskType ()))
1172 block_return_type
= s_return_type
.TypeArguments
[0];
1175 if (ec
.CurrentIterator
!= null) {
1176 Error_ReturnFromIterator (ec
);
1177 } else if (block_return_type
!= InternalType
.ErrorType
) {
1178 ec
.Report
.Error (126, loc
,
1179 "An object of a type convertible to `{0}' is required for the return statement",
1180 block_return_type
.GetSignatureForError ());
1186 expr
= expr
.Resolve (ec
);
1188 AnonymousExpression am
= ec
.CurrentAnonymousMethod
;
1190 if (block_return_type
.Kind
== MemberKind
.Void
) {
1191 ec
.Report
.Error (127, loc
,
1192 "`{0}': A return keyword must not be followed by any expression when method returns void",
1193 ec
.GetSignatureForError ());
1198 if (am
.IsIterator
) {
1199 Error_ReturnFromIterator (ec
);
1203 var async_block
= am
as AsyncInitializer
;
1204 if (async_block
!= null) {
1206 var storey
= (AsyncTaskStorey
) am
.Storey
;
1207 var async_type
= storey
.ReturnType
;
1209 if (async_type
== null && async_block
.ReturnTypeInference
!= null) {
1210 if (expr
.Type
.Kind
== MemberKind
.Void
&& !(this is ContextualReturn
))
1211 ec
.Report
.Error (4029, loc
, "Cannot return an expression of type `void'");
1213 async_block
.ReturnTypeInference
.AddCommonTypeBoundAsync (expr
.Type
);
1217 if (async_type
.Kind
== MemberKind
.Void
) {
1218 ec
.Report
.Error (8030, loc
,
1219 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1223 if (!async_type
.IsGeneric
) {
1224 if (this is ContextualReturn
)
1227 if (async_block
.DelegateType
!= null) {
1228 ec
.Report
.Error (8031, loc
,
1229 "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task<T>'");
1231 ec
.Report
.Error (1997, loc
,
1232 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1233 ec
.GetSignatureForError ());
1239 // The return type is actually Task<T> type argument
1241 if (expr
.Type
== async_type
&& async_type
.TypeArguments
[0] != ec
.Module
.PredefinedTypes
.Task
.TypeSpec
) {
1242 ec
.Report
.Error (4016, loc
,
1243 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1244 ec
.GetSignatureForError (), async_type
.TypeArguments
[0].GetSignatureForError ());
1246 block_return_type
= async_type
.TypeArguments
[0];
1250 if (block_return_type
.Kind
== MemberKind
.Void
) {
1251 ec
.Report
.Error (8030, loc
,
1252 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1256 var l
= am
as AnonymousMethodBody
;
1257 if (l
!= null && expr
!= null) {
1258 if (l
.ReturnTypeInference
!= null) {
1259 l
.ReturnTypeInference
.AddCommonTypeBound (expr
.Type
);
1264 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1265 // unexpected debugging experience
1267 if (this is ContextualReturn
&& !ec
.IsInProbingMode
&& ec
.Module
.Compiler
.Settings
.Optimize
) {
1268 l
.DirectMethodGroupConversion
= expr
.CanReduceLambda (l
);
1277 if (expr
is ReferenceExpression
&& block_return_type
.Kind
!= MemberKind
.ByRef
) {
1278 ec
.Report
.Error (8149, loc
, "By-reference returns can only be used in methods that return by reference");
1282 if (expr
.Type
!= block_return_type
&& expr
.Type
!= InternalType
.ErrorType
) {
1283 if (block_return_type
.Kind
== MemberKind
.ByRef
) {
1284 var ref_expr
= Expr
as ReferenceExpression
;
1285 if (ref_expr
== null) {
1286 ec
.Report
.Error (8150, loc
, "By-reference return is required when method returns by reference");
1290 var byref_return
= (ReferenceContainer
)block_return_type
;
1292 if (expr
.Type
!= byref_return
.Element
) {
1293 ec
.Report
.Error (8151, loc
, "The return by reference expression must be of type `{0}' because this method returns by reference",
1294 byref_return
.GetSignatureForError ());
1299 expr
= Convert
.ImplicitConversionRequired (ec
, expr
, block_return_type
, loc
);
1302 if (am
!= null && block_return_type
== ec
.ReturnType
) {
1303 ec
.Report
.Error (1662, loc
,
1304 "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",
1305 am
.ContainerType
, am
.GetSignatureForError ());
1315 protected override void DoEmit (EmitContext ec
)
1319 var async_body
= ec
.CurrentAnonymousMethod
as AsyncInitializer
;
1320 if (async_body
!= null) {
1321 var storey
= (AsyncTaskStorey
)async_body
.Storey
;
1322 Label exit_label
= async_body
.BodyEnd
;
1325 // It's null for await without async
1327 if (storey
.HoistedReturnValue
!= null) {
1329 // Special case hoisted return value (happens in try/finally scenario)
1331 if (ec
.TryFinallyUnwind
!= null) {
1332 exit_label
= TryFinally
.EmitRedirectedReturn (ec
, async_body
, unwind_protect
);
1335 var async_return
= (IAssignMethod
)storey
.HoistedReturnValue
;
1336 async_return
.EmitAssign (ec
, expr
, false, false);
1341 if (ec
.TryFinallyUnwind
!= null)
1342 exit_label
= TryFinally
.EmitRedirectedReturn (ec
, async_body
, unwind_protect
);
1345 ec
.Emit (OpCodes
.Leave
, exit_label
);
1352 if (unwind_protect
|| ec
.EmitAccurateDebugInfo
)
1353 ec
.Emit (OpCodes
.Stloc
, ec
.TemporaryReturn ());
1356 if (unwind_protect
) {
1357 ec
.Emit (OpCodes
.Leave
, ec
.CreateReturnLabel ());
1358 } else if (ec
.EmitAccurateDebugInfo
) {
1359 ec
.Emit (OpCodes
.Br
, ec
.CreateReturnLabel ());
1361 ec
.Emit (OpCodes
.Ret
);
1365 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
1368 expr
.FlowAnalysis (fc
);
1371 base.DoFlowAnalysis (fc
);
1376 void Error_ReturnFromIterator (ResolveContext rc
)
1378 rc
.Report
.Error (1622, loc
,
1379 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1382 public override Reachability
MarkReachable (Reachability rc
)
1384 base.MarkReachable (rc
);
1387 rc
= Expr
.MarkReachable (rc
);
1388 expr_returns
= rc
.IsUnreachable
;
1391 return Reachability
.CreateUnreachable ();
1394 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1396 Return target
= (Return
) t
;
1397 // It's null for simple return;
1399 target
.expr
= expr
.Clone (clonectx
);
1402 public override object Accept (StructuralVisitor visitor
)
1404 return visitor
.Visit (this);
1408 public class Goto
: ExitStatement
1411 LabeledStatement label
;
1412 TryFinally try_finally
;
1414 public Goto (string label
, Location l
)
1420 public string Target
{
1421 get { return target; }
1424 protected override bool IsLocalExit
{
1430 protected override bool DoResolve (BlockContext bc
)
1432 label
= bc
.CurrentBlock
.LookupLabel (target
);
1433 if (label
== null) {
1434 Error_UnknownLabel (bc
, target
, loc
);
1438 try_finally
= bc
.CurrentTryBlock
as TryFinally
;
1440 CheckExitBoundaries (bc
, label
.Block
);
1445 public static void Error_UnknownLabel (BlockContext bc
, string label
, Location loc
)
1447 bc
.Report
.Error (159, loc
, "The label `{0}:' could not be found within the scope of the goto statement",
1451 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
1453 // Goto to unreachable label
1457 if (fc
.AddReachedLabel (label
))
1460 label
.Block
.ScanGotoJump (label
, fc
);
1464 public override Reachability
MarkReachable (Reachability rc
)
1466 if (rc
.IsUnreachable
)
1469 base.MarkReachable (rc
);
1471 if (try_finally
!= null) {
1472 if (try_finally
.FinallyBlock
.HasReachableClosingBrace
) {
1473 label
.AddGotoReference (rc
);
1478 label
.AddGotoReference (rc
);
1481 return Reachability
.CreateUnreachable ();
1484 protected override void CloneTo (CloneContext clonectx
, Statement target
)
1489 protected override void DoEmit (EmitContext ec
)
1491 // This should only happen for goto from try block to unrechable label
1495 Label l
= label
.LabelTarget (ec
);
1497 if (ec
.TryFinallyUnwind
!= null && IsLeavingFinally (label
.Block
)) {
1498 var async_body
= (AsyncInitializer
) ec
.CurrentAnonymousMethod
;
1499 l
= TryFinally
.EmitRedirectedJump (ec
, async_body
, l
, label
.Block
, unwind_protect
);
1502 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, l
);
1505 bool IsLeavingFinally (Block labelBlock
)
1507 var b
= try_finally
.Statement
as Block
;
1509 if (b
== labelBlock
)
1518 public override object Accept (StructuralVisitor visitor
)
1520 return visitor
.Visit (this);
1524 public class LabeledStatement
: Statement
{
1531 public LabeledStatement (string name
, Block block
, Location l
)
1538 public Label
LabelTarget (EmitContext ec
)
1543 label
= ec
.DefineLabel ();
1548 public Block Block
{
1554 public string Name
{
1555 get { return name; }
1558 protected override void CloneTo (CloneContext clonectx
, Statement target
)
1560 var t
= (LabeledStatement
) target
;
1562 t
.block
= clonectx
.RemapBlockCopy (block
);
1565 public override bool Resolve (BlockContext bc
)
1570 protected override void DoEmit (EmitContext ec
)
1573 ec
.MarkLabel (label
);
1576 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
1579 fc
.Report
.Warning (164, 2, loc
, "This label has not been referenced");
1585 public override Reachability
MarkReachable (Reachability rc
)
1587 base.MarkReachable (rc
);
1590 rc
= new Reachability ();
1595 public void AddGotoReference (Reachability rc
)
1603 block
.ScanGotoJump (this);
1606 public override object Accept (StructuralVisitor visitor
)
1608 return visitor
.Visit (this);
1614 /// `goto default' statement
1616 public class GotoDefault
: SwitchGoto
1618 public GotoDefault (Location l
)
1623 public override bool Resolve (BlockContext bc
)
1625 if (bc
.Switch
== null) {
1626 Error_GotoCaseRequiresSwitchBlock (bc
);
1630 bc
.Switch
.RegisterGotoCase (null, null);
1636 protected override void DoEmit (EmitContext ec
)
1638 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, ec
.Switch
.DefaultLabel
.GetILLabel (ec
));
1641 public override Reachability
MarkReachable (Reachability rc
)
1643 if (!rc
.IsUnreachable
) {
1644 var label
= switch_statement
.DefaultLabel
;
1645 if (label
.IsUnreachable
) {
1646 label
.MarkReachable (rc
);
1647 switch_statement
.Block
.ScanGotoJump (label
);
1651 return base.MarkReachable (rc
);
1654 public override object Accept (StructuralVisitor visitor
)
1656 return visitor
.Visit (this);
1661 /// `goto case' statement
1663 public class GotoCase
: SwitchGoto
1667 public GotoCase (Expression e
, Location l
)
1673 public Expression Expr
{
1679 public SwitchLabel Label { get; set; }
1681 public override bool Resolve (BlockContext ec
)
1683 if (ec
.Switch
== null) {
1684 Error_GotoCaseRequiresSwitchBlock (ec
);
1688 Constant c
= expr
.ResolveLabelConstant (ec
);
1694 if (ec
.Switch
.IsNullable
&& c
is NullLiteral
) {
1697 TypeSpec type
= ec
.Switch
.SwitchType
;
1698 res
= c
.Reduce (ec
, type
);
1700 c
.Error_ValueCannotBeConverted (ec
, type
, true);
1704 if (!Convert
.ImplicitStandardConversionExists (c
, type
))
1705 ec
.Report
.Warning (469, 2, loc
,
1706 "The `goto case' value is not implicitly convertible to type `{0}'",
1707 type
.GetSignatureForError ());
1711 ec
.Switch
.RegisterGotoCase (this, res
);
1718 protected override void DoEmit (EmitContext ec
)
1720 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, Label
.GetILLabel (ec
));
1723 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1725 GotoCase target
= (GotoCase
) t
;
1727 target
.expr
= expr
.Clone (clonectx
);
1730 public override Reachability
MarkReachable (Reachability rc
)
1732 if (!rc
.IsUnreachable
) {
1733 var label
= switch_statement
.FindLabel ((Constant
) expr
);
1734 if (label
.IsUnreachable
) {
1735 label
.MarkReachable (rc
);
1736 switch_statement
.Block
.ScanGotoJump (label
);
1740 return base.MarkReachable (rc
);
1743 public override object Accept (StructuralVisitor visitor
)
1745 return visitor
.Visit (this);
1749 public abstract class SwitchGoto
: Statement
1751 protected bool unwind_protect
;
1752 protected Switch switch_statement
;
1754 protected SwitchGoto (Location loc
)
1759 protected override void CloneTo (CloneContext clonectx
, Statement target
)
1764 public override bool Resolve (BlockContext bc
)
1766 CheckExitBoundaries (bc
, bc
.Switch
.Block
);
1768 unwind_protect
= bc
.HasAny (ResolveContext
.Options
.TryScope
| ResolveContext
.Options
.CatchScope
);
1769 switch_statement
= bc
.Switch
;
1774 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
1779 public override Reachability
MarkReachable (Reachability rc
)
1781 base.MarkReachable (rc
);
1782 return Reachability
.CreateUnreachable ();
1785 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc
)
1787 bc
.Report
.Error (153, loc
, "A goto case is only valid inside a switch statement");
1791 public class Throw
: Statement
{
1794 public Throw (Expression expr
, Location l
)
1800 public Expression Expr
{
1806 public static Expression
ConvertType (ResolveContext rc
, Expression expr
)
1808 var et
= rc
.BuiltinTypes
.Exception
;
1809 if (Convert
.ImplicitConversionExists (rc
, expr
, et
))
1810 expr
= Convert
.ImplicitConversion (rc
, expr
, et
, expr
.Location
);
1812 rc
.Report
.Error (155, expr
.Location
, "The type caught or thrown must be derived from System.Exception");
1813 expr
= EmptyCast
.Create (expr
, et
);
1819 public override bool Resolve (BlockContext ec
)
1822 if (!ec
.HasSet (ResolveContext
.Options
.CatchScope
)) {
1823 ec
.Report
.Error (156, loc
, "A throw statement with no arguments is not allowed outside of a catch clause");
1824 } else if (ec
.HasSet (ResolveContext
.Options
.FinallyScope
)) {
1825 for (var b
= ec
.CurrentBlock
; b
!= null && !b
.IsCatchBlock
; b
= b
.Parent
) {
1826 if (b
.IsFinallyBlock
) {
1827 ec
.Report
.Error (724, loc
,
1828 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1837 expr
= expr
.Resolve (ec
, ResolveFlags
.Type
| ResolveFlags
.VariableOrValue
);
1842 expr
= ConvertType (ec
, expr
);
1847 protected override void DoEmit (EmitContext ec
)
1850 var atv
= ec
.AsyncThrowVariable
;
1852 if (atv
.HoistedVariant
!= null) {
1853 atv
.HoistedVariant
.Emit (ec
);
1858 ec
.Emit (OpCodes
.Throw
);
1860 ec
.Emit (OpCodes
.Rethrow
);
1865 ec
.Emit (OpCodes
.Throw
);
1869 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
1872 expr
.FlowAnalysis (fc
);
1877 public override Reachability
MarkReachable (Reachability rc
)
1879 base.MarkReachable (rc
);
1880 return Reachability
.CreateUnreachable ();
1883 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1885 Throw target
= (Throw
) t
;
1888 target
.expr
= expr
.Clone (clonectx
);
1891 public override object Accept (StructuralVisitor visitor
)
1893 return visitor
.Visit (this);
1897 public class Break
: LocalExitStatement
1899 public Break (Location l
)
1904 public override object Accept (StructuralVisitor visitor
)
1906 return visitor
.Visit (this);
1909 protected override void DoEmit (EmitContext ec
)
1913 if (ec
.TryFinallyUnwind
!= null) {
1914 var async_body
= (AsyncInitializer
) ec
.CurrentAnonymousMethod
;
1915 l
= TryFinally
.EmitRedirectedJump (ec
, async_body
, l
, enclosing_loop
.Statement
as Block
, unwind_protect
);
1918 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, l
);
1921 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
1923 enclosing_loop
.AddEndDefiniteAssignment (fc
);
1927 protected override bool DoResolve (BlockContext bc
)
1929 enclosing_loop
= bc
.EnclosingLoopOrSwitch
;
1930 return base.DoResolve (bc
);
1933 public override Reachability
MarkReachable (Reachability rc
)
1935 base.MarkReachable (rc
);
1937 if (!rc
.IsUnreachable
)
1938 enclosing_loop
.SetEndReachable ();
1940 return Reachability
.CreateUnreachable ();
1944 public class Continue
: LocalExitStatement
1946 public Continue (Location l
)
1951 public override object Accept (StructuralVisitor visitor
)
1953 return visitor
.Visit (this);
1957 protected override void DoEmit (EmitContext ec
)
1959 var l
= ec
.LoopBegin
;
1961 if (ec
.TryFinallyUnwind
!= null) {
1962 var async_body
= (AsyncInitializer
) ec
.CurrentAnonymousMethod
;
1963 l
= TryFinally
.EmitRedirectedJump (ec
, async_body
, l
, enclosing_loop
.Statement
as Block
, unwind_protect
);
1966 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, l
);
1969 protected override bool DoResolve (BlockContext bc
)
1971 enclosing_loop
= bc
.EnclosingLoop
;
1972 return base.DoResolve (bc
);
1975 public override Reachability
MarkReachable (Reachability rc
)
1977 base.MarkReachable (rc
);
1979 if (!rc
.IsUnreachable
)
1980 enclosing_loop
.SetIteratorReachable ();
1982 return Reachability
.CreateUnreachable ();
1986 public abstract class LocalExitStatement
: ExitStatement
1988 protected LoopStatement enclosing_loop
;
1990 protected LocalExitStatement (Location loc
)
1995 protected override bool IsLocalExit
{
2001 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2006 protected override bool DoResolve (BlockContext bc
)
2008 if (enclosing_loop
== null) {
2009 bc
.Report
.Error (139, loc
, "No enclosing loop out of which to break or continue");
2013 var block
= enclosing_loop
.Statement
as Block
;
2015 // Don't need to do extra checks for simple statements loops
2016 if (block
!= null) {
2017 CheckExitBoundaries (bc
, block
);
2024 public interface ILocalVariable
2026 void Emit (EmitContext ec
);
2027 void EmitAssign (EmitContext ec
);
2028 void EmitAddressOf (EmitContext ec
);
2031 public interface INamedBlockVariable
2033 Block Block { get; }
2034 Expression
CreateReferenceExpression (ResolveContext rc
, Location loc
);
2035 bool IsDeclared { get; }
2036 bool IsParameter { get; }
2037 Location Location { get; }
2040 public class BlockVariableDeclarator
2043 Expression initializer
;
2045 public BlockVariableDeclarator (LocalVariable li
, Expression initializer
)
2047 if (li
.Type
!= null)
2048 throw new ArgumentException ("Expected null variable type");
2051 this.initializer
= initializer
;
2056 public LocalVariable Variable
{
2062 public Expression Initializer
{
2067 initializer
= value;
2073 public virtual BlockVariableDeclarator
Clone (CloneContext cloneCtx
)
2075 var t
= (BlockVariableDeclarator
) MemberwiseClone ();
2076 if (initializer
!= null)
2077 t
.initializer
= initializer
.Clone (cloneCtx
);
2083 public class BlockVariable
: Statement
2085 Expression initializer
;
2086 protected FullNamedExpression type_expr
;
2087 protected LocalVariable li
;
2088 protected List
<BlockVariableDeclarator
> declarators
;
2091 public BlockVariable (FullNamedExpression type
, LocalVariable li
)
2093 this.type_expr
= type
;
2095 this.loc
= type_expr
.Location
;
2098 protected BlockVariable (LocalVariable li
)
2105 public List
<BlockVariableDeclarator
> Declarators
{
2111 public Expression Initializer
{
2116 initializer
= value;
2120 public FullNamedExpression TypeExpression
{
2126 public LocalVariable Variable
{
2134 public void AddDeclarator (BlockVariableDeclarator decl
)
2136 if (declarators
== null)
2137 declarators
= new List
<BlockVariableDeclarator
> ();
2139 declarators
.Add (decl
);
2142 static void CreateEvaluatorVariable (BlockContext bc
, LocalVariable li
)
2144 if (bc
.Report
.Errors
!= 0)
2147 var container
= bc
.CurrentMemberDefinition
.Parent
.PartialContainer
;
2149 Field f
= new Field (container
, new TypeExpression (li
.Type
, li
.Location
), Modifiers
.PUBLIC
| Modifiers
.STATIC
,
2150 new MemberName (li
.Name
, li
.Location
), null);
2152 container
.AddField (f
);
2155 li
.HoistedVariant
= new HoistedEvaluatorVariable (f
);
2159 public override bool Resolve (BlockContext bc
)
2161 return Resolve (bc
, true);
2164 public bool Resolve (BlockContext bc
, bool resolveDeclaratorInitializers
)
2166 if (type
== null && !li
.IsCompilerGenerated
) {
2167 var vexpr
= type_expr
as VarExpr
;
2170 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2171 // same name exists or as a keyword when no type was found
2173 if (vexpr
!= null && !vexpr
.IsPossibleType (bc
)) {
2174 if (bc
.Module
.Compiler
.Settings
.Version
< LanguageVersion
.V_3
)
2175 bc
.Report
.FeatureIsNotAvailable (bc
.Module
.Compiler
, loc
, "implicitly typed local variable");
2178 bc
.Report
.Error (821, loc
, "A fixed statement cannot use an implicitly typed local variable");
2182 if (li
.IsConstant
) {
2183 bc
.Report
.Error (822, loc
, "An implicitly typed local variable cannot be a constant");
2187 if (Initializer
== null) {
2188 bc
.Report
.Error (818, loc
, "An implicitly typed local variable declarator must include an initializer");
2192 if (declarators
!= null) {
2193 bc
.Report
.Error (819, loc
, "An implicitly typed local variable declaration cannot include multiple declarators");
2197 Initializer
= Initializer
.Resolve (bc
);
2198 if (Initializer
!= null) {
2199 ((VarExpr
) type_expr
).InferType (bc
, Initializer
);
2200 type
= type_expr
.Type
;
2202 // Set error type to indicate the var was placed correctly but could
2205 // var a = missing ();
2207 type
= InternalType
.ErrorType
;
2212 type
= ResolveTypeExpression (bc
);
2216 if (li
.IsConstant
&& !type
.IsConstantCompatible
) {
2217 Const
.Error_InvalidConstantType (type
, loc
, bc
.Report
);
2222 FieldBase
.Error_VariableOfStaticClass (loc
, li
.Name
, type
, bc
.Report
);
2227 bool eval_global
= bc
.Module
.Compiler
.Settings
.StatementMode
&& bc
.CurrentBlock
is ToplevelBlock
;
2229 CreateEvaluatorVariable (bc
, li
);
2230 } else if (type
!= InternalType
.ErrorType
) {
2231 li
.PrepareAssignmentAnalysis (bc
);
2234 if (initializer
!= null) {
2236 if (!(initializer
is ReferenceExpression
)) {
2237 bc
.Report
.Error (8172, loc
, "Cannot initialize a by-reference variable `{0}' with a value", li
.Name
);
2241 if (bc
.CurrentAnonymousMethod
is AsyncInitializer
) {
2242 bc
.Report
.Error (8177, loc
, "Async methods cannot use by-reference variables");
2243 } else if (bc
.CurrentIterator
!= null) {
2244 bc
.Report
.Error (8176, loc
, "Iterators cannot use by-reference variables");
2248 if (initializer
is ReferenceExpression
) {
2249 bc
.Report
.Error (8171, loc
, "Cannot initialize a by-value variable `{0}' with a reference expression", li
.Name
);
2254 initializer
= ResolveInitializer (bc
, li
, initializer
);
2255 // li.Variable.DefinitelyAssigned
2258 if (declarators
!= null) {
2259 foreach (var d
in declarators
) {
2260 d
.Variable
.Type
= li
.Type
;
2262 CreateEvaluatorVariable (bc
, d
.Variable
);
2263 } else if (type
!= InternalType
.ErrorType
) {
2264 d
.Variable
.PrepareAssignmentAnalysis (bc
);
2267 if (d
.Initializer
!= null && resolveDeclaratorInitializers
) {
2268 d
.Initializer
= ResolveInitializer (bc
, d
.Variable
, d
.Initializer
);
2269 // d.Variable.DefinitelyAssigned
2277 protected virtual Expression
ResolveInitializer (BlockContext bc
, LocalVariable li
, Expression initializer
)
2279 var a
= new SimpleAssign (li
.CreateReferenceExpression (bc
, li
.Location
), initializer
, li
.Location
);
2280 return a
.ResolveStatement (bc
);
2283 protected virtual TypeSpec
ResolveTypeExpression (BlockContext bc
)
2285 return type_expr
.ResolveAsType (bc
);
2288 protected override void DoEmit (EmitContext ec
)
2290 li
.CreateBuilder (ec
);
2292 if (Initializer
!= null && !IsUnreachable
)
2293 ((ExpressionStatement
) Initializer
).EmitStatement (ec
);
2295 if (declarators
!= null) {
2296 foreach (var d
in declarators
) {
2297 d
.Variable
.CreateBuilder (ec
);
2298 if (d
.Initializer
!= null && !IsUnreachable
) {
2299 ec
.Mark (d
.Variable
.Location
);
2300 ((ExpressionStatement
) d
.Initializer
).EmitStatement (ec
);
2306 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
2308 if (Initializer
!= null)
2309 Initializer
.FlowAnalysis (fc
);
2311 if (declarators
!= null) {
2312 foreach (var d
in declarators
) {
2313 if (d
.Initializer
!= null)
2314 d
.Initializer
.FlowAnalysis (fc
);
2321 public override Reachability
MarkReachable (Reachability rc
)
2323 base.MarkReachable (rc
);
2324 return initializer
== null ? rc
: initializer
.MarkReachable (rc
);
2327 protected override void CloneTo (CloneContext clonectx
, Statement target
)
2329 BlockVariable t
= (BlockVariable
) target
;
2331 if (type_expr
!= null)
2332 t
.type_expr
= (FullNamedExpression
) type_expr
.Clone (clonectx
);
2334 if (initializer
!= null)
2335 t
.initializer
= initializer
.Clone (clonectx
);
2337 if (declarators
!= null) {
2338 t
.declarators
= null;
2339 foreach (var d
in declarators
)
2340 t
.AddDeclarator (d
.Clone (clonectx
));
2344 public override object Accept (StructuralVisitor visitor
)
2346 return visitor
.Visit (this);
2350 public class BlockConstant
: BlockVariable
2352 public BlockConstant (FullNamedExpression type
, LocalVariable li
)
2357 public override void Emit (EmitContext ec
)
2359 if (!Variable
.IsUsed
)
2360 ec
.Report
.Warning (219, 3, loc
, "The constant `{0}' is never used", Variable
.Name
);
2362 // Nothing to emit, not even sequence point
2365 protected override Expression
ResolveInitializer (BlockContext bc
, LocalVariable li
, Expression initializer
)
2367 initializer
= initializer
.Resolve (bc
);
2368 if (initializer
== null)
2372 if (initializer
.Type
== InternalType
.DefaultType
)
2373 c
= New
.Constantify (li
.Type
, initializer
.Location
);
2375 c
= initializer
as Constant
;
2378 initializer
.Error_ExpressionMustBeConstant (bc
, initializer
.Location
, li
.Name
);
2382 c
= c
.ConvertImplicitly (li
.Type
);
2384 if (TypeSpec
.IsReferenceType (li
.Type
))
2385 initializer
.Error_ConstantCanBeInitializedWithNullOnly (bc
, li
.Type
, initializer
.Location
, li
.Name
);
2387 initializer
.Error_ValueCannotBeConverted (bc
, li
.Type
, false);
2392 li
.ConstantValue
= c
;
2396 public override object Accept (StructuralVisitor visitor
)
2398 return visitor
.Visit (this);
2403 // The information about a user-perceived local variable
2405 public sealed class LocalVariable
: INamedBlockVariable
, ILocalVariable
2412 AddressTaken
= 1 << 2,
2413 CompilerGenerated
= 1 << 3,
2415 ForeachVariable
= 1 << 5 | ReadonlyMask
,
2416 FixedVariable
= 1 << 6 | ReadonlyMask
,
2417 UsingVariable
= 1 << 7 | ReadonlyMask
,
2419 SymbolFileHidden
= 1 << 9,
2421 PointerByRef
= 1 << 11,
2423 ReadonlyMask
= 1 << 20
2427 readonly string name
;
2428 readonly Location loc
;
2429 readonly Block block
;
2431 Constant const_value
;
2433 public VariableInfo VariableInfo
;
2434 HoistedVariable hoisted_variant
;
2436 LocalBuilder builder
;
2438 public LocalVariable (Block block
, string name
, Location loc
)
2445 public LocalVariable (Block block
, string name
, Flags flags
, Location loc
)
2446 : this (block
, name
, loc
)
2452 // Used by variable declarators
2454 public LocalVariable (LocalVariable li
, string name
, Location loc
)
2455 : this (li
.block
, name
, li
.flags
, loc
)
2461 public bool AddressTaken
{
2463 return (flags
& Flags
.AddressTaken
) != 0;
2467 public Block Block
{
2473 public Constant ConstantValue
{
2478 const_value
= value;
2483 // Hoisted local variable variant
2485 public HoistedVariable HoistedVariant
{
2487 return hoisted_variant
;
2490 hoisted_variant
= value;
2494 public bool Created
{
2496 return builder
!= null;
2500 public bool IsDeclared
{
2502 return type
!= null;
2506 public bool IsByRef
=> (flags
& Flags
.ByRef
) != 0;
2508 public bool IsCompilerGenerated
{
2510 return (flags
& Flags
.CompilerGenerated
) != 0;
2514 public bool IsConstant
{
2516 return (flags
& Flags
.Constant
) != 0;
2520 public bool IsLocked
{
2522 return (flags
& Flags
.IsLocked
) != 0;
2525 flags
= value ? flags
| Flags
.IsLocked
: flags
& ~Flags
.IsLocked
;
2529 public bool IsThis
{
2531 return (flags
& Flags
.IsThis
) != 0;
2535 public bool IsUsed
{
2537 return (flags
& Flags
.Used
) != 0;
2541 public bool IsFixed
{
2543 return (flags
& Flags
.FixedVariable
) == Flags
.FixedVariable
;
2546 flags
= value ? flags
| Flags
.FixedVariable
: flags
& ~Flags
.FixedVariable
;
2550 bool INamedBlockVariable
.IsParameter
{
2556 public bool IsReadonly
{
2558 return (flags
& Flags
.ReadonlyMask
) != 0;
2562 public Location Location
{
2568 public string Name
{
2574 public TypeSpec Type
{
2585 public void CreateBuilder (EmitContext ec
)
2587 if ((flags
& Flags
.Used
) == 0) {
2588 if (VariableInfo
== null) {
2589 // Missing flow analysis or wrong variable flags
2590 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name
);
2593 if (VariableInfo
.IsEverAssigned
)
2594 ec
.Report
.Warning (219, 3, Location
, "The variable `{0}' is assigned but its value is never used", Name
);
2596 ec
.Report
.Warning (168, 3, Location
, "The variable `{0}' is declared but never used", Name
);
2599 if (HoistedVariant
!= null)
2602 if (builder
!= null) {
2603 if ((flags
& Flags
.CompilerGenerated
) != 0)
2606 // To avoid Used warning duplicates
2607 throw new InternalErrorException ("Already created variable `{0}'", name
);
2611 builder
= ec
.DeclareLocal (ReferenceContainer
.MakeType (ec
.Module
, Type
), IsFixed
);
2612 } else if ((flags
& Flags
.PointerByRef
) != 0) {
2613 builder
= ec
.DeclareLocal (ReferenceContainer
.MakeType (ec
.Module
, ((PointerContainer
) Type
).Element
), IsFixed
);
2616 // All fixed variabled are pinned, a slot has to be alocated
2618 builder
= ec
.DeclareLocal (Type
, IsFixed
);
2621 if ((flags
& Flags
.SymbolFileHidden
) == 0)
2622 ec
.DefineLocalVariable (name
, builder
);
2625 public static LocalVariable
CreateCompilerGenerated (TypeSpec type
, Block block
, Location loc
, bool writeToSymbolFile
= false, Flags additionalFlags
= 0)
2627 LocalVariable li
= new LocalVariable (block
, GetCompilerGeneratedName (block
), Flags
.CompilerGenerated
| Flags
.Used
| additionalFlags
, loc
);
2628 if (!writeToSymbolFile
)
2629 li
.flags
|= Flags
.SymbolFileHidden
;
2635 public Expression
CreateReferenceExpression (ResolveContext rc
, Location loc
)
2637 if (IsConstant
&& const_value
!= null) {
2639 return Constant
.CreateConstantFromValue (Type
, const_value
.GetValue (), loc
);
2642 return new LocalVariableReference (this, loc
);
2645 public void Emit (EmitContext ec
)
2647 // TODO: Need something better for temporary variables
2648 if ((flags
& Flags
.CompilerGenerated
) != 0)
2651 ec
.Emit (OpCodes
.Ldloc
, builder
);
2654 public void EmitAssign (EmitContext ec
)
2656 // TODO: Need something better for temporary variables
2657 if ((flags
& Flags
.CompilerGenerated
) != 0)
2660 ec
.Emit (OpCodes
.Stloc
, builder
);
2663 public void EmitAddressOf (EmitContext ec
)
2665 // TODO: Need something better for temporary variables
2666 if ((flags
& Flags
.CompilerGenerated
) != 0)
2670 ec
.Emit (OpCodes
.Ldloc
, builder
);
2672 ec
.Emit (OpCodes
.Ldloca
, builder
);
2675 public static string GetCompilerGeneratedName (Block block
)
2677 // HACK: Debugger depends on the name semantics
2678 return "$locvar" + block
.ParametersBlock
.TemporaryLocalsCount
++.ToString ("X");
2681 public string GetReadOnlyContext ()
2683 switch (flags
& (Flags
.ForeachVariable
| Flags
.FixedVariable
| Flags
.UsingVariable
)) {
2684 case Flags
.FixedVariable
:
2685 return "fixed variable";
2686 case Flags
.ForeachVariable
:
2687 return "foreach iteration variable";
2688 case Flags
.UsingVariable
:
2689 return "using variable";
2692 throw new InternalErrorException ("Variable is not readonly");
2695 public bool IsThisAssigned (FlowAnalysisContext fc
, Block block
)
2697 if (VariableInfo
== null)
2698 throw new Exception ();
2700 if (IsAssigned (fc
))
2703 return VariableInfo
.IsFullyInitialized (fc
, block
.StartLocation
);
2706 public bool IsAssigned (FlowAnalysisContext fc
)
2708 return fc
.IsDefinitelyAssigned (VariableInfo
);
2711 public void PrepareAssignmentAnalysis (BlockContext bc
)
2714 // No need to run assignment analysis for these guys
2716 if ((flags
& (Flags
.Constant
| Flags
.ReadonlyMask
| Flags
.CompilerGenerated
)) != 0)
2719 VariableInfo
= VariableInfo
.Create (bc
, this);
2723 // Mark the variables as referenced in the user code
2725 public void SetIsUsed ()
2727 flags
|= Flags
.Used
;
2730 public void SetIsPointerByRef ()
2732 flags
|= Flags
.PointerByRef
;
2735 public void SetHasAddressTaken ()
2737 flags
|= (Flags
.AddressTaken
| Flags
.Used
);
2740 public override string ToString ()
2742 return string.Format ("LocalInfo ({0},{1},{2},{3})", name
, type
, VariableInfo
, Location
);
2747 /// Block represents a C# block.
2751 /// This class is used in a number of places: either to represent
2752 /// explicit blocks that the programmer places or implicit blocks.
2754 /// Implicit blocks are used as labels or to introduce variable
2757 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2758 /// they contain extra information that is not necessary on normal blocks.
2760 public class Block
: Statement
{
2767 HasCapturedVariable
= 64,
2768 HasCapturedThis
= 1 << 7,
2769 IsExpressionTree
= 1 << 8,
2770 CompilerGenerated
= 1 << 9,
2771 HasAsyncModifier
= 1 << 10,
2773 YieldBlock
= 1 << 12,
2774 AwaitBlock
= 1 << 13,
2775 FinallyBlock
= 1 << 14,
2776 CatchBlock
= 1 << 15,
2777 HasReferenceToStoreyForInstanceLambdas
= 1 << 16,
2779 NoFlowAnalysis
= 1 << 21,
2780 InitializationEmitted
= 1 << 22
2783 public Block Parent
;
2784 public Location StartLocation
;
2785 public Location EndLocation
;
2787 public ExplicitBlock Explicit
;
2788 public ParametersBlock ParametersBlock
;
2790 protected Flags flags
;
2793 // The statements in this block
2795 protected List
<Statement
> statements
;
2797 protected List
<Statement
> scope_initializers
;
2799 int? resolving_init_idx
;
2805 public int ID
= id
++;
2807 static int clone_id_counter
;
2811 // int assignable_slots;
2813 public Block (Block parent
, Location start
, Location end
)
2814 : this (parent
, 0, start
, end
)
2818 public Block (Block parent
, Flags flags
, Location start
, Location end
)
2820 if (parent
!= null) {
2821 // the appropriate constructors will fixup these fields
2822 ParametersBlock
= parent
.ParametersBlock
;
2823 Explicit
= parent
.Explicit
;
2826 this.Parent
= parent
;
2828 this.StartLocation
= start
;
2829 this.EndLocation
= end
;
2831 statements
= new List
<Statement
> (4);
2833 this.original
= this;
2838 public Block Original
{
2847 public bool IsCompilerGenerated
{
2848 get { return (flags & Flags.CompilerGenerated) != 0; }
2849 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2853 public bool IsCatchBlock
{
2855 return (flags
& Flags
.CatchBlock
) != 0;
2859 public bool IsFinallyBlock
{
2861 return (flags
& Flags
.FinallyBlock
) != 0;
2865 public bool Unchecked
{
2866 get { return (flags & Flags.Unchecked) != 0; }
2867 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2870 public bool Unsafe
{
2871 get { return (flags & Flags.Unsafe) != 0; }
2872 set { flags |= Flags.Unsafe; }
2875 public List
<Statement
> Statements
{
2876 get { return statements; }
2881 public void SetEndLocation (Location loc
)
2886 public void AddLabel (LabeledStatement target
)
2888 ParametersBlock
.TopBlock
.AddLabel (target
.Name
, target
);
2891 public void AddLocalName (LocalVariable li
)
2893 AddLocalName (li
.Name
, li
);
2896 public virtual void AddLocalName (string name
, INamedBlockVariable li
, bool canShadowChildrenBlockName
= false)
2898 ParametersBlock
.TopBlock
.AddLocalName (name
, li
, canShadowChildrenBlockName
);
2901 public virtual void Error_AlreadyDeclared (string name
, INamedBlockVariable variable
, string reason
)
2903 if (reason
== null) {
2904 Error_AlreadyDeclared (name
, variable
);
2908 ParametersBlock
.TopBlock
.Report
.Error (136, variable
.Location
,
2909 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2910 "to `{0}', which is already used in a `{1}' scope to denote something else",
2914 public virtual void Error_AlreadyDeclared (string name
, INamedBlockVariable variable
)
2916 var pi
= variable
as ParametersBlock
.ParameterInfo
;
2918 pi
.Parameter
.Error_DuplicateName (ParametersBlock
.TopBlock
.Report
);
2920 ParametersBlock
.TopBlock
.Report
.Error (128, variable
.Location
,
2921 "A local variable named `{0}' is already defined in this scope", name
);
2925 public virtual void Error_AlreadyDeclaredTypeParameter (string name
, Location loc
)
2927 ParametersBlock
.TopBlock
.Report
.Error (412, loc
,
2928 "The type parameter name `{0}' is the same as local variable or parameter name",
2933 // It should be used by expressions which require to
2934 // register a statement during resolve process.
2936 public void AddScopeStatement (Statement s
)
2938 if (scope_initializers
== null)
2939 scope_initializers
= new List
<Statement
> ();
2942 // Simple recursive helper, when resolve scope initializer another
2943 // new scope initializer can be added, this ensures it's initialized
2944 // before existing one. For now this can happen with expression trees
2945 // in base ctor initializer only
2947 if (resolving_init_idx
.HasValue
) {
2948 scope_initializers
.Insert (resolving_init_idx
.Value
, s
);
2949 ++resolving_init_idx
;
2951 scope_initializers
.Add (s
);
2955 public void InsertStatement (int index
, Statement s
)
2957 statements
.Insert (index
, s
);
2960 public void AddStatement (Statement s
)
2965 public LabeledStatement
LookupLabel (string name
)
2967 return ParametersBlock
.GetLabel (name
, this);
2970 public override Reachability
MarkReachable (Reachability rc
)
2972 if (rc
.IsUnreachable
)
2975 MarkReachableScope (rc
);
2977 foreach (var s
in statements
) {
2978 rc
= s
.MarkReachable (rc
);
2979 if (rc
.IsUnreachable
) {
2980 if ((flags
& Flags
.ReachableEnd
) != 0)
2981 return new Reachability ();
2987 flags
|= Flags
.ReachableEnd
;
2992 public void MarkReachableScope (Reachability rc
)
2994 base.MarkReachable (rc
);
2996 if (scope_initializers
!= null) {
2997 foreach (var si
in scope_initializers
)
2998 si
.MarkReachable (rc
);
3002 public override bool Resolve (BlockContext bc
)
3004 if ((flags
& Flags
.Resolved
) != 0)
3007 Block prev_block
= bc
.CurrentBlock
;
3008 bc
.CurrentBlock
= this;
3011 // Compiler generated scope statements
3013 if (scope_initializers
!= null) {
3014 for (resolving_init_idx
= 0; resolving_init_idx
< scope_initializers
.Count
; ++resolving_init_idx
) {
3015 scope_initializers
[resolving_init_idx
.Value
].Resolve (bc
);
3018 resolving_init_idx
= null;
3022 int statement_count
= statements
.Count
;
3023 for (int ix
= 0; ix
< statement_count
; ix
++){
3024 Statement s
= statements
[ix
];
3026 if (!s
.Resolve (bc
)) {
3028 statements
[ix
] = new EmptyStatement (s
.loc
);
3033 bc
.CurrentBlock
= prev_block
;
3035 flags
|= Flags
.Resolved
;
3039 protected override void DoEmit (EmitContext ec
)
3041 for (int ix
= 0; ix
< statements
.Count
; ix
++){
3042 statements
[ix
].Emit (ec
);
3046 public override void Emit (EmitContext ec
)
3048 if (scope_initializers
!= null)
3049 EmitScopeInitializers (ec
);
3054 protected void EmitScopeInitializers (EmitContext ec
)
3056 foreach (Statement s
in scope_initializers
)
3060 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
3062 if (scope_initializers
!= null) {
3063 foreach (var si
in scope_initializers
)
3064 si
.FlowAnalysis (fc
);
3067 return DoFlowAnalysis (fc
, 0);
3070 bool DoFlowAnalysis (FlowAnalysisContext fc
, int startIndex
)
3072 bool end_unreachable
= !reachable
;
3073 bool goto_flow_analysis
= startIndex
!= 0;
3074 for (; startIndex
< statements
.Count
; ++startIndex
) {
3075 var s
= statements
[startIndex
];
3077 end_unreachable
= s
.FlowAnalysis (fc
);
3078 if (s
.IsUnreachable
) {
3079 statements
[startIndex
] = RewriteUnreachableStatement (s
);
3084 // Statement end reachability is needed mostly due to goto support. Consider
3093 // X label is reachable only via goto not as another statement after if. We need
3094 // this for flow-analysis only to carry variable info correctly.
3096 if (end_unreachable
) {
3097 bool after_goto_case
= goto_flow_analysis
&& s
is GotoCase
;
3099 var f
= s
as TryFinally
;
3100 if (f
!= null && !f
.FinallyBlock
.HasReachableClosingBrace
) {
3102 // Special case for try-finally with unreachable code after
3103 // finally block. Try block has to include leave opcode but there is
3104 // no label to leave to after unreachable finally block closing
3105 // brace. This sentinel ensures there is always IL instruction to
3106 // leave to even if we know it'll never be reached.
3108 statements
.Insert (startIndex
+ 1, new SentinelStatement ());
3110 for (++startIndex
; startIndex
< statements
.Count
; ++startIndex
) {
3111 s
= statements
[startIndex
];
3112 if (s
is SwitchLabel
) {
3113 if (!after_goto_case
)
3114 s
.FlowAnalysis (fc
);
3119 if (s
.IsUnreachable
) {
3120 s
.FlowAnalysis (fc
);
3121 statements
[startIndex
] = RewriteUnreachableStatement (s
);
3127 // Idea is to stop after goto case because goto case will always have at least same
3128 // variable assigned as switch case label. This saves a lot for complex goto case tests
3130 if (after_goto_case
)
3136 var lb
= s
as LabeledStatement
;
3137 if (lb
!= null && fc
.AddReachedLabel (lb
))
3142 // The condition should be true unless there is forward jumping goto
3144 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3147 return !Explicit
.HasReachableClosingBrace
;
3150 static Statement
RewriteUnreachableStatement (Statement s
)
3152 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3153 // analysis. Even csc report unreachable warning for it but it's actually used hence
3154 // we try to emulate this behaviour
3162 if (s
is BlockVariable
|| s
is EmptyStatement
|| s
is SentinelStatement
)
3165 return new EmptyStatement (s
.loc
);
3168 public void ScanGotoJump (Statement label
)
3171 for (i
= 0; i
< statements
.Count
; ++i
) {
3172 if (statements
[i
] == label
)
3176 var rc
= new Reachability ();
3177 for (++i
; i
< statements
.Count
; ++i
) {
3178 var s
= statements
[i
];
3179 rc
= s
.MarkReachable (rc
);
3180 if (rc
.IsUnreachable
)
3184 flags
|= Flags
.ReachableEnd
;
3187 public void ScanGotoJump (Statement label
, FlowAnalysisContext fc
)
3190 for (i
= 0; i
< statements
.Count
; ++i
) {
3191 if (statements
[i
] == label
)
3195 DoFlowAnalysis (fc
, ++i
);
3199 public override string ToString ()
3201 return String
.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID
, clone_id
!= 0, StartLocation
);
3205 protected override void CloneTo (CloneContext clonectx
, Statement t
)
3207 Block target
= (Block
) t
;
3209 target
.clone_id
= ++clone_id_counter
;
3212 clonectx
.AddBlockMap (this, target
);
3213 if (original
!= this)
3214 clonectx
.AddBlockMap (original
, target
);
3216 target
.ParametersBlock
= (ParametersBlock
) (ParametersBlock
== this ? target
: clonectx
.RemapBlockCopy (ParametersBlock
));
3217 target
.Explicit
= (ExplicitBlock
) (Explicit
== this ? target
: clonectx
.LookupBlock (Explicit
));
3220 target
.Parent
= clonectx
.RemapBlockCopy (Parent
);
3222 target
.statements
= new List
<Statement
> (statements
.Count
);
3223 foreach (Statement s
in statements
)
3224 target
.statements
.Add (s
.Clone (clonectx
));
3227 public override object Accept (StructuralVisitor visitor
)
3229 return visitor
.Visit (this);
3233 public class ExplicitBlock
: Block
3235 protected AnonymousMethodStorey am_storey
;
3236 int debug_scope_index
;
3238 public ExplicitBlock (Block parent
, Location start
, Location end
)
3239 : this (parent
, (Flags
) 0, start
, end
)
3243 public ExplicitBlock (Block parent
, Flags flags
, Location start
, Location end
)
3244 : base (parent
, flags
, start
, end
)
3246 this.Explicit
= this;
3251 public AnonymousMethodStorey AnonymousMethodStorey
{
3257 public bool HasAwait
{
3259 return (flags
& Flags
.AwaitBlock
) != 0;
3263 public bool HasCapturedThis
{
3265 flags
= value ? flags
| Flags
.HasCapturedThis
: flags
& ~Flags
.HasCapturedThis
;
3268 return (flags
& Flags
.HasCapturedThis
) != 0;
3273 // Used to indicate that the block has reference to parent
3274 // block and cannot be made static when defining anonymous method
3276 public bool HasCapturedVariable
{
3278 flags
= value ? flags
| Flags
.HasCapturedVariable
: flags
& ~Flags
.HasCapturedVariable
;
3281 return (flags
& Flags
.HasCapturedVariable
) != 0;
3285 public bool HasReachableClosingBrace
{
3287 return (flags
& Flags
.ReachableEnd
) != 0;
3290 flags
= value ? flags
| Flags
.ReachableEnd
: flags
& ~Flags
.ReachableEnd
;
3294 public bool HasYield
{
3296 return (flags
& Flags
.YieldBlock
) != 0;
3303 // Creates anonymous method storey in current block
3305 public AnonymousMethodStorey
CreateAnonymousMethodStorey (ResolveContext ec
)
3308 // Return same story for iterator and async blocks unless we are
3309 // in nested anonymous method
3311 if (ec
.CurrentAnonymousMethod
is StateMachineInitializer
&& ParametersBlock
.Original
== ec
.CurrentAnonymousMethod
.Block
.Original
)
3312 return ec
.CurrentAnonymousMethod
.Storey
;
3314 if (am_storey
== null) {
3315 MemberBase mc
= ec
.MemberContext
as MemberBase
;
3318 // Creates anonymous method storey for this block
3320 am_storey
= new AnonymousMethodStorey (this, ec
.CurrentMemberDefinition
.Parent
.PartialContainer
, mc
, ec
.CurrentTypeParameters
, "AnonStorey", MemberKind
.Class
);
3326 public void EmitScopeInitialization (EmitContext ec
)
3328 if ((flags
& Flags
.InitializationEmitted
) != 0)
3331 if (am_storey
!= null) {
3332 DefineStoreyContainer (ec
, am_storey
);
3333 am_storey
.EmitStoreyInstantiation (ec
, this);
3336 if (scope_initializers
!= null)
3337 EmitScopeInitializers (ec
);
3339 flags
|= Flags
.InitializationEmitted
;
3342 public override void Emit (EmitContext ec
)
3344 // TODO: It's needed only when scope has variable (normal or lifted)
3345 var scopeIndex
= GetDebugSymbolScopeIndex ();
3346 if (scopeIndex
> 0) {
3347 ec
.BeginScope (scopeIndex
);
3350 EmitScopeInitialization (ec
);
3352 if (ec
.EmitAccurateDebugInfo
&& !IsCompilerGenerated
&& ec
.Mark (StartLocation
)) {
3353 ec
.Emit (OpCodes
.Nop
);
3361 if (ec
.EmitAccurateDebugInfo
&& HasReachableClosingBrace
&& !(this is ParametersBlock
) &&
3362 !IsCompilerGenerated
&& ec
.Mark (EndLocation
)) {
3363 ec
.Emit (OpCodes
.Nop
);
3367 protected void DefineStoreyContainer (EmitContext ec
, AnonymousMethodStorey storey
)
3369 if (ec
.CurrentAnonymousMethod
!= null && ec
.CurrentAnonymousMethod
.Storey
!= null) {
3370 storey
.SetNestedStoryParent (ec
.CurrentAnonymousMethod
.Storey
);
3371 storey
.Mutator
= ec
.CurrentAnonymousMethod
.Storey
.Mutator
;
3375 // Creates anonymous method storey
3377 storey
.CreateContainer ();
3378 storey
.DefineContainer ();
3379 storey
.ExpandBaseInterfaces ();
3381 if (Original
.Explicit
.HasCapturedThis
&& Original
.ParametersBlock
.TopBlock
.ThisReferencesFromChildrenBlock
!= null) {
3384 // Only first storey in path will hold this reference. All children blocks will
3385 // reference it indirectly using $ref field
3387 for (Block b
= Original
.Explicit
; b
!= null; b
= b
.Parent
) {
3388 if (b
.Parent
!= null) {
3389 var s
= b
.Parent
.Explicit
.AnonymousMethodStorey
;
3391 storey
.HoistedThis
= s
.HoistedThis
;
3396 if (b
.Explicit
== b
.Explicit
.ParametersBlock
&& b
.Explicit
.ParametersBlock
.StateMachine
!= null) {
3397 if (storey
.HoistedThis
== null)
3398 storey
.HoistedThis
= b
.Explicit
.ParametersBlock
.StateMachine
.HoistedThis
;
3400 if (storey
.HoistedThis
!= null)
3406 // We are the first storey on path and 'this' has to be hoisted
3408 if (storey
.HoistedThis
== null || !(storey
.Parent
is HoistedStoreyClass
)) {
3409 foreach (ExplicitBlock ref_block
in Original
.ParametersBlock
.TopBlock
.ThisReferencesFromChildrenBlock
) {
3411 // ThisReferencesFromChildrenBlock holds all reference even if they
3412 // are not on this path. It saves some memory otherwise it'd have to
3413 // be in every explicit block. We run this check to see if the reference
3414 // is valid for this storey
3416 Block block_on_path
= ref_block
;
3417 for (; block_on_path
!= null && block_on_path
!= Original
; block_on_path
= block_on_path
.Parent
);
3419 if (block_on_path
== null)
3422 if (storey
.HoistedThis
== null) {
3423 storey
.AddCapturedThisField (ec
, null);
3426 for (ExplicitBlock b
= ref_block
; b
.AnonymousMethodStorey
!= storey
; b
= b
.Parent
.Explicit
) {
3428 AnonymousMethodStorey b_storey
= b
.AnonymousMethodStorey
;
3430 if (b_storey
!= null) {
3432 // Don't add storey cross reference for `this' when the storey ends up not
3433 // beeing attached to any parent
3435 if (b
.ParametersBlock
.StateMachine
== null) {
3436 AnonymousMethodStorey s
= null;
3437 for (Block ab
= b
.AnonymousMethodStorey
.OriginalSourceBlock
.Parent
; ab
!= null; ab
= ab
.Parent
) {
3438 s
= ab
.Explicit
.AnonymousMethodStorey
;
3443 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3445 var parent
= storey
== null || storey
.Kind
== MemberKind
.Struct
? null : storey
;
3446 b
.AnonymousMethodStorey
.AddCapturedThisField (ec
, parent
);
3453 // Stop propagation inside same top block
3455 if (b
.ParametersBlock
== ParametersBlock
.Original
) {
3456 b_storey
.AddParentStoreyReference (ec
, storey
);
3457 // b_storey.HoistedThis = storey.HoistedThis;
3461 b
= pb
= b
.ParametersBlock
;
3463 pb
= b
as ParametersBlock
;
3466 if (pb
!= null && pb
.StateMachine
!= null) {
3467 if (pb
.StateMachine
== storey
)
3471 // If we are state machine with no parent. We can hook into parent without additional
3472 // reference and capture this directly
3474 ExplicitBlock parent_storey_block
= pb
;
3475 while (parent_storey_block
.Parent
!= null) {
3476 parent_storey_block
= parent_storey_block
.Parent
.Explicit
;
3477 if (parent_storey_block
.AnonymousMethodStorey
!= null) {
3482 if (parent_storey_block
.AnonymousMethodStorey
== null) {
3483 if (pb
.StateMachine
.HoistedThis
== null) {
3484 pb
.StateMachine
.AddCapturedThisField (ec
, null);
3485 b
.HasCapturedThis
= true;
3491 var parent_this_block
= pb
;
3492 while (parent_this_block
.Parent
!= null) {
3493 parent_this_block
= parent_this_block
.Parent
.ParametersBlock
;
3494 if (parent_this_block
.StateMachine
!= null && parent_this_block
.StateMachine
.HoistedThis
!= null) {
3500 // Add reference to closest storey which holds captured this
3502 pb
.StateMachine
.AddParentStoreyReference (ec
, parent_this_block
.StateMachine
?? storey
);
3506 // Add parent storey reference only when this is not captured directly
3508 if (b_storey
!= null) {
3509 b_storey
.AddParentStoreyReference (ec
, storey
);
3510 b_storey
.HoistedThis
= storey
.HoistedThis
;
3517 var ref_blocks
= storey
.ReferencesFromChildrenBlock
;
3518 if (ref_blocks
!= null) {
3519 foreach (ExplicitBlock ref_block
in ref_blocks
) {
3520 for (ExplicitBlock b
= ref_block
; b
.AnonymousMethodStorey
!= storey
; b
= b
.Parent
.Explicit
) {
3521 if (b
.AnonymousMethodStorey
!= null) {
3522 b
.AnonymousMethodStorey
.AddParentStoreyReference (ec
, storey
);
3525 // Stop propagation inside same top block
3527 if (b
.ParametersBlock
== ParametersBlock
.Original
)
3530 b
= b
.ParametersBlock
;
3533 var pb
= b
as ParametersBlock
;
3534 if (pb
!= null && pb
.StateMachine
!= null) {
3535 if (pb
.StateMachine
== storey
)
3538 pb
.StateMachine
.AddParentStoreyReference (ec
, storey
);
3541 b
.HasCapturedVariable
= true;
3547 storey
.PrepareEmit ();
3548 storey
.Parent
.PartialContainer
.AddCompilerGeneratedClass (storey
);
3551 public void DisableDebugScopeIndex ()
3553 debug_scope_index
= -1;
3556 public virtual int GetDebugSymbolScopeIndex ()
3558 if (debug_scope_index
== 0)
3559 debug_scope_index
= ++ParametersBlock
.debug_scope_index
;
3561 return debug_scope_index
;
3564 public void RegisterAsyncAwait ()
3567 while ((block
.flags
& Flags
.AwaitBlock
) == 0) {
3568 block
.flags
|= Flags
.AwaitBlock
;
3570 if (block
is ParametersBlock
)
3573 block
= block
.Parent
.Explicit
;
3577 public void RegisterIteratorYield ()
3579 ParametersBlock
.TopBlock
.IsIterator
= true;
3582 while ((block
.flags
& Flags
.YieldBlock
) == 0) {
3583 block
.flags
|= Flags
.YieldBlock
;
3585 if (block
.Parent
== null)
3588 block
= block
.Parent
.Explicit
;
3592 public void SetCatchBlock ()
3594 flags
|= Flags
.CatchBlock
;
3597 public void SetFinallyBlock ()
3599 flags
|= Flags
.FinallyBlock
;
3602 public void WrapIntoDestructor (TryFinally tf
, ExplicitBlock tryBlock
)
3604 tryBlock
.statements
= statements
;
3605 statements
= new List
<Statement
> (1);
3606 statements
.Add (tf
);
3611 // ParametersBlock was introduced to support anonymous methods
3612 // and lambda expressions
3614 public class ParametersBlock
: ExplicitBlock
3616 public class ParameterInfo
: INamedBlockVariable
3618 readonly ParametersBlock block
;
3620 public VariableInfo VariableInfo
;
3623 public ParameterInfo (ParametersBlock block
, int index
)
3631 public ParametersBlock Block
{
3637 Block INamedBlockVariable
.Block
{
3643 public bool IsDeclared
{
3649 public bool IsParameter
{
3655 public bool IsLocked
{
3664 public Location Location
{
3666 return Parameter
.Location
;
3670 public Parameter Parameter
{
3672 return block
.Parameters
[index
];
3676 public TypeSpec ParameterType
{
3678 return Parameter
.Type
;
3684 public Expression
CreateReferenceExpression (ResolveContext rc
, Location loc
)
3686 return new ParameterReference (this, loc
);
3691 // Block is converted into an expression
3693 sealed class BlockScopeExpression
: Expression
3696 readonly ParametersBlock block
;
3698 public BlockScopeExpression (Expression child
, ParametersBlock block
)
3704 public override bool ContainsEmitWithAwait ()
3706 return child
.ContainsEmitWithAwait ();
3709 public override Expression
CreateExpressionTree (ResolveContext ec
)
3711 throw new NotSupportedException ();
3714 protected override Expression
DoResolve (ResolveContext ec
)
3719 child
= child
.Resolve (ec
);
3723 eclass
= child
.eclass
;
3728 public override void Emit (EmitContext ec
)
3730 block
.EmitScopeInitializers (ec
);
3735 protected ParametersCompiled parameters
;
3736 protected ParameterInfo
[] parameter_info
;
3737 protected bool resolved
;
3738 protected ToplevelBlock top_block
;
3739 protected StateMachine state_machine
;
3740 protected Dictionary
<string, object> labels
;
3742 public ParametersBlock (Block parent
, ParametersCompiled parameters
, Location start
, Flags flags
= 0)
3743 : base (parent
, 0, start
, start
)
3745 if (parameters
== null)
3746 throw new ArgumentNullException ("parameters");
3748 this.parameters
= parameters
;
3749 ParametersBlock
= this;
3751 this.flags
|= flags
| (parent
.ParametersBlock
.flags
& (Flags
.YieldBlock
| Flags
.AwaitBlock
));
3753 this.top_block
= parent
.ParametersBlock
.top_block
;
3754 ProcessParameters ();
3757 protected ParametersBlock (ParametersCompiled parameters
, Location start
)
3758 : base (null, 0, start
, start
)
3760 if (parameters
== null)
3761 throw new ArgumentNullException ("parameters");
3763 this.parameters
= parameters
;
3764 ParametersBlock
= this;
3768 // It's supposed to be used by method body implementation of anonymous methods
3770 protected ParametersBlock (ParametersBlock source
, ParametersCompiled parameters
)
3771 : base (null, 0, source
.StartLocation
, source
.EndLocation
)
3773 this.parameters
= parameters
;
3774 this.statements
= source
.statements
;
3775 this.scope_initializers
= source
.scope_initializers
;
3777 this.resolved
= true;
3778 this.reachable
= source
.reachable
;
3779 this.am_storey
= source
.am_storey
;
3780 this.state_machine
= source
.state_machine
;
3781 this.flags
= source
.flags
& Flags
.ReachableEnd
;
3783 ParametersBlock
= this;
3786 // Overwrite original for comparison purposes when linking cross references
3787 // between anonymous methods
3789 Original
= source
.Original
;
3794 public bool HasReferenceToStoreyForInstanceLambdas
{
3796 return (flags
& Flags
.HasReferenceToStoreyForInstanceLambdas
) != 0;
3799 flags
= value ? flags
| Flags
.HasReferenceToStoreyForInstanceLambdas
: flags
& ~Flags
.HasReferenceToStoreyForInstanceLambdas
;
3803 public bool IsAsync
{
3805 return (flags
& Flags
.HasAsyncModifier
) != 0;
3808 flags
= value ? flags
| Flags
.HasAsyncModifier
: flags
& ~Flags
.HasAsyncModifier
;
3813 // Block has been converted to expression tree
3815 public bool IsExpressionTree
{
3817 return (flags
& Flags
.IsExpressionTree
) != 0;
3822 // The parameters for the block.
3824 public ParametersCompiled Parameters
{
3830 public StateMachine StateMachine
{
3832 return state_machine
;
3836 public ToplevelBlock TopBlock
{
3845 public bool Resolved
{
3847 return (flags
& Flags
.Resolved
) != 0;
3851 public int TemporaryLocalsCount { get; set; }
3856 // Checks whether all `out' parameters have been assigned.
3858 public void CheckControlExit (FlowAnalysisContext fc
)
3860 CheckControlExit (fc
, fc
.DefiniteAssignment
);
3863 public virtual void CheckControlExit (FlowAnalysisContext fc
, DefiniteAssignmentBitSet dat
)
3865 if (parameter_info
== null)
3868 foreach (var p
in parameter_info
) {
3869 if (p
.VariableInfo
== null)
3872 if (p
.VariableInfo
.IsAssigned (dat
))
3875 fc
.Report
.Error (177, p
.Location
,
3876 "The out parameter `{0}' must be assigned to before control leaves the current method",
3881 protected override void CloneTo (CloneContext clonectx
, Statement t
)
3883 base.CloneTo (clonectx
, t
);
3885 var target
= (ParametersBlock
) t
;
3888 // Clone label statements as well as they contain block reference
3892 if (pb
.labels
!= null) {
3893 target
.labels
= new Dictionary
<string, object> ();
3895 foreach (var entry
in pb
.labels
) {
3896 var list
= entry
.Value
as List
<LabeledStatement
>;
3899 var list_clone
= new List
<LabeledStatement
> ();
3900 foreach (var lentry
in list
) {
3901 list_clone
.Add (RemapLabeledStatement (lentry
, clonectx
.RemapBlockCopy (lentry
.Block
)));
3904 target
.labels
.Add (entry
.Key
, list_clone
);
3906 var labeled
= (LabeledStatement
) entry
.Value
;
3907 target
.labels
.Add (entry
.Key
, RemapLabeledStatement (labeled
, clonectx
.RemapBlockCopy (labeled
.Block
)));
3914 if (pb
.Parent
== null)
3917 pb
= pb
.Parent
.ParametersBlock
;
3921 public override Expression
CreateExpressionTree (ResolveContext ec
)
3923 if (statements
.Count
== 1) {
3924 Expression expr
= statements
[0].CreateExpressionTree (ec
);
3925 if (scope_initializers
!= null)
3926 expr
= new BlockScopeExpression (expr
, this);
3931 return base.CreateExpressionTree (ec
);
3934 public override void Emit (EmitContext ec
)
3936 if (state_machine
!= null && state_machine
.OriginalSourceBlock
!= this) {
3937 DefineStoreyContainer (ec
, state_machine
);
3938 state_machine
.EmitStoreyInstantiation (ec
, this);
3944 public void EmitEmbedded (EmitContext ec
)
3946 if (state_machine
!= null && state_machine
.OriginalSourceBlock
!= this) {
3947 DefineStoreyContainer (ec
, state_machine
);
3948 state_machine
.EmitStoreyInstantiation (ec
, this);
3954 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
3956 var res
= base.DoFlowAnalysis (fc
);
3958 if (HasReachableClosingBrace
)
3959 CheckControlExit (fc
);
3964 public override int GetDebugSymbolScopeIndex ()
3969 public LabeledStatement
GetLabel (string name
, Block block
)
3972 // Cloned parameters blocks can have their own cloned version of top-level labels
3974 if (labels
== null) {
3976 return Parent
.ParametersBlock
.GetLabel (name
, block
);
3982 if (!labels
.TryGetValue (name
, out value)) {
3986 var label
= value as LabeledStatement
;
3988 if (label
!= null) {
3989 if (IsLabelVisible (label
, b
))
3993 List
<LabeledStatement
> list
= (List
<LabeledStatement
>) value;
3994 for (int i
= 0; i
< list
.Count
; ++i
) {
3996 if (IsLabelVisible (label
, b
))
4004 static bool IsLabelVisible (LabeledStatement label
, Block b
)
4007 if (label
.Block
== b
)
4010 } while (b
!= null);
4015 public ParameterInfo
GetParameterInfo (Parameter p
)
4017 for (int i
= 0; i
< parameters
.Count
; ++i
) {
4018 if (parameters
[i
] == p
)
4019 return parameter_info
[i
];
4022 throw new ArgumentException ("Invalid parameter");
4025 public ParameterReference
GetParameterReference (int index
, Location loc
)
4027 return new ParameterReference (parameter_info
[index
], loc
);
4030 public Statement
PerformClone (ref HashSet
<LocalVariable
> undeclaredVariables
)
4032 undeclaredVariables
= TopBlock
.GetUndeclaredVariables ();
4034 CloneContext clonectx
= new CloneContext ();
4035 return Clone (clonectx
);
4038 protected void ProcessParameters ()
4040 if (parameters
.Count
== 0)
4043 parameter_info
= new ParameterInfo
[parameters
.Count
];
4044 for (int i
= 0; i
< parameter_info
.Length
; ++i
) {
4045 var p
= parameters
.FixedParameters
[i
];
4049 // TODO: Should use Parameter only and more block there
4050 parameter_info
[i
] = new ParameterInfo (this, i
);
4052 AddLocalName (p
.Name
, parameter_info
[i
]);
4056 LabeledStatement
RemapLabeledStatement (LabeledStatement stmt
, Block dst
)
4058 var src
= stmt
.Block
;
4061 // Cannot remap label block if the label was not yet cloned which
4062 // can happen in case of anonymous method inside anoynymous method
4063 // with a label. But in this case we don't care because goto cannot
4064 // jump of out anonymous method
4066 if (src
.ParametersBlock
!= this)
4069 var src_stmts
= src
.Statements
;
4070 for (int i
= 0; i
< src_stmts
.Count
; ++i
) {
4071 if (src_stmts
[i
] == stmt
)
4072 return (LabeledStatement
) dst
.Statements
[i
];
4075 throw new InternalErrorException ("Should never be reached");
4078 public override bool Resolve (BlockContext bc
)
4080 // TODO: if ((flags & Flags.Resolved) != 0)
4087 if (bc
.HasSet (ResolveContext
.Options
.ExpressionTreeConversion
))
4088 flags
|= Flags
.IsExpressionTree
;
4091 PrepareAssignmentAnalysis (bc
);
4093 if (!base.Resolve (bc
))
4096 } catch (Exception e
) {
4097 if (e
is CompletionResult
|| bc
.Report
.IsDisabled
|| e
is FatalException
|| bc
.Report
.Printer
is NullReportPrinter
|| bc
.Module
.Compiler
.Settings
.BreakOnInternalError
)
4100 if (bc
.CurrentBlock
!= null) {
4101 bc
.Report
.Error (584, bc
.CurrentBlock
.StartLocation
, "Internal compiler error: {0}", e
.Message
);
4103 bc
.Report
.Error (587, "Internal compiler error: {0}", e
.Message
);
4108 // If an asynchronous body of F is either an expression classified as nothing, or a
4109 // statement block where no return statements have expressions, the inferred return type is Task
4112 var am
= bc
.CurrentAnonymousMethod
as AnonymousMethodBody
;
4113 if (am
!= null && am
.ReturnTypeInference
!= null && !am
.ReturnTypeInference
.HasBounds (0)) {
4114 am
.ReturnTypeInference
= null;
4115 am
.ReturnType
= bc
.Module
.PredefinedTypes
.Task
.TypeSpec
;
4123 void PrepareAssignmentAnalysis (BlockContext bc
)
4125 for (int i
= 0; i
< parameters
.Count
; ++i
) {
4126 var par
= parameters
.FixedParameters
[i
];
4128 if ((par
.ModFlags
& Parameter
.Modifier
.OUT
) == 0)
4131 parameter_info
[i
].VariableInfo
= VariableInfo
.Create (bc
, (Parameter
) par
);
4135 public ToplevelBlock
ConvertToIterator (IMethodData method
, TypeDefinition host
, TypeSpec iterator_type
, bool is_enumerable
)
4137 var iterator
= new Iterator (this, method
, host
, iterator_type
, is_enumerable
);
4138 var stateMachine
= new IteratorStorey (iterator
);
4140 state_machine
= stateMachine
;
4141 iterator
.SetStateMachine (stateMachine
);
4143 var tlb
= new ToplevelBlock (host
.Compiler
, Parameters
, Location
.Null
, Flags
.CompilerGenerated
);
4144 tlb
.Original
= this;
4145 tlb
.state_machine
= stateMachine
;
4146 tlb
.AddStatement (new Return (iterator
, iterator
.Location
));
4150 public ParametersBlock
ConvertToAsyncTask (IMemberContext context
, TypeDefinition host
, ParametersCompiled parameters
, TypeSpec returnType
, TypeSpec delegateType
, Location loc
)
4152 for (int i
= 0; i
< parameters
.Count
; i
++) {
4153 Parameter p
= parameters
[i
];
4154 Parameter
.Modifier mod
= p
.ModFlags
;
4155 if ((mod
& Parameter
.Modifier
.RefOutMask
) != 0) {
4156 host
.Compiler
.Report
.Error (1988, p
.Location
,
4157 "Async methods cannot have ref or out parameters");
4161 if (p
is ArglistParameter
) {
4162 host
.Compiler
.Report
.Error (4006, p
.Location
,
4163 "__arglist is not allowed in parameter list of async methods");
4167 if (parameters
.Types
[i
].IsPointer
) {
4168 host
.Compiler
.Report
.Error (4005, p
.Location
,
4169 "Async methods cannot have unsafe parameters");
4175 host
.Compiler
.Report
.Warning (1998, 1, loc
,
4176 "Async block lacks `await' operator and will run synchronously");
4179 var block_type
= host
.Module
.Compiler
.BuiltinTypes
.Void
;
4180 var initializer
= new AsyncInitializer (this, host
, block_type
);
4181 initializer
.Type
= block_type
;
4182 initializer
.DelegateType
= delegateType
;
4184 var stateMachine
= new AsyncTaskStorey (this, context
, initializer
, returnType
);
4186 state_machine
= stateMachine
;
4187 initializer
.SetStateMachine (stateMachine
);
4189 const Flags flags
= Flags
.CompilerGenerated
;
4191 var b
= this is ToplevelBlock
?
4192 new ToplevelBlock (host
.Compiler
, Parameters
, Location
.Null
, flags
) :
4193 new ParametersBlock (Parent
, parameters
, Location
.Null
, flags
| Flags
.HasAsyncModifier
);
4196 b
.state_machine
= stateMachine
;
4197 b
.AddStatement (new AsyncInitializerStatement (initializer
));
4205 public class ToplevelBlock
: ParametersBlock
4207 LocalVariable this_variable
;
4208 CompilerContext compiler
;
4209 Dictionary
<string, object> names
;
4211 List
<ExplicitBlock
> this_references
;
4213 public ToplevelBlock (CompilerContext ctx
, Location loc
)
4214 : this (ctx
, ParametersCompiled
.EmptyReadOnlyParameters
, loc
)
4218 public ToplevelBlock (CompilerContext ctx
, ParametersCompiled parameters
, Location start
, Flags flags
= 0)
4219 : base (parameters
, start
)
4221 this.compiler
= ctx
;
4225 ProcessParameters ();
4229 // Recreates a top level block from parameters block. Used for
4230 // compiler generated methods where the original block comes from
4231 // explicit child block. This works for already resolved blocks
4232 // only to ensure we resolve them in the correct flow order
4234 public ToplevelBlock (ParametersBlock source
, ParametersCompiled parameters
)
4235 : base (source
, parameters
)
4237 this.compiler
= source
.TopBlock
.compiler
;
4241 public bool IsIterator
{
4243 return (flags
& Flags
.Iterator
) != 0;
4246 flags
= value ? flags
| Flags
.Iterator
: flags
& ~Flags
.Iterator
;
4250 public Report Report
{
4252 return compiler
.Report
;
4257 // Used by anonymous blocks to track references of `this' variable
4259 public List
<ExplicitBlock
> ThisReferencesFromChildrenBlock
{
4261 return this_references
;
4266 // Returns the "this" instance variable of this block.
4267 // See AddThisVariable() for more information.
4269 public LocalVariable ThisVariable
{
4271 return this_variable
;
4275 public override void AddLocalName (string name
, INamedBlockVariable li
, bool ignoreChildrenBlocks
)
4278 names
= new Dictionary
<string, object> ();
4281 if (!names
.TryGetValue (name
, out value)) {
4282 names
.Add (name
, li
);
4286 INamedBlockVariable existing
= value as INamedBlockVariable
;
4287 List
<INamedBlockVariable
> existing_list
;
4288 if (existing
!= null) {
4289 existing_list
= new List
<INamedBlockVariable
> ();
4290 existing_list
.Add (existing
);
4291 names
[name
] = existing_list
;
4293 existing_list
= (List
<INamedBlockVariable
>) value;
4297 // A collision checking between local names
4299 var variable_block
= li
.Block
.Explicit
;
4300 for (int i
= 0; i
< existing_list
.Count
; ++i
) {
4301 existing
= existing_list
[i
];
4302 Block b
= existing
.Block
.Explicit
;
4304 // Collision at same level
4305 if (variable_block
== b
) {
4306 li
.Block
.Error_AlreadyDeclared (name
, li
);
4310 // Collision with parent
4311 Block parent
= variable_block
;
4312 while ((parent
= parent
.Parent
) != null) {
4314 li
.Block
.Error_AlreadyDeclared (name
, li
, "parent or current");
4315 i
= existing_list
.Count
;
4320 if (!ignoreChildrenBlocks
&& variable_block
.Parent
!= b
.Parent
) {
4321 // Collision with children
4322 while ((b
= b
.Parent
) != null) {
4323 if (variable_block
== b
) {
4324 li
.Block
.Error_AlreadyDeclared (name
, li
, "child");
4325 i
= existing_list
.Count
;
4332 existing_list
.Add (li
);
4335 public void AddLabel (string name
, LabeledStatement label
)
4338 labels
= new Dictionary
<string, object> ();
4341 if (!labels
.TryGetValue (name
, out value)) {
4342 labels
.Add (name
, label
);
4346 LabeledStatement existing
= value as LabeledStatement
;
4347 List
<LabeledStatement
> existing_list
;
4348 if (existing
!= null) {
4349 existing_list
= new List
<LabeledStatement
> ();
4350 existing_list
.Add (existing
);
4351 labels
[name
] = existing_list
;
4353 existing_list
= (List
<LabeledStatement
>) value;
4357 // A collision checking between labels
4359 for (int i
= 0; i
< existing_list
.Count
; ++i
) {
4360 existing
= existing_list
[i
];
4361 Block b
= existing
.Block
;
4363 // Collision at same level
4364 if (label
.Block
== b
) {
4365 Report
.SymbolRelatedToPreviousError (existing
.loc
, name
);
4366 Report
.Error (140, label
.loc
, "The label `{0}' is a duplicate", name
);
4370 // Collision with parent
4372 while ((b
= b
.Parent
) != null) {
4373 if (existing
.Block
== b
) {
4374 Report
.Error (158, label
.loc
,
4375 "The label `{0}' shadows another label by the same name in a contained scope", name
);
4376 i
= existing_list
.Count
;
4381 // Collision with with children
4383 while ((b
= b
.Parent
) != null) {
4384 if (label
.Block
== b
) {
4385 Report
.Error (158, label
.loc
,
4386 "The label `{0}' shadows another label by the same name in a contained scope", name
);
4387 i
= existing_list
.Count
;
4393 existing_list
.Add (label
);
4396 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block
)
4398 if (this_references
== null)
4399 this_references
= new List
<ExplicitBlock
> ();
4401 if (!this_references
.Contains (block
))
4402 this_references
.Add (block
);
4405 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block
)
4407 this_references
.Remove (block
);
4411 // Creates an arguments set from all parameters, useful for method proxy calls
4413 public Arguments
GetAllParametersArguments ()
4415 int count
= parameters
.Count
;
4416 Arguments args
= new Arguments (count
);
4417 for (int i
= 0; i
< count
; ++i
) {
4418 var pi
= parameter_info
[i
];
4419 var arg_expr
= GetParameterReference (i
, pi
.Location
);
4421 Argument
.AType atype_modifier
;
4422 switch (pi
.Parameter
.ParameterModifier
& Parameter
.Modifier
.RefOutMask
) {
4423 case Parameter
.Modifier
.REF
:
4424 atype_modifier
= Argument
.AType
.Ref
;
4426 case Parameter
.Modifier
.OUT
:
4427 atype_modifier
= Argument
.AType
.Out
;
4434 args
.Add (new Argument (arg_expr
, atype_modifier
));
4441 // Lookup inside a block, the returned value can represent 3 states
4443 // true+variable: A local name was found and it's valid
4444 // false+variable: A local name was found in a child block only
4445 // false+null: No local name was found
4447 public bool GetLocalName (string name
, Block block
, ref INamedBlockVariable variable
)
4453 if (!names
.TryGetValue (name
, out value))
4456 variable
= value as INamedBlockVariable
;
4458 if (variable
!= null) {
4460 if (variable
.Block
== b
.Original
)
4464 } while (b
!= null);
4472 } while (b
!= null);
4474 List
<INamedBlockVariable
> list
= (List
<INamedBlockVariable
>) value;
4475 for (int i
= 0; i
< list
.Count
; ++i
) {
4478 if (variable
.Block
== b
.Original
)
4482 } while (b
!= null);
4490 } while (b
!= null);
4500 public void IncludeBlock (ParametersBlock pb
, ToplevelBlock block
)
4502 if (block
.names
!= null) {
4503 foreach (var n
in block
.names
) {
4504 var variable
= n
.Value
as INamedBlockVariable
;
4505 if (variable
!= null) {
4506 if (variable
.Block
.ParametersBlock
== pb
)
4507 AddLocalName (n
.Key
, variable
, false);
4511 foreach (var v
in (List
<INamedBlockVariable
>) n
.Value
)
4512 if (v
.Block
.ParametersBlock
== pb
)
4513 AddLocalName (n
.Key
, v
, false);
4519 // This is used by non-static `struct' constructors which do not have an
4520 // initializer - in this case, the constructor must initialize all of the
4521 // struct's fields. To do this, we add a "this" variable and use the flow
4522 // analysis code to ensure that it's been fully initialized before control
4523 // leaves the constructor.
4525 public void AddThisVariable (BlockContext bc
)
4527 if (this_variable
!= null)
4528 throw new InternalErrorException (StartLocation
.ToString ());
4530 this_variable
= new LocalVariable (this, "this", LocalVariable
.Flags
.IsThis
| LocalVariable
.Flags
.Used
, StartLocation
);
4531 this_variable
.Type
= bc
.CurrentType
;
4532 this_variable
.PrepareAssignmentAnalysis (bc
);
4535 public override void CheckControlExit (FlowAnalysisContext fc
, DefiniteAssignmentBitSet dat
)
4538 // If we're a non-static struct constructor which doesn't have an
4539 // initializer, then we must initialize all of the struct's fields.
4541 if (this_variable
!= null)
4542 this_variable
.IsThisAssigned (fc
, this);
4544 base.CheckControlExit (fc
, dat
);
4547 public HashSet
<LocalVariable
> GetUndeclaredVariables ()
4552 HashSet
<LocalVariable
> variables
= null;
4554 foreach (var entry
in names
) {
4555 var complex
= entry
.Value
as List
<INamedBlockVariable
>;
4556 if (complex
!= null) {
4557 foreach (var centry
in complex
) {
4558 if (IsUndeclaredVariable (centry
)) {
4559 if (variables
== null)
4560 variables
= new HashSet
<LocalVariable
> ();
4562 variables
.Add ((LocalVariable
) centry
);
4565 } else if (IsUndeclaredVariable ((INamedBlockVariable
)entry
.Value
)) {
4566 if (variables
== null)
4567 variables
= new HashSet
<LocalVariable
> ();
4569 variables
.Add ((LocalVariable
)entry
.Value
);
4576 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable
)
4578 var lv
= namedBlockVariable
as LocalVariable
;
4579 return lv
!= null && !lv
.IsDeclared
;
4582 public void SetUndeclaredVariables (HashSet
<LocalVariable
> undeclaredVariables
)
4587 foreach (var entry
in names
) {
4588 var complex
= entry
.Value
as List
<INamedBlockVariable
>;
4589 if (complex
!= null) {
4590 foreach (var centry
in complex
) {
4591 var lv
= centry
as LocalVariable
;
4592 if (lv
!= null && undeclaredVariables
.Contains (lv
)) {
4597 var lv
= entry
.Value
as LocalVariable
;
4598 if (lv
!= null && undeclaredVariables
.Contains (lv
))
4604 public override void Emit (EmitContext ec
)
4606 if (Report
.Errors
> 0)
4610 if (IsCompilerGenerated
) {
4611 using (ec
.With (BuilderContext
.Options
.OmitDebugInfo
, true)) {
4619 // If `HasReturnLabel' is set, then we already emitted a
4620 // jump to the end of the method, so we must emit a `ret'
4623 // Unfortunately, System.Reflection.Emit automatically emits
4624 // a leave to the end of a finally block. This is a problem
4625 // if no code is following the try/finally block since we may
4626 // jump to a point after the end of the method.
4627 // As a workaround, we're always creating a return label in
4630 if (ec
.HasReturnLabel
|| HasReachableClosingBrace
) {
4631 if (ec
.HasReturnLabel
)
4632 ec
.MarkLabel (ec
.ReturnLabel
);
4634 if (ec
.EmitAccurateDebugInfo
&& !IsCompilerGenerated
)
4635 ec
.Mark (EndLocation
);
4637 if (ec
.ReturnType
.Kind
!= MemberKind
.Void
)
4638 ec
.Emit (OpCodes
.Ldloc
, ec
.TemporaryReturn ());
4640 ec
.Emit (OpCodes
.Ret
);
4643 } catch (Exception e
) {
4644 throw new InternalErrorException (e
, StartLocation
);
4648 public bool Resolve (BlockContext bc
, IMethodData md
)
4653 var errors
= bc
.Report
.Errors
;
4657 if (bc
.Report
.Errors
> errors
)
4660 MarkReachable (new Reachability ());
4662 if (HasReachableClosingBrace
&& bc
.ReturnType
.Kind
!= MemberKind
.Void
) {
4663 // TODO: var md = bc.CurrentMemberDefinition;
4664 bc
.Report
.Error (161, md
.Location
, "`{0}': not all code paths return a value", md
.GetSignatureForError ());
4667 if ((flags
& Flags
.NoFlowAnalysis
) != 0)
4670 var fc
= new FlowAnalysisContext (bc
.Module
.Compiler
, this, bc
.AssignmentInfoOffset
);
4673 } catch (Exception e
) {
4674 throw new InternalErrorException (e
, StartLocation
);
4681 public class SwitchLabel
: Statement
4689 // if expr == null, then it is the default case.
4691 public SwitchLabel (Expression expr
, Location l
)
4697 public bool IsDefault
{
4699 return label
== null;
4703 public Expression Label
{
4709 public Location Location
{
4715 public Constant Converted
{
4724 public bool PatternMatching { get; set; }
4726 public bool SectionStart { get; set; }
4728 public Label
GetILLabel (EmitContext ec
)
4730 if (il_label
== null){
4731 il_label
= ec
.DefineLabel ();
4734 return il_label
.Value
;
4737 protected override void DoEmit (EmitContext ec
)
4739 ec
.MarkLabel (GetILLabel (ec
));
4742 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
4747 fc
.BranchDefiniteAssignment (fc
.SwitchInitialDefinitiveAssignment
);
4751 public override bool Resolve (BlockContext bc
)
4753 if (ResolveAndReduce (bc
))
4754 bc
.Switch
.RegisterLabel (bc
, this);
4760 // Resolves the expression, reduces it to a literal if possible
4761 // and then converts it to the requested type.
4763 bool ResolveAndReduce (BlockContext bc
)
4768 var switch_statement
= bc
.Switch
;
4770 if (PatternMatching
) {
4771 label
= new Is (switch_statement
.ExpressionValue
, label
, loc
).Resolve (bc
);
4772 return label
!= null;
4775 var c
= label
.ResolveLabelConstant (bc
);
4779 if (switch_statement
.IsNullable
&& c
is NullLiteral
) {
4784 if (switch_statement
.IsPatternMatching
) {
4785 label
= new Is (switch_statement
.ExpressionValue
, label
, loc
).Resolve (bc
);
4789 converted
= c
.ImplicitConversionRequired (bc
, switch_statement
.SwitchType
);
4790 return converted
!= null;
4793 public void Error_AlreadyOccurs (ResolveContext ec
, SwitchLabel collision_with
)
4795 ec
.Report
.SymbolRelatedToPreviousError (collision_with
.loc
, null);
4796 ec
.Report
.Error (152, loc
, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4799 protected override void CloneTo (CloneContext clonectx
, Statement target
)
4801 var t
= (SwitchLabel
) target
;
4803 t
.label
= label
.Clone (clonectx
);
4806 public override object Accept (StructuralVisitor visitor
)
4808 return visitor
.Visit (this);
4811 public string GetSignatureForError ()
4814 if (converted
== null)
4817 label
= converted
.GetValueAsLiteral ();
4819 return string.Format ("case {0}:", label
);
4823 public class Switch
: LoopStatement
4825 // structure used to hold blocks of keys while calculating table switch
4826 sealed class LabelsRange
: IComparable
<LabelsRange
>
4828 public readonly long min
;
4830 public readonly List
<long> label_values
;
4832 public LabelsRange (long value)
4835 label_values
= new List
<long> ();
4836 label_values
.Add (value);
4839 public LabelsRange (long min
, long max
, ICollection
<long> values
)
4843 this.label_values
= new List
<long> (values
);
4848 return max
- min
+ 1;
4852 public bool AddValue (long value)
4854 var gap
= value - min
+ 1;
4855 // Ensure the range has > 50% occupancy
4856 if (gap
> 2 * (label_values
.Count
+ 1) || gap
<= 0)
4860 label_values
.Add (value);
4864 public int CompareTo (LabelsRange other
)
4866 int nLength
= label_values
.Count
;
4867 int nLengthOther
= other
.label_values
.Count
;
4868 if (nLengthOther
== nLength
)
4869 return (int) (other
.min
- min
);
4871 return nLength
- nLengthOther
;
4875 sealed class DispatchStatement
: Statement
4877 readonly Switch body
;
4879 public DispatchStatement (Switch body
)
4884 protected override void CloneTo (CloneContext clonectx
, Statement target
)
4886 throw new NotImplementedException ();
4889 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
4894 protected override void DoEmit (EmitContext ec
)
4896 body
.EmitDispatch (ec
);
4900 class MissingBreak
: Statement
4902 readonly SwitchLabel label
;
4904 public MissingBreak (SwitchLabel sl
)
4910 public bool FallOut { get; set; }
4912 protected override void DoEmit (EmitContext ec
)
4916 protected override void CloneTo (CloneContext clonectx
, Statement target
)
4920 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
4923 fc
.Report
.Error (8070, loc
, "Control cannot fall out of switch statement through final case label `{0}'",
4924 label
.GetSignatureForError ());
4926 fc
.Report
.Error (163, loc
, "Control cannot fall through from one case label `{0}' to another",
4927 label
.GetSignatureForError ());
4933 public Expression Expr
;
4936 // Mapping of all labels to their SwitchLabels
4938 Dictionary
<long, SwitchLabel
> labels
;
4939 Dictionary
<string, SwitchLabel
> string_labels
;
4940 List
<SwitchLabel
> case_labels
;
4942 List
<Tuple
<GotoCase
, Constant
>> goto_cases
;
4943 List
<DefiniteAssignmentBitSet
> end_reachable_das
;
4946 /// The governing switch type
4948 public TypeSpec SwitchType
;
4950 Expression new_expr
;
4952 SwitchLabel case_null
;
4953 SwitchLabel case_default
;
4955 Label defaultLabel
, nullLabel
;
4956 VariableReference
value;
4957 ExpressionStatement string_dictionary
;
4958 FieldExpr switch_cache_field
;
4959 ExplicitBlock block
;
4963 // Nullable Types support
4965 Nullable
.Unwrap unwrap
;
4967 public Switch (Expression e
, ExplicitBlock block
, Location l
)
4975 public SwitchLabel ActiveLabel { get; set; }
4977 public ExplicitBlock Block
{
4983 public SwitchLabel DefaultLabel
{
4985 return case_default
;
4989 public bool IsNullable
{
4991 return unwrap
!= null;
4995 public bool IsPatternMatching
{
4997 return new_expr
== null && SwitchType
!= null;
5001 public List
<SwitchLabel
> RegisteredLabels
{
5007 public VariableReference ExpressionValue
{
5014 // Determines the governing type for a switch. The returned
5015 // expression might be the expression from the switch, or an
5016 // expression that includes any potential conversions to
5018 static Expression
SwitchGoverningType (ResolveContext rc
, Expression expr
, bool unwrapExpr
)
5020 switch (expr
.Type
.BuiltinType
) {
5021 case BuiltinTypeSpec
.Type
.Byte
:
5022 case BuiltinTypeSpec
.Type
.SByte
:
5023 case BuiltinTypeSpec
.Type
.UShort
:
5024 case BuiltinTypeSpec
.Type
.Short
:
5025 case BuiltinTypeSpec
.Type
.UInt
:
5026 case BuiltinTypeSpec
.Type
.Int
:
5027 case BuiltinTypeSpec
.Type
.ULong
:
5028 case BuiltinTypeSpec
.Type
.Long
:
5029 case BuiltinTypeSpec
.Type
.Char
:
5030 case BuiltinTypeSpec
.Type
.String
:
5031 case BuiltinTypeSpec
.Type
.Bool
:
5035 if (expr
.Type
.IsEnum
)
5039 // Try to find a *user* defined implicit conversion.
5041 // If there is no implicit conversion, or if there are multiple
5042 // conversions, we have to report an error
5044 Expression converted
= null;
5045 foreach (TypeSpec tt
in rc
.Module
.PredefinedTypes
.SwitchUserTypes
) {
5047 if (!unwrapExpr
&& tt
.IsNullableType
&& expr
.Type
.IsNullableType
)
5050 var restr
= Convert
.UserConversionRestriction
.ImplicitOnly
|
5051 Convert
.UserConversionRestriction
.ProbingOnly
;
5054 restr
|= Convert
.UserConversionRestriction
.NullableSourceOnly
;
5056 var e
= Convert
.UserDefinedConversion (rc
, expr
, tt
, restr
, Location
.Null
);
5061 // Ignore over-worked ImplicitUserConversions that do
5062 // an implicit conversion in addition to the user conversion.
5064 var uc
= e
as UserCast
;
5068 if (converted
!= null){
5069 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
5078 public static TypeSpec
[] CreateSwitchUserTypes (ModuleContainer module
, TypeSpec nullable
)
5080 var types
= module
.Compiler
.BuiltinTypes
;
5082 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
5083 TypeSpec
[] stypes
= new[] {
5096 if (nullable
!= null) {
5098 Array
.Resize (ref stypes
, stypes
.Length
+ 9);
5100 for (int i
= 0; i
< 9; ++i
) {
5101 stypes
[10 + i
] = nullable
.MakeGenericType (module
, new [] { stypes [i] }
);
5108 public void RegisterLabel (BlockContext rc
, SwitchLabel sl
)
5110 case_labels
.Add (sl
);
5113 if (case_default
!= null) {
5114 sl
.Error_AlreadyOccurs (rc
, case_default
);
5122 if (sl
.Converted
== null)
5126 if (string_labels
!= null) {
5127 string string_value
= sl
.Converted
.GetValue () as string;
5128 if (string_value
== null)
5131 string_labels
.Add (string_value
, sl
);
5133 if (sl
.Converted
.IsNull
) {
5136 labels
.Add (sl
.Converted
.GetValueAsLong (), sl
);
5139 } catch (ArgumentException
) {
5140 if (string_labels
!= null)
5141 sl
.Error_AlreadyOccurs (rc
, string_labels
[(string) sl
.Converted
.GetValue ()]);
5143 sl
.Error_AlreadyOccurs (rc
, labels
[sl
.Converted
.GetValueAsLong ()]);
5148 // This method emits code for a lookup-based switch statement (non-string)
5149 // Basically it groups the cases into blocks that are at least half full,
5150 // and then spits out individual lookup opcodes for each block.
5151 // It emits the longest blocks first, and short blocks are just
5152 // handled with direct compares.
5154 void EmitTableSwitch (EmitContext ec
, Expression val
)
5156 if (labels
!= null && labels
.Count
> 0) {
5157 List
<LabelsRange
> ranges
;
5158 if (string_labels
!= null) {
5159 // We have done all hard work for string already
5160 // setup single range only
5161 ranges
= new List
<LabelsRange
> (1);
5162 ranges
.Add (new LabelsRange (0, labels
.Count
- 1, labels
.Keys
));
5164 var element_keys
= new long[labels
.Count
];
5165 labels
.Keys
.CopyTo (element_keys
, 0);
5166 Array
.Sort (element_keys
);
5169 // Build possible ranges of switch labes to reduce number
5172 ranges
= new List
<LabelsRange
> (element_keys
.Length
);
5173 var range
= new LabelsRange (element_keys
[0]);
5175 for (int i
= 1; i
< element_keys
.Length
; ++i
) {
5176 var l
= element_keys
[i
];
5177 if (range
.AddValue (l
))
5180 range
= new LabelsRange (l
);
5184 // sort the blocks so we can tackle the largest ones first
5188 Label lbl_default
= defaultLabel
;
5189 TypeSpec compare_type
= SwitchType
.IsEnum
? EnumSpec
.GetUnderlyingType (SwitchType
) : SwitchType
;
5191 for (int range_index
= ranges
.Count
- 1; range_index
>= 0; --range_index
) {
5192 LabelsRange kb
= ranges
[range_index
];
5193 lbl_default
= (range_index
== 0) ? defaultLabel
: ec
.DefineLabel ();
5195 // Optimize small ranges using simple equality check
5196 if (kb
.Range
<= 2) {
5197 foreach (var key
in kb
.label_values
) {
5198 SwitchLabel sl
= labels
[key
];
5199 if (sl
== case_default
|| sl
== case_null
)
5202 if (sl
.Converted
.IsZeroInteger
) {
5203 val
.EmitBranchable (ec
, sl
.GetILLabel (ec
), false);
5206 sl
.Converted
.Emit (ec
);
5207 ec
.Emit (OpCodes
.Beq
, sl
.GetILLabel (ec
));
5211 // TODO: if all the keys in the block are the same and there are
5212 // no gaps/defaults then just use a range-check.
5213 if (compare_type
.BuiltinType
== BuiltinTypeSpec
.Type
.Long
|| compare_type
.BuiltinType
== BuiltinTypeSpec
.Type
.ULong
) {
5214 // TODO: optimize constant/I4 cases
5216 // check block range (could be > 2^31)
5218 ec
.EmitLong (kb
.min
);
5219 ec
.Emit (OpCodes
.Blt
, lbl_default
);
5222 ec
.EmitLong (kb
.max
);
5223 ec
.Emit (OpCodes
.Bgt
, lbl_default
);
5228 ec
.EmitLong (kb
.min
);
5229 ec
.Emit (OpCodes
.Sub
);
5232 ec
.Emit (OpCodes
.Conv_I4
); // assumes < 2^31 labels!
5236 int first
= (int) kb
.min
;
5239 ec
.Emit (OpCodes
.Sub
);
5240 } else if (first
< 0) {
5241 ec
.EmitInt (-first
);
5242 ec
.Emit (OpCodes
.Add
);
5246 // first, build the list of labels for the switch
5248 long cJumps
= kb
.Range
;
5249 Label
[] switch_labels
= new Label
[cJumps
];
5250 for (int iJump
= 0; iJump
< cJumps
; iJump
++) {
5251 var key
= kb
.label_values
[iKey
];
5252 if (key
== kb
.min
+ iJump
) {
5253 switch_labels
[iJump
] = labels
[key
].GetILLabel (ec
);
5256 switch_labels
[iJump
] = lbl_default
;
5260 // emit the switch opcode
5261 ec
.Emit (OpCodes
.Switch
, switch_labels
);
5264 // mark the default for this block
5265 if (range_index
!= 0)
5266 ec
.MarkLabel (lbl_default
);
5269 // the last default just goes to the end
5270 if (ranges
.Count
> 0)
5271 ec
.Emit (OpCodes
.Br
, lbl_default
);
5275 public SwitchLabel
FindLabel (Constant
value)
5277 SwitchLabel sl
= null;
5279 if (string_labels
!= null) {
5280 string s
= value.GetValue () as string;
5282 if (case_null
!= null)
5284 else if (case_default
!= null)
5287 string_labels
.TryGetValue (s
, out sl
);
5290 if (value is NullLiteral
) {
5293 labels
.TryGetValue (value.GetValueAsLong (), out sl
);
5297 if (sl
== null || sl
.SectionStart
)
5301 // Always return section start, it simplifies handling of switch labels
5303 for (int idx
= case_labels
.IndexOf (sl
); ; --idx
) {
5304 var cs
= case_labels
[idx
];
5305 if (cs
.SectionStart
)
5310 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
5312 Expr
.FlowAnalysis (fc
);
5314 var prev_switch
= fc
.SwitchInitialDefinitiveAssignment
;
5315 var InitialDefinitiveAssignment
= fc
.DefiniteAssignment
;
5316 fc
.SwitchInitialDefinitiveAssignment
= InitialDefinitiveAssignment
;
5318 block
.FlowAnalysis (fc
);
5320 fc
.SwitchInitialDefinitiveAssignment
= prev_switch
;
5322 if (end_reachable_das
!= null) {
5323 var sections_das
= DefiniteAssignmentBitSet
.And (end_reachable_das
);
5324 InitialDefinitiveAssignment
|= sections_das
;
5325 end_reachable_das
= null;
5328 fc
.DefiniteAssignment
= InitialDefinitiveAssignment
;
5330 return case_default
!= null && !end_reachable
;
5333 public override bool Resolve (BlockContext ec
)
5335 Expr
= Expr
.Resolve (ec
);
5340 // LAMESPEC: User conversion from non-nullable governing type has a priority
5342 new_expr
= SwitchGoverningType (ec
, Expr
, false);
5344 if (new_expr
== null) {
5345 if (Expr
.Type
.IsNullableType
) {
5346 unwrap
= Nullable
.Unwrap
.Create (Expr
, false);
5351 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5352 // involving nullable Expr and nullable governing type is
5354 new_expr
= SwitchGoverningType (ec
, unwrap
, true);
5358 Expression switch_expr
;
5359 if (new_expr
== null) {
5360 if (ec
.Module
.Compiler
.Settings
.Version
!= LanguageVersion
.Experimental
) {
5361 if (Expr
.Type
!= InternalType
.ErrorType
) {
5362 ec
.Report
.Error (151, loc
,
5363 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5364 Expr
.Type
.GetSignatureForError ());
5371 SwitchType
= Expr
.Type
;
5373 switch_expr
= new_expr
;
5374 SwitchType
= new_expr
.Type
;
5375 if (SwitchType
.IsNullableType
) {
5376 new_expr
= unwrap
= Nullable
.Unwrap
.Create (new_expr
, true);
5377 SwitchType
= Nullable
.NullableInfo
.GetUnderlyingType (SwitchType
);
5380 if (SwitchType
.BuiltinType
== BuiltinTypeSpec
.Type
.Bool
&& ec
.Module
.Compiler
.Settings
.Version
== LanguageVersion
.ISO_1
) {
5381 ec
.Report
.FeatureIsNotAvailable (ec
.Module
.Compiler
, loc
, "switch expression of boolean type");
5385 if (block
.Statements
.Count
== 0)
5388 if (SwitchType
.BuiltinType
== BuiltinTypeSpec
.Type
.String
) {
5389 string_labels
= new Dictionary
<string, SwitchLabel
> ();
5391 labels
= new Dictionary
<long, SwitchLabel
> ();
5395 var constant
= switch_expr
as Constant
;
5398 // Don't need extra variable for constant switch or switch with
5399 // only default case
5401 if (constant
== null) {
5403 // Store switch expression for comparison purposes
5405 value = switch_expr
as VariableReference
;
5406 if (value == null && !HasOnlyDefaultSection ()) {
5407 var current_block
= ec
.CurrentBlock
;
5408 ec
.CurrentBlock
= Block
;
5409 // Create temporary variable inside switch scope
5410 value = TemporaryVariableReference
.Create (SwitchType
, ec
.CurrentBlock
, loc
);
5412 ec
.CurrentBlock
= current_block
;
5416 case_labels
= new List
<SwitchLabel
> ();
5418 Switch old_switch
= ec
.Switch
;
5420 var parent_los
= ec
.EnclosingLoopOrSwitch
;
5421 ec
.EnclosingLoopOrSwitch
= this;
5423 var ok
= Statement
.Resolve (ec
);
5425 ec
.EnclosingLoopOrSwitch
= parent_los
;
5426 ec
.Switch
= old_switch
;
5429 // Check if all goto cases are valid. Needs to be done after switch
5430 // is resolved because goto can jump forward in the scope.
5432 if (goto_cases
!= null) {
5433 foreach (var gc
in goto_cases
) {
5434 if (gc
.Item1
== null) {
5435 if (DefaultLabel
== null) {
5436 Goto
.Error_UnknownLabel (ec
, "default", loc
);
5442 var sl
= FindLabel (gc
.Item2
);
5444 Goto
.Error_UnknownLabel (ec
, "case " + gc
.Item2
.GetValueAsLiteral (), loc
);
5446 gc
.Item1
.Label
= sl
;
5454 if (constant
== null && SwitchType
.BuiltinType
== BuiltinTypeSpec
.Type
.String
&& string_labels
.Count
> 6) {
5455 ResolveStringSwitchMap (ec
);
5459 // Anonymous storey initialization has to happen before
5460 // any generated switch dispatch
5462 block
.InsertStatement (0, new DispatchStatement (this));
5467 bool HasOnlyDefaultSection ()
5469 for (int i
= 0; i
< block
.Statements
.Count
; ++i
) {
5470 var s
= block
.Statements
[i
] as SwitchLabel
;
5472 if (s
== null || s
.IsDefault
)
5481 public override Reachability
MarkReachable (Reachability rc
)
5483 if (rc
.IsUnreachable
)
5486 base.MarkReachable (rc
);
5488 block
.MarkReachableScope (rc
);
5490 if (block
.Statements
.Count
== 0)
5493 SwitchLabel constant_label
= null;
5494 var constant
= new_expr
as Constant
;
5496 if (constant
!= null) {
5497 constant_label
= FindLabel (constant
) ?? case_default
;
5498 if (constant_label
== null) {
5499 block
.Statements
.RemoveAt (0);
5504 var section_rc
= new Reachability ();
5505 SwitchLabel prev_label
= null;
5507 for (int i
= 0; i
< block
.Statements
.Count
; ++i
) {
5508 var s
= block
.Statements
[i
];
5509 var sl
= s
as SwitchLabel
;
5511 if (sl
!= null && sl
.SectionStart
) {
5513 // Section is marked already via goto case
5515 if (!sl
.IsUnreachable
) {
5516 section_rc
= new Reachability ();
5520 if (section_rc
.IsUnreachable
) {
5522 // Common case. Previous label section end is unreachable as
5523 // it ends with break, return, etc. For next section revert
5524 // to reachable again unless we have constant switch block
5526 section_rc
= constant_label
!= null && constant_label
!= sl
?
5527 Reachability
.CreateUnreachable () :
5528 new Reachability ();
5529 } else if (prev_label
!= null) {
5531 // Error case as control cannot fall through from one case label
5533 sl
.SectionStart
= false;
5534 s
= new MissingBreak (prev_label
);
5535 s
.MarkReachable (rc
);
5536 block
.Statements
.Insert (i
- 1, s
);
5538 } else if (constant_label
!= null && constant_label
!= sl
) {
5540 // Special case for the first unreachable label in constant
5543 section_rc
= Reachability
.CreateUnreachable ();
5549 section_rc
= s
.MarkReachable (section_rc
);
5552 if (!section_rc
.IsUnreachable
&& prev_label
!= null) {
5553 prev_label
.SectionStart
= false;
5554 var s
= new MissingBreak (prev_label
) {
5558 s
.MarkReachable (rc
);
5559 block
.Statements
.Add (s
);
5563 // Reachability can affect parent only when all possible paths are handled but
5564 // we still need to run reachability check on switch body to check for fall-through
5566 if (case_default
== null && constant_label
== null)
5570 // We have at least one local exit from the switch
5575 return Reachability
.CreateUnreachable ();
5578 public void RegisterGotoCase (GotoCase gotoCase
, Constant
value)
5580 if (goto_cases
== null)
5581 goto_cases
= new List
<Tuple
<GotoCase
, Constant
>> ();
5583 goto_cases
.Add (Tuple
.Create (gotoCase
, value));
5587 // Converts string switch into string hashtable
5589 void ResolveStringSwitchMap (ResolveContext ec
)
5591 FullNamedExpression string_dictionary_type
;
5592 if (ec
.Module
.PredefinedTypes
.Dictionary
.Define ()) {
5593 string_dictionary_type
= new TypeExpression (
5594 ec
.Module
.PredefinedTypes
.Dictionary
.TypeSpec
.MakeGenericType (ec
,
5595 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }
),
5597 } else if (ec
.Module
.PredefinedTypes
.Hashtable
.Define ()) {
5598 string_dictionary_type
= new TypeExpression (ec
.Module
.PredefinedTypes
.Hashtable
.TypeSpec
, loc
);
5600 ec
.Module
.PredefinedTypes
.Dictionary
.Resolve ();
5604 var ctype
= ec
.CurrentMemberDefinition
.Parent
.PartialContainer
;
5605 Field field
= new Field (ctype
, string_dictionary_type
,
5606 Modifiers
.STATIC
| Modifiers
.PRIVATE
| Modifiers
.COMPILER_GENERATED
,
5607 new MemberName (CompilerGeneratedContainer
.MakeName (null, "f", "switch$map", ec
.Module
.CounterSwitchTypes
++), loc
), null);
5608 if (!field
.Define ())
5610 ctype
.AddField (field
);
5612 var init
= new List
<Expression
> ();
5614 labels
= new Dictionary
<long, SwitchLabel
> (string_labels
.Count
);
5615 string value = null;
5617 foreach (SwitchLabel sl
in case_labels
) {
5619 if (sl
.SectionStart
)
5620 labels
.Add (++counter
, sl
);
5622 if (sl
== case_default
|| sl
== case_null
)
5625 value = (string) sl
.Converted
.GetValue ();
5626 var init_args
= new List
<Expression
> (2);
5627 init_args
.Add (new StringLiteral (ec
.BuiltinTypes
, value, sl
.Location
));
5629 sl
.Converted
= new IntConstant (ec
.BuiltinTypes
, counter
, loc
);
5630 init_args
.Add (sl
.Converted
);
5632 init
.Add (new CollectionElementInitializer (init_args
, loc
));
5635 Arguments args
= new Arguments (1);
5636 args
.Add (new Argument (new IntConstant (ec
.BuiltinTypes
, init
.Count
, loc
)));
5637 Expression initializer
= new NewInitialize (string_dictionary_type
, args
,
5638 new CollectionOrObjectInitializers (init
, loc
), loc
);
5640 switch_cache_field
= new FieldExpr (field
, loc
);
5641 string_dictionary
= new SimpleAssign (switch_cache_field
, initializer
.Resolve (ec
));
5644 void DoEmitStringSwitch (EmitContext ec
)
5646 Label l_initialized
= ec
.DefineLabel ();
5649 // Skip initialization when value is null
5651 value.EmitBranchable (ec
, nullLabel
, false);
5654 // Check if string dictionary is initialized and initialize
5656 switch_cache_field
.EmitBranchable (ec
, l_initialized
, true);
5657 using (ec
.With (BuilderContext
.Options
.OmitDebugInfo
, true)) {
5658 string_dictionary
.EmitStatement (ec
);
5660 ec
.MarkLabel (l_initialized
);
5662 LocalTemporary string_switch_variable
= new LocalTemporary (ec
.BuiltinTypes
.Int
);
5664 ResolveContext rc
= new ResolveContext (ec
.MemberContext
);
5666 if (switch_cache_field
.Type
.IsGeneric
) {
5667 Arguments get_value_args
= new Arguments (2);
5668 get_value_args
.Add (new Argument (value));
5669 get_value_args
.Add (new Argument (string_switch_variable
, Argument
.AType
.Out
));
5670 Expression get_item
= new Invocation (new MemberAccess (switch_cache_field
, "TryGetValue", loc
), get_value_args
).Resolve (rc
);
5671 if (get_item
== null)
5675 // A value was not found, go to default case
5677 get_item
.EmitBranchable (ec
, defaultLabel
, false);
5679 Arguments get_value_args
= new Arguments (1);
5680 get_value_args
.Add (new Argument (value));
5682 Expression get_item
= new ElementAccess (switch_cache_field
, get_value_args
, loc
).Resolve (rc
);
5683 if (get_item
== null)
5686 LocalTemporary get_item_object
= new LocalTemporary (ec
.BuiltinTypes
.Object
);
5687 get_item_object
.EmitAssign (ec
, get_item
, true, false);
5688 ec
.Emit (OpCodes
.Brfalse
, defaultLabel
);
5690 ExpressionStatement get_item_int
= (ExpressionStatement
) new SimpleAssign (string_switch_variable
,
5691 new Cast (new TypeExpression (ec
.BuiltinTypes
.Int
, loc
), get_item_object
, loc
)).Resolve (rc
);
5693 get_item_int
.EmitStatement (ec
);
5694 get_item_object
.Release (ec
);
5697 EmitTableSwitch (ec
, string_switch_variable
);
5698 string_switch_variable
.Release (ec
);
5702 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5704 void EmitShortSwitch (EmitContext ec
)
5706 MethodSpec equal_method
= null;
5707 if (SwitchType
.BuiltinType
== BuiltinTypeSpec
.Type
.String
) {
5708 equal_method
= ec
.Module
.PredefinedMembers
.StringEqual
.Resolve (loc
);
5711 if (equal_method
!= null) {
5712 value.EmitBranchable (ec
, nullLabel
, false);
5715 for (int i
= 0; i
< case_labels
.Count
; ++i
) {
5716 var label
= case_labels
[i
];
5717 if (label
== case_default
|| label
== case_null
)
5720 var constant
= label
.Converted
;
5722 if (constant
== null) {
5723 label
.Label
.EmitBranchable (ec
, label
.GetILLabel (ec
), true);
5727 if (equal_method
!= null) {
5731 var call
= new CallEmitter ();
5732 call
.EmitPredefined (ec
, equal_method
, new Arguments (0));
5733 ec
.Emit (OpCodes
.Brtrue
, label
.GetILLabel (ec
));
5737 if (constant
.IsZeroInteger
&& constant
.Type
.BuiltinType
!= BuiltinTypeSpec
.Type
.Long
&& constant
.Type
.BuiltinType
!= BuiltinTypeSpec
.Type
.ULong
) {
5738 value.EmitBranchable (ec
, label
.GetILLabel (ec
), false);
5744 ec
.Emit (OpCodes
.Beq
, label
.GetILLabel (ec
));
5747 ec
.Emit (OpCodes
.Br
, defaultLabel
);
5750 void EmitDispatch (EmitContext ec
)
5752 if (IsPatternMatching
) {
5753 EmitShortSwitch (ec
);
5757 if (value == null) {
5759 // Constant switch, we've already done the work if there is only 1 label
5763 foreach (var sl
in case_labels
) {
5764 if (sl
.IsUnreachable
)
5767 if (reachable
++ > 0) {
5768 var constant
= (Constant
) new_expr
;
5769 var constant_label
= FindLabel (constant
) ?? case_default
;
5771 ec
.Emit (OpCodes
.Br
, constant_label
.GetILLabel (ec
));
5779 if (string_dictionary
!= null) {
5780 DoEmitStringSwitch (ec
);
5781 } else if (case_labels
.Count
< 4 || string_labels
!= null) {
5782 EmitShortSwitch (ec
);
5784 EmitTableSwitch (ec
, value);
5788 protected override void DoEmit (EmitContext ec
)
5791 // Setup the codegen context
5793 Label old_end
= ec
.LoopEnd
;
5794 Switch old_switch
= ec
.Switch
;
5796 ec
.LoopEnd
= ec
.DefineLabel ();
5799 defaultLabel
= case_default
== null ? ec
.LoopEnd
: case_default
.GetILLabel (ec
);
5800 nullLabel
= case_null
== null ? defaultLabel
: case_null
.GetILLabel (ec
);
5802 if (value != null) {
5805 var switch_expr
= new_expr
?? Expr
;
5807 unwrap
.EmitCheck (ec
);
5808 ec
.Emit (OpCodes
.Brfalse
, nullLabel
);
5809 value.EmitAssign (ec
, switch_expr
, false, false);
5810 } else if (switch_expr
!= value) {
5811 value.EmitAssign (ec
, switch_expr
, false, false);
5816 // Next statement is compiler generated we don't need extra
5817 // nop when we can use the statement for sequence point
5819 ec
.Mark (block
.StartLocation
);
5820 block
.IsCompilerGenerated
= true;
5822 new_expr
.EmitSideEffect (ec
);
5827 // Restore context state.
5828 ec
.MarkLabel (ec
.LoopEnd
);
5831 // Restore the previous context
5833 ec
.LoopEnd
= old_end
;
5834 ec
.Switch
= old_switch
;
5837 protected override void CloneTo (CloneContext clonectx
, Statement t
)
5839 Switch target
= (Switch
) t
;
5841 target
.Expr
= Expr
.Clone (clonectx
);
5842 target
.Statement
= target
.block
= (ExplicitBlock
) block
.Clone (clonectx
);
5845 public override object Accept (StructuralVisitor visitor
)
5847 return visitor
.Visit (this);
5850 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc
)
5852 if (case_default
== null && !(new_expr
is Constant
))
5855 if (end_reachable_das
== null)
5856 end_reachable_das
= new List
<DefiniteAssignmentBitSet
> ();
5858 end_reachable_das
.Add (fc
.DefiniteAssignment
);
5861 public override void SetEndReachable ()
5863 end_reachable
= true;
5867 // A place where execution can restart in a state machine
5868 public abstract class ResumableStatement
: Statement
5871 protected Label resume_point
;
5873 public Label
PrepareForEmit (EmitContext ec
)
5877 resume_point
= ec
.DefineLabel ();
5879 return resume_point
;
5882 public virtual Label
PrepareForDispose (EmitContext ec
, Label end
)
5887 public virtual void EmitForDispose (EmitContext ec
, LocalBuilder pc
, Label end
, bool have_dispatcher
)
5892 public abstract class TryFinallyBlock
: ExceptionStatement
5894 protected Statement stmt
;
5895 Label dispose_try_block
;
5896 bool prepared_for_dispose
, emitted_dispose
;
5897 Method finally_host
;
5899 protected TryFinallyBlock (Statement stmt
, Location loc
)
5907 public Statement Statement
{
5915 protected abstract void EmitTryBody (EmitContext ec
);
5916 public abstract void EmitFinallyBody (EmitContext ec
);
5918 public override Label
PrepareForDispose (EmitContext ec
, Label end
)
5920 if (!prepared_for_dispose
) {
5921 prepared_for_dispose
= true;
5922 dispose_try_block
= ec
.DefineLabel ();
5924 return dispose_try_block
;
5927 protected sealed override void DoEmit (EmitContext ec
)
5929 EmitTryBodyPrepare (ec
);
5932 bool beginFinally
= EmitBeginFinallyBlock (ec
);
5934 Label start_finally
= ec
.DefineLabel ();
5935 if (resume_points
!= null && beginFinally
) {
5936 var state_machine
= (StateMachineInitializer
) ec
.CurrentAnonymousMethod
;
5938 ec
.Emit (OpCodes
.Ldloc
, state_machine
.SkipFinally
);
5939 ec
.Emit (OpCodes
.Brfalse_S
, start_finally
);
5940 ec
.Emit (OpCodes
.Endfinally
);
5943 ec
.MarkLabel (start_finally
);
5945 if (finally_host
!= null) {
5946 finally_host
.Define ();
5947 finally_host
.PrepareEmit ();
5948 finally_host
.Emit ();
5950 // Now it's safe to add, to close it properly and emit sequence points
5951 finally_host
.Parent
.AddMember (finally_host
);
5953 var ce
= new CallEmitter ();
5954 ce
.InstanceExpression
= new CompilerGeneratedThis (ec
.CurrentType
, loc
);
5955 ce
.EmitPredefined (ec
, finally_host
.Spec
, new Arguments (0), true);
5957 EmitFinallyBody (ec
);
5961 ec
.EndExceptionBlock ();
5964 public override void EmitForDispose (EmitContext ec
, LocalBuilder pc
, Label end
, bool have_dispatcher
)
5966 if (emitted_dispose
)
5969 emitted_dispose
= true;
5971 Label end_of_try
= ec
.DefineLabel ();
5973 // Ensure that the only way we can get into this code is through a dispatcher
5974 if (have_dispatcher
)
5975 ec
.Emit (OpCodes
.Br
, end
);
5977 ec
.BeginExceptionBlock ();
5979 ec
.MarkLabel (dispose_try_block
);
5981 Label
[] labels
= null;
5982 for (int i
= 0; i
< resume_points
.Count
; ++i
) {
5983 ResumableStatement s
= resume_points
[i
];
5984 Label ret
= s
.PrepareForDispose (ec
, end_of_try
);
5985 if (ret
.Equals (end_of_try
) && labels
== null)
5987 if (labels
== null) {
5988 labels
= new Label
[resume_points
.Count
];
5989 for (int j
= 0; j
< i
; ++j
)
5990 labels
[j
] = end_of_try
;
5995 if (labels
!= null) {
5997 for (j
= 1; j
< labels
.Length
; ++j
)
5998 if (!labels
[0].Equals (labels
[j
]))
6000 bool emit_dispatcher
= j
< labels
.Length
;
6002 if (emit_dispatcher
) {
6003 ec
.Emit (OpCodes
.Ldloc
, pc
);
6004 ec
.EmitInt (first_resume_pc
);
6005 ec
.Emit (OpCodes
.Sub
);
6006 ec
.Emit (OpCodes
.Switch
, labels
);
6009 foreach (ResumableStatement s
in resume_points
)
6010 s
.EmitForDispose (ec
, pc
, end_of_try
, emit_dispatcher
);
6013 ec
.MarkLabel (end_of_try
);
6015 ec
.BeginFinallyBlock ();
6017 if (finally_host
!= null) {
6018 var ce
= new CallEmitter ();
6019 ce
.InstanceExpression
= new CompilerGeneratedThis (ec
.CurrentType
, loc
);
6020 ce
.EmitPredefined (ec
, finally_host
.Spec
, new Arguments (0), true);
6022 EmitFinallyBody (ec
);
6025 ec
.EndExceptionBlock ();
6028 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
6030 var res
= stmt
.FlowAnalysis (fc
);
6031 parent_try_block
= null;
6035 protected virtual bool EmitBeginFinallyBlock (EmitContext ec
)
6037 ec
.BeginFinallyBlock ();
6041 public override Reachability
MarkReachable (Reachability rc
)
6043 base.MarkReachable (rc
);
6044 return Statement
.MarkReachable (rc
);
6047 public override bool Resolve (BlockContext bc
)
6051 parent_try_block
= bc
.CurrentTryBlock
;
6052 bc
.CurrentTryBlock
= this;
6054 if (stmt
is TryCatch
) {
6055 ok
= stmt
.Resolve (bc
);
6057 using (bc
.Set (ResolveContext
.Options
.TryScope
)) {
6058 ok
= stmt
.Resolve (bc
);
6062 bc
.CurrentTryBlock
= parent_try_block
;
6065 // Finally block inside iterator is called from MoveNext and
6066 // Dispose methods that means we need to lift the block into
6067 // newly created host method to emit the body only once. The
6068 // original block then simply calls the newly generated method.
6070 if (bc
.CurrentIterator
!= null && !bc
.IsInProbingMode
) {
6071 var b
= stmt
as Block
;
6072 if (b
!= null && b
.Explicit
.HasYield
) {
6073 finally_host
= bc
.CurrentIterator
.CreateFinallyHost (this);
6077 return base.Resolve (bc
) && ok
;
6082 // Base class for blocks using exception handling
6084 public abstract class ExceptionStatement
: ResumableStatement
6086 protected List
<ResumableStatement
> resume_points
;
6087 protected int first_resume_pc
;
6088 protected ExceptionStatement parent_try_block
;
6089 protected int first_catch_resume_pc
= -1;
6091 protected ExceptionStatement (Location loc
)
6096 protected virtual void EmitTryBodyPrepare (EmitContext ec
)
6098 StateMachineInitializer state_machine
= null;
6099 if (resume_points
!= null) {
6100 state_machine
= (StateMachineInitializer
) ec
.CurrentAnonymousMethod
;
6102 ec
.EmitInt ((int) IteratorStorey
.State
.Running
);
6103 ec
.Emit (OpCodes
.Stloc
, state_machine
.CurrentPC
);
6107 // The resume points in catch section when this is try-catch-finally
6109 if (IsRewrittenTryCatchFinally ()) {
6110 ec
.BeginExceptionBlock ();
6112 if (first_catch_resume_pc
>= 0) {
6114 ec
.MarkLabel (resume_point
);
6116 // For normal control flow, we want to fall-through the Switch
6117 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6118 ec
.Emit (OpCodes
.Ldloc
, state_machine
.CurrentPC
);
6119 ec
.EmitInt (first_resume_pc
+ first_catch_resume_pc
);
6120 ec
.Emit (OpCodes
.Sub
);
6122 var labels
= new Label
[resume_points
.Count
- first_catch_resume_pc
];
6123 for (int i
= 0; i
< labels
.Length
; ++i
)
6124 labels
[i
] = resume_points
[i
+ first_catch_resume_pc
].PrepareForEmit (ec
);
6125 ec
.Emit (OpCodes
.Switch
, labels
);
6129 ec
.BeginExceptionBlock ();
6132 // The resume points for try section
6134 if (resume_points
!= null && first_catch_resume_pc
!= 0) {
6135 if (first_catch_resume_pc
< 0)
6136 ec
.MarkLabel (resume_point
);
6138 // For normal control flow, we want to fall-through the Switch
6139 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6140 ec
.Emit (OpCodes
.Ldloc
, state_machine
.CurrentPC
);
6141 ec
.EmitInt (first_resume_pc
);
6142 ec
.Emit (OpCodes
.Sub
);
6144 var labels
= new Label
[first_catch_resume_pc
> 0 ? first_catch_resume_pc
: resume_points
.Count
];
6145 for (int i
= 0; i
< labels
.Length
; ++i
)
6146 labels
[i
] = resume_points
[i
].PrepareForEmit (ec
);
6147 ec
.Emit (OpCodes
.Switch
, labels
);
6151 bool IsRewrittenTryCatchFinally ()
6153 var tf
= this as TryFinally
;
6157 var tc
= tf
.Statement
as TryCatch
;
6161 return tf
.FinallyBlock
.HasAwait
|| tc
.HasClauseWithAwait
;
6164 public int AddResumePoint (ResumableStatement stmt
, int pc
, StateMachineInitializer stateMachine
, TryCatch catchBlock
)
6166 if (parent_try_block
!= null) {
6167 pc
= parent_try_block
.AddResumePoint (this, pc
, stateMachine
, catchBlock
);
6169 pc
= stateMachine
.AddResumePoint (this);
6172 if (resume_points
== null) {
6173 resume_points
= new List
<ResumableStatement
> ();
6174 first_resume_pc
= pc
;
6177 if (pc
!= first_resume_pc
+ resume_points
.Count
)
6178 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6180 var tf
= this as TryFinally
;
6181 if (tf
!= null && tf
.Statement
== catchBlock
&& first_catch_resume_pc
< 0) {
6182 first_catch_resume_pc
= resume_points
.Count
;
6185 resume_points
.Add (stmt
);
6190 public class Lock
: TryFinallyBlock
6193 TemporaryVariableReference expr_copy
;
6194 TemporaryVariableReference lock_taken
;
6196 public Lock (Expression expr
, Statement stmt
, Location loc
)
6202 public Expression Expr
{
6208 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
6210 expr
.FlowAnalysis (fc
);
6211 return base.DoFlowAnalysis (fc
);
6214 public override bool Resolve (BlockContext ec
)
6216 expr
= expr
.Resolve (ec
);
6220 if (!TypeSpec
.IsReferenceType (expr
.Type
) && expr
.Type
!= InternalType
.ErrorType
) {
6221 ec
.Report
.Error (185, loc
,
6222 "`{0}' is not a reference type as required by the lock statement",
6223 expr
.Type
.GetSignatureForError ());
6226 if (expr
.Type
.IsGenericParameter
) {
6227 expr
= Convert
.ImplicitTypeParameterConversion (expr
, (TypeParameterSpec
)expr
.Type
, ec
.BuiltinTypes
.Object
);
6230 VariableReference lv
= expr
as VariableReference
;
6233 locked
= lv
.IsLockedByStatement
;
6234 lv
.IsLockedByStatement
= true;
6241 // Have to keep original lock value around to unlock same location
6242 // in the case of original value has changed or is null
6244 expr_copy
= TemporaryVariableReference
.Create (ec
.BuiltinTypes
.Object
, ec
.CurrentBlock
, loc
);
6245 expr_copy
.Resolve (ec
);
6248 // Ensure Monitor methods are available
6250 if (ResolvePredefinedMethods (ec
) > 1) {
6251 lock_taken
= TemporaryVariableReference
.Create (ec
.BuiltinTypes
.Bool
, ec
.CurrentBlock
, loc
);
6252 lock_taken
.Resolve (ec
);
6255 using (ec
.Set (ResolveContext
.Options
.LockScope
)) {
6260 lv
.IsLockedByStatement
= locked
;
6266 protected override void EmitTryBodyPrepare (EmitContext ec
)
6268 expr_copy
.EmitAssign (ec
, expr
);
6270 if (lock_taken
!= null) {
6272 // Initialize ref variable
6274 lock_taken
.EmitAssign (ec
, new BoolLiteral (ec
.BuiltinTypes
, false, loc
));
6277 // Monitor.Enter (expr_copy)
6279 expr_copy
.Emit (ec
);
6280 ec
.Emit (OpCodes
.Call
, ec
.Module
.PredefinedMembers
.MonitorEnter
.Get ());
6283 base.EmitTryBodyPrepare (ec
);
6286 protected override void EmitTryBody (EmitContext ec
)
6289 // Monitor.Enter (expr_copy, ref lock_taken)
6291 if (lock_taken
!= null) {
6292 expr_copy
.Emit (ec
);
6293 lock_taken
.LocalInfo
.CreateBuilder (ec
);
6294 lock_taken
.AddressOf (ec
, AddressOp
.Load
);
6295 ec
.Emit (OpCodes
.Call
, ec
.Module
.PredefinedMembers
.MonitorEnter_v4
.Get ());
6298 Statement
.Emit (ec
);
6301 public override void EmitFinallyBody (EmitContext ec
)
6304 // if (lock_taken) Monitor.Exit (expr_copy)
6306 Label skip
= ec
.DefineLabel ();
6308 if (lock_taken
!= null) {
6309 lock_taken
.Emit (ec
);
6310 ec
.Emit (OpCodes
.Brfalse_S
, skip
);
6313 expr_copy
.Emit (ec
);
6314 var m
= ec
.Module
.PredefinedMembers
.MonitorExit
.Resolve (loc
);
6316 ec
.Emit (OpCodes
.Call
, m
);
6318 ec
.MarkLabel (skip
);
6321 int ResolvePredefinedMethods (ResolveContext rc
)
6323 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6324 var m
= rc
.Module
.PredefinedMembers
.MonitorEnter_v4
.Get ();
6328 m
= rc
.Module
.PredefinedMembers
.MonitorEnter
.Get ();
6332 rc
.Module
.PredefinedMembers
.MonitorEnter_v4
.Resolve (loc
);
6336 protected override void CloneTo (CloneContext clonectx
, Statement t
)
6338 Lock target
= (Lock
) t
;
6340 target
.expr
= expr
.Clone (clonectx
);
6341 target
.stmt
= Statement
.Clone (clonectx
);
6344 public override object Accept (StructuralVisitor visitor
)
6346 return visitor
.Visit (this);
6351 public class Unchecked
: Statement
{
6354 public Unchecked (Block b
, Location loc
)
6361 public override bool Resolve (BlockContext ec
)
6363 using (ec
.With (ResolveContext
.Options
.AllCheckStateFlags
, false))
6364 return Block
.Resolve (ec
);
6367 protected override void DoEmit (EmitContext ec
)
6369 using (ec
.With (EmitContext
.Options
.CheckedScope
, false))
6373 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
6375 return Block
.FlowAnalysis (fc
);
6378 public override Reachability
MarkReachable (Reachability rc
)
6380 base.MarkReachable (rc
);
6381 return Block
.MarkReachable (rc
);
6384 protected override void CloneTo (CloneContext clonectx
, Statement t
)
6386 Unchecked target
= (Unchecked
) t
;
6388 target
.Block
= clonectx
.LookupBlock (Block
);
6391 public override object Accept (StructuralVisitor visitor
)
6393 return visitor
.Visit (this);
6397 public class Checked
: Statement
{
6400 public Checked (Block b
, Location loc
)
6403 b
.Unchecked
= false;
6407 public override bool Resolve (BlockContext ec
)
6409 using (ec
.With (ResolveContext
.Options
.AllCheckStateFlags
, true))
6410 return Block
.Resolve (ec
);
6413 protected override void DoEmit (EmitContext ec
)
6415 using (ec
.With (EmitContext
.Options
.CheckedScope
, true))
6419 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
6421 return Block
.FlowAnalysis (fc
);
6424 public override Reachability
MarkReachable (Reachability rc
)
6426 base.MarkReachable (rc
);
6427 return Block
.MarkReachable (rc
);
6430 protected override void CloneTo (CloneContext clonectx
, Statement t
)
6432 Checked target
= (Checked
) t
;
6434 target
.Block
= clonectx
.LookupBlock (Block
);
6437 public override object Accept (StructuralVisitor visitor
)
6439 return visitor
.Visit (this);
6443 public class Unsafe
: Statement
{
6446 public Unsafe (Block b
, Location loc
)
6449 Block
.Unsafe
= true;
6453 public override bool Resolve (BlockContext ec
)
6455 if (ec
.CurrentIterator
!= null)
6456 Expression
.UnsafeInsideIteratorError (ec
, loc
);
6458 using (ec
.Set (ResolveContext
.Options
.UnsafeScope
))
6459 return Block
.Resolve (ec
);
6462 protected override void DoEmit (EmitContext ec
)
6467 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
6469 return Block
.FlowAnalysis (fc
);
6472 public override Reachability
MarkReachable (Reachability rc
)
6474 base.MarkReachable (rc
);
6475 return Block
.MarkReachable (rc
);
6478 protected override void CloneTo (CloneContext clonectx
, Statement t
)
6480 Unsafe target
= (Unsafe
) t
;
6482 target
.Block
= clonectx
.LookupBlock (Block
);
6485 public override object Accept (StructuralVisitor visitor
)
6487 return visitor
.Visit (this);
6494 public class Fixed
: Statement
6496 abstract class Emitter
: ShimExpression
6498 protected LocalVariable vi
;
6500 protected Emitter (Expression expr
, LocalVariable li
)
6506 public abstract void EmitExit (EmitContext ec
);
6508 public override void FlowAnalysis (FlowAnalysisContext fc
)
6510 expr
.FlowAnalysis (fc
);
6514 sealed class ExpressionEmitter
: Emitter
{
6515 public ExpressionEmitter (Expression converted
, LocalVariable li
)
6516 : base (converted
, li
)
6520 protected override Expression
DoResolve (ResolveContext rc
)
6522 throw new NotImplementedException ();
6525 public override void Emit (EmitContext ec
) {
6527 // Store pointer in pinned location
6533 public override void EmitExit (EmitContext ec
)
6536 ec
.Emit (OpCodes
.Conv_U
);
6541 class StringEmitter
: Emitter
6543 LocalVariable pinned_string
;
6545 public StringEmitter (Expression expr
, LocalVariable li
)
6550 protected override Expression
DoResolve (ResolveContext rc
)
6552 pinned_string
= new LocalVariable (vi
.Block
, "$pinned",
6553 LocalVariable
.Flags
.FixedVariable
| LocalVariable
.Flags
.CompilerGenerated
| LocalVariable
.Flags
.Used
,
6555 pinned_string
.Type
= rc
.BuiltinTypes
.String
;
6558 eclass
= ExprClass
.Variable
;
6559 type
= rc
.BuiltinTypes
.Int
;
6563 public override void Emit (EmitContext ec
)
6565 pinned_string
.CreateBuilder (ec
);
6568 pinned_string
.EmitAssign (ec
);
6570 // TODO: Should use Binary::Add
6571 pinned_string
.Emit (ec
);
6572 ec
.Emit (OpCodes
.Conv_U
);
6574 var m
= ec
.Module
.PredefinedMembers
.RuntimeHelpersOffsetToStringData
.Resolve (loc
);
6578 var null_value
= ec
.DefineLabel ();
6581 ec
.Emit (OpCodes
.Brfalse_S
, null_value
);
6584 PropertyExpr pe
= new PropertyExpr (m
, pinned_string
.Location
);
6585 //pe.InstanceExpression = pinned_string;
6586 pe
.Resolve (new ResolveContext (ec
.MemberContext
)).Emit (ec
);
6588 ec
.Emit (OpCodes
.Add
);
6591 ec
.MarkLabel (null_value
);
6594 public override void EmitExit (EmitContext ec
)
6597 pinned_string
.EmitAssign (ec
);
6601 public class VariableDeclaration
: BlockVariable
6603 public VariableDeclaration (FullNamedExpression type
, LocalVariable li
)
6608 protected override Expression
ResolveInitializer (BlockContext bc
, LocalVariable li
, Expression initializer
)
6610 if (!Variable
.Type
.IsPointer
&& li
== Variable
) {
6611 bc
.Report
.Error (209, TypeExpression
.Location
,
6612 "The type of locals declared in a fixed statement must be a pointer type");
6616 var res
= initializer
.Resolve (bc
);
6623 var ac
= res
.Type
as ArrayContainer
;
6625 TypeSpec array_type
= ac
.Element
;
6628 // Provided that array_type is unmanaged,
6630 if (!TypeManager
.VerifyUnmanaged (bc
.Module
, array_type
, loc
))
6633 Expression res_init
;
6634 if (ExpressionAnalyzer
.IsInexpensiveLoad (res
)) {
6637 var expr_variable
= LocalVariable
.CreateCompilerGenerated (ac
, bc
.CurrentBlock
, loc
);
6638 res_init
= new CompilerAssign (expr_variable
.CreateReferenceExpression (bc
, loc
), res
, loc
);
6639 res
= expr_variable
.CreateReferenceExpression (bc
, loc
);
6643 // and T* is implicitly convertible to the
6644 // pointer type given in the fixed statement.
6646 ArrayPtr array_ptr
= new ArrayPtr (res
, array_type
, loc
);
6648 Expression converted
= Convert
.ImplicitConversionRequired (bc
, array_ptr
.Resolve (bc
), li
.Type
, loc
);
6649 if (converted
== null)
6653 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6655 converted
= new Conditional (new BooleanExpression (new Binary (Binary
.Operator
.LogicalOr
,
6656 new Binary (Binary
.Operator
.Equality
, res_init
, new NullLiteral (loc
)),
6657 new Binary (Binary
.Operator
.Equality
, new MemberAccess (res
, "Length"), new IntConstant (bc
.BuiltinTypes
, 0, loc
)))),
6658 new NullLiteral (loc
),
6661 converted
= converted
.Resolve (bc
);
6663 return new ExpressionEmitter (converted
, li
);
6669 if (res
.Type
.BuiltinType
== BuiltinTypeSpec
.Type
.String
) {
6670 return new StringEmitter (res
, li
).Resolve (bc
);
6673 // Case 3: fixed buffer
6674 if (res
is FixedBufferPtr
) {
6675 return new ExpressionEmitter (res
, li
);
6679 // Case 4: & object.
6681 Unary u
= res
as Unary
;
6683 bool already_fixed
= true;
6685 if (u
.Oper
== Unary
.Operator
.AddressOf
) {
6686 IVariableReference vr
= u
.Expr
as IVariableReference
;
6687 if (vr
== null || !vr
.IsFixed
) {
6688 already_fixed
= false;
6692 if (already_fixed
) {
6693 bc
.Report
.Error (213, loc
, "You cannot use the fixed statement to take the address of an already fixed expression");
6697 res
= Convert
.ImplicitConversionRequired (bc
, res
, li
.Type
, loc
);
6698 return new ExpressionEmitter (res
, li
);
6701 if (initializer
is Cast
) {
6702 bc
.Report
.Error (254, initializer
.Location
, "The right hand side of a fixed statement assignment may not be a cast expression");
6707 // Case 5: by-ref GetPinnableReference method on the rhs expression
6709 var method
= GetPinnableReference (bc
, res
);
6710 if (method
== null) {
6711 bc
.Report
.Error (8385, initializer
.Location
, "The given expression cannot be used in a fixed statement");
6715 var compiler
= bc
.Module
.Compiler
;
6716 if (compiler
.Settings
.Version
< LanguageVersion
.V_7_3
) {
6717 bc
.Report
.FeatureIsNotAvailable (compiler
, initializer
.Location
, "extensible fixed statement");
6720 method
.InstanceExpression
= res
;
6721 res
= new Invocation
.Predefined (method
, null).ResolveLValue (bc
, EmptyExpression
.OutAccess
);
6725 ReferenceContainer rType
= (ReferenceContainer
)method
.BestCandidateReturnType
;
6726 PointerContainer lType
= li
.Type
as PointerContainer
;
6727 if (rType
.Element
!= lType
?.Element
) {
6728 // CSC: Should be better error code
6729 res
.Error_ValueCannotBeConverted (bc
, lType
, false);
6733 li
.SetIsPointerByRef ();
6734 return new ExpressionEmitter (res
, li
);
6737 MethodGroupExpr
GetPinnableReference (BlockContext bc
, Expression expr
)
6739 TypeSpec type
= expr
.Type
;
6740 var mexpr
= Expression
.MemberLookup (bc
, false, type
,
6741 "GetPinnableReference", 0, Expression
.MemberLookupRestrictions
.ExactArity
, loc
);
6746 var mg
= mexpr
as MethodGroupExpr
;
6750 mg
.InstanceExpression
= expr
;
6752 // TODO: handle extension methods
6753 Arguments args
= new Arguments (0);
6754 mg
= mg
.OverloadResolve (bc
, ref args
, null, OverloadResolver
.Restrictions
.None
);
6756 if (mg
== null || mg
.BestCandidate
.IsStatic
|| !mg
.BestCandidate
.IsPublic
|| mg
.BestCandidateReturnType
.Kind
!= MemberKind
.ByRef
|| !mg
.BestCandidate
.Parameters
.IsEmpty
) {
6757 if (bc
.Module
.Compiler
.Settings
.Version
> LanguageVersion
.V_7_2
) {
6758 bc
.Report
.Warning (280, 2, expr
.Location
, "`{0}' has the wrong signature to be used in extensible fixed statement", mg
.GetSignatureForError ());
6769 VariableDeclaration decl
;
6770 Statement statement
;
6773 public Fixed (VariableDeclaration decl
, Statement stmt
, Location l
)
6782 public Statement Statement
{
6788 public BlockVariable Variables
{
6796 public override bool Resolve (BlockContext bc
)
6798 using (bc
.Set (ResolveContext
.Options
.FixedInitializerScope
)) {
6799 if (!decl
.Resolve (bc
))
6803 return statement
.Resolve (bc
);
6806 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
6808 decl
.FlowAnalysis (fc
);
6809 return statement
.FlowAnalysis (fc
);
6812 protected override void DoEmit (EmitContext ec
)
6814 decl
.Variable
.CreateBuilder (ec
);
6815 decl
.Initializer
.Emit (ec
);
6816 if (decl
.Declarators
!= null) {
6817 foreach (var d
in decl
.Declarators
) {
6818 d
.Variable
.CreateBuilder (ec
);
6819 d
.Initializer
.Emit (ec
);
6823 statement
.Emit (ec
);
6829 // Clear the pinned variable
6831 ((Emitter
) decl
.Initializer
).EmitExit (ec
);
6832 if (decl
.Declarators
!= null) {
6833 foreach (var d
in decl
.Declarators
) {
6834 ((Emitter
)d
.Initializer
).EmitExit (ec
);
6839 public override Reachability
MarkReachable (Reachability rc
)
6841 base.MarkReachable (rc
);
6843 decl
.MarkReachable (rc
);
6845 rc
= statement
.MarkReachable (rc
);
6847 // TODO: What if there is local exit?
6848 has_ret
= rc
.IsUnreachable
;
6852 protected override void CloneTo (CloneContext clonectx
, Statement t
)
6854 Fixed target
= (Fixed
) t
;
6856 target
.decl
= (VariableDeclaration
) decl
.Clone (clonectx
);
6857 target
.statement
= statement
.Clone (clonectx
);
6860 public override object Accept (StructuralVisitor visitor
)
6862 return visitor
.Visit (this);
6866 public class Catch
: Statement
6868 class CatchVariableStore
: Statement
6870 readonly Catch ctch
;
6872 public CatchVariableStore (Catch ctch
)
6877 protected override void CloneTo (CloneContext clonectx
, Statement target
)
6881 protected override void DoEmit (EmitContext ec
)
6883 // Emits catch variable debug information inside correct block
6884 ctch
.EmitCatchVariableStore (ec
);
6887 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
6893 class FilterStatement
: Statement
6895 readonly Catch ctch
;
6897 public FilterStatement (Catch ctch
)
6902 protected override void CloneTo (CloneContext clonectx
, Statement target
)
6906 protected override void DoEmit (EmitContext ec
)
6908 if (ctch
.li
!= null) {
6909 if (ctch
.hoisted_temp
!= null)
6910 ctch
.hoisted_temp
.Emit (ec
);
6914 if (!ctch
.IsGeneral
&& ctch
.type
.Kind
== MemberKind
.TypeParameter
)
6915 ec
.Emit (OpCodes
.Box
, ctch
.type
);
6918 var expr_start
= ec
.DefineLabel ();
6919 var end
= ec
.DefineLabel ();
6921 ec
.Emit (OpCodes
.Brtrue_S
, expr_start
);
6923 ec
.Emit (OpCodes
.Br
, end
);
6924 ec
.MarkLabel (expr_start
);
6926 ctch
.Filter
.Emit (ec
);
6929 ec
.Emit (OpCodes
.Endfilter
);
6930 ec
.BeginFilterHandler ();
6931 ec
.Emit (OpCodes
.Pop
);
6934 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
6936 ctch
.Filter
.FlowAnalysis (fc
);
6940 public override bool Resolve (BlockContext bc
)
6942 ctch
.Filter
= ctch
.Filter
.Resolve (bc
);
6944 if (ctch
.Filter
!= null) {
6945 if (ctch
.Filter
.ContainsEmitWithAwait ()) {
6946 bc
.Report
.Error (7094, ctch
.Filter
.Location
, "The `await' operator cannot be used in the filter expression of a catch clause");
6949 var c
= ctch
.Filter
as Constant
;
6950 if (c
!= null && !c
.IsDefaultValue
) {
6951 bc
.Report
.Warning (7095, 1, ctch
.Filter
.Location
, "Exception filter expression is a constant");
6959 ExplicitBlock block
;
6961 FullNamedExpression type_expr
;
6962 CompilerAssign assign
;
6964 LocalTemporary hoisted_temp
;
6966 public Catch (ExplicitBlock block
, Location loc
)
6974 public ExplicitBlock Block
{
6980 public TypeSpec CatchType
{
6986 public Expression Filter
{
6990 public bool IsGeneral
{
6992 return type_expr
== null;
6996 public FullNamedExpression TypeExpression
{
7005 public LocalVariable Variable
{
7016 protected override void DoEmit (EmitContext ec
)
7018 if (Filter
!= null) {
7019 ec
.BeginExceptionFilterBlock ();
7020 ec
.Emit (OpCodes
.Isinst
, IsGeneral
? ec
.BuiltinTypes
.Object
: CatchType
);
7022 if (Block
.HasAwait
) {
7023 Block
.EmitScopeInitialization (ec
);
7032 ec
.BeginCatchBlock (ec
.BuiltinTypes
.Object
);
7034 ec
.BeginCatchBlock (CatchType
);
7037 ec
.Emit (OpCodes
.Pop
);
7039 if (Block
.HasAwait
) {
7041 EmitCatchVariableStore (ec
);
7047 void EmitCatchVariableStore (EmitContext ec
)
7049 li
.CreateBuilder (ec
);
7052 // For hoisted catch variable we have to use a temporary local variable
7053 // for captured variable initialization during storey setup because variable
7054 // needs to be on the stack after storey instance for stfld operation
7056 if (li
.HoistedVariant
!= null) {
7057 hoisted_temp
= new LocalTemporary (li
.Type
);
7058 hoisted_temp
.Store (ec
);
7060 // switch to assignment from temporary variable and not from top of the stack
7061 assign
.UpdateSource (hoisted_temp
);
7065 public override bool Resolve (BlockContext bc
)
7067 using (bc
.Set (ResolveContext
.Options
.CatchScope
)) {
7068 if (type_expr
== null) {
7069 if (CreateExceptionVariable (bc
.Module
.Compiler
.BuiltinTypes
.Object
)) {
7070 if (!block
.HasAwait
|| Filter
!= null)
7071 block
.AddScopeStatement (new CatchVariableStore (this));
7073 Expression source
= new EmptyExpression (li
.Type
);
7074 assign
= new CompilerAssign (new LocalVariableReference (li
, Location
.Null
), source
, Location
.Null
);
7075 Block
.AddScopeStatement (new StatementExpression (assign
, Location
.Null
));
7078 type
= type_expr
.ResolveAsType (bc
);
7083 CreateExceptionVariable (type
);
7085 if (type
.BuiltinType
!= BuiltinTypeSpec
.Type
.Exception
&& !TypeSpec
.IsBaseClass (type
, bc
.BuiltinTypes
.Exception
, false)) {
7086 bc
.Report
.Error (155, loc
, "The type caught or thrown must be derived from System.Exception");
7087 } else if (li
!= null) {
7089 li
.PrepareAssignmentAnalysis (bc
);
7091 // source variable is at the top of the stack
7092 Expression source
= new EmptyExpression (li
.Type
);
7093 if (li
.Type
.IsGenericParameter
)
7094 source
= new UnboxCast (source
, li
.Type
);
7096 if (!block
.HasAwait
|| Filter
!= null)
7097 block
.AddScopeStatement (new CatchVariableStore (this));
7100 // Uses Location.Null to hide from symbol file
7102 assign
= new CompilerAssign (new LocalVariableReference (li
, Location
.Null
), source
, Location
.Null
);
7103 Block
.AddScopeStatement (new StatementExpression (assign
, Location
.Null
));
7107 if (Filter
!= null) {
7108 Block
.AddScopeStatement (new FilterStatement (this));
7111 Block
.SetCatchBlock ();
7112 return Block
.Resolve (bc
);
7116 bool CreateExceptionVariable (TypeSpec type
)
7118 if (!Block
.HasAwait
)
7121 // TODO: Scan the block for rethrow expression
7122 //if (!Block.HasRethrow)
7125 li
= LocalVariable
.CreateCompilerGenerated (type
, block
, Location
.Null
);
7129 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
7131 if (li
!= null && !li
.IsCompilerGenerated
) {
7132 fc
.SetVariableAssigned (li
.VariableInfo
, true);
7135 return block
.FlowAnalysis (fc
);
7138 public override Reachability
MarkReachable (Reachability rc
)
7140 base.MarkReachable (rc
);
7142 var c
= Filter
as Constant
;
7143 if (c
!= null && c
.IsDefaultValue
)
7144 return Reachability
.CreateUnreachable ();
7146 return block
.MarkReachable (rc
);
7149 protected override void CloneTo (CloneContext clonectx
, Statement t
)
7151 Catch target
= (Catch
) t
;
7153 if (type_expr
!= null)
7154 target
.type_expr
= (FullNamedExpression
) type_expr
.Clone (clonectx
);
7157 target
.Filter
= Filter
.Clone (clonectx
);
7159 target
.block
= (ExplicitBlock
) clonectx
.LookupBlock (block
);
7163 public class TryFinally
: TryFinallyBlock
7166 List
<DefiniteAssignmentBitSet
> try_exit_dat
;
7167 List
<Tuple
<Label
, bool>> redirected_jumps
;
7168 Label
? start_fin_label
;
7170 public TryFinally (Statement stmt
, ExplicitBlock fini
, Location loc
)
7176 public ExplicitBlock FinallyBlock
{
7182 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector
)
7184 if (try_exit_dat
== null)
7185 try_exit_dat
= new List
<DefiniteAssignmentBitSet
> ();
7187 try_exit_dat
.Add (vector
);
7190 public override bool Resolve (BlockContext bc
)
7192 bool ok
= base.Resolve (bc
);
7194 fini
.SetFinallyBlock ();
7195 using (bc
.Set (ResolveContext
.Options
.FinallyScope
)) {
7196 ok
&= fini
.Resolve (bc
);
7202 protected override void EmitTryBody (EmitContext ec
)
7204 if (fini
.HasAwait
) {
7205 if (ec
.TryFinallyUnwind
== null)
7206 ec
.TryFinallyUnwind
= new List
<TryFinally
> ();
7208 ec
.TryFinallyUnwind
.Add (this);
7211 if (first_catch_resume_pc
< 0 && stmt
is TryCatch
)
7212 ec
.EndExceptionBlock ();
7214 ec
.TryFinallyUnwind
.Remove (this);
7216 if (start_fin_label
!= null)
7217 ec
.MarkLabel (start_fin_label
.Value
);
7225 protected override bool EmitBeginFinallyBlock (EmitContext ec
)
7230 return base.EmitBeginFinallyBlock (ec
);
7233 public override void EmitFinallyBody (EmitContext ec
)
7235 if (!fini
.HasAwait
) {
7241 // Emits catch block like
7243 // catch (object temp) {
7244 // this.exception_field = temp;
7247 var type
= ec
.BuiltinTypes
.Object
;
7248 ec
.BeginCatchBlock (type
);
7250 var temp
= ec
.GetTemporaryLocal (type
);
7251 ec
.Emit (OpCodes
.Stloc
, temp
);
7253 var exception_field
= ec
.GetTemporaryField (type
);
7254 exception_field
.AutomaticallyReuse
= false;
7256 ec
.Emit (OpCodes
.Ldloc
, temp
);
7257 exception_field
.EmitAssignFromStack (ec
);
7259 ec
.EndExceptionBlock ();
7261 ec
.FreeTemporaryLocal (temp
, type
);
7266 // Emits exception rethrow
7268 // if (this.exception_field != null)
7269 // throw this.exception_field;
7271 exception_field
.Emit (ec
);
7272 var skip_throw
= ec
.DefineLabel ();
7273 ec
.Emit (OpCodes
.Brfalse_S
, skip_throw
);
7274 exception_field
.Emit (ec
);
7275 ec
.Emit (OpCodes
.Throw
);
7276 ec
.MarkLabel (skip_throw
);
7278 exception_field
.PrepareCleanup (ec
);
7280 EmitUnwindFinallyTable (ec
);
7283 bool IsParentBlock (Block block
)
7285 for (Block b
= fini
; b
!= null; b
= b
.Parent
) {
7293 public static Label
EmitRedirectedJump (EmitContext ec
, AsyncInitializer initializer
, Label label
, Block labelBlock
, bool unwindProtect
)
7296 if (labelBlock
!= null) {
7297 for (idx
= ec
.TryFinallyUnwind
.Count
; idx
!= 0; --idx
) {
7298 var fin
= ec
.TryFinallyUnwind
[idx
- 1];
7299 if (!fin
.IsParentBlock (labelBlock
))
7306 bool set_return_state
= true;
7308 for (; idx
< ec
.TryFinallyUnwind
.Count
; ++idx
) {
7309 var fin
= ec
.TryFinallyUnwind
[idx
];
7310 if (labelBlock
!= null && !fin
.IsParentBlock (labelBlock
))
7313 fin
.EmitRedirectedExit (ec
, label
, initializer
, set_return_state
, unwindProtect
);
7314 set_return_state
= false;
7316 if (fin
.start_fin_label
== null) {
7317 fin
.start_fin_label
= ec
.DefineLabel ();
7320 label
= fin
.start_fin_label
.Value
;
7326 public static Label
EmitRedirectedReturn (EmitContext ec
, AsyncInitializer initializer
, bool unwindProtect
)
7328 return EmitRedirectedJump (ec
, initializer
, initializer
.BodyEnd
, null, unwindProtect
);
7331 void EmitRedirectedExit (EmitContext ec
, Label label
, AsyncInitializer initializer
, bool setReturnState
, bool unwindProtect
)
7333 if (redirected_jumps
== null) {
7334 redirected_jumps
= new List
<Tuple
<Label
, bool>> ();
7336 // Add fallthrough label
7337 redirected_jumps
.Add (Tuple
.Create (ec
.DefineLabel (), false));
7340 initializer
.HoistedReturnState
= ec
.GetTemporaryField (ec
.Module
.Compiler
.BuiltinTypes
.Int
, true);
7343 int index
= redirected_jumps
.FindIndex (l
=> l
.Item1
== label
);
7345 redirected_jumps
.Add (Tuple
.Create (label
, unwindProtect
));
7346 index
= redirected_jumps
.Count
- 1;
7350 // Indicates we have captured exit jump
7352 if (setReturnState
) {
7353 var value = new IntConstant (initializer
.HoistedReturnState
.Type
, index
, Location
.Null
);
7354 initializer
.HoistedReturnState
.EmitAssign (ec
, value, false, false);
7359 // Emits state table of jumps outside of try block and reload of return
7360 // value when try block returns value
7362 void EmitUnwindFinallyTable (EmitContext ec
)
7364 if (redirected_jumps
== null)
7367 var initializer
= (AsyncInitializer
)ec
.CurrentAnonymousMethod
;
7368 initializer
.HoistedReturnState
.EmitLoad (ec
);
7370 var jumps_table
= new Label
[redirected_jumps
.Count
];
7371 List
<Tuple
<Label
, Label
>> leave_redirect
= null;
7372 for (int i
= 0; i
< jumps_table
.Length
; ++i
) {
7373 var val
= redirected_jumps
[i
];
7376 if (leave_redirect
== null)
7377 leave_redirect
= new List
<Tuple
<Label
, Label
>> ();
7378 var label
= ec
.DefineLabel ();
7379 leave_redirect
.Add (Tuple
.Create (label
, val
.Item1
));
7380 jumps_table
[i
] = label
;
7382 jumps_table
[i
] = val
.Item1
;
7386 ec
.Emit (OpCodes
.Switch
, jumps_table
);
7388 if (leave_redirect
!= null) {
7389 foreach (var entry
in leave_redirect
) {
7390 ec
.MarkLabel (entry
.Item1
);
7391 ec
.Emit (OpCodes
.Leave
, entry
.Item2
);
7395 // Mark fallthrough label
7396 ec
.MarkLabel (jumps_table
[0]);
7399 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
7401 var da
= fc
.BranchDefiniteAssignment ();
7403 var tf
= fc
.TryFinally
;
7404 fc
.TryFinally
= this;
7406 var res_stmt
= Statement
.FlowAnalysis (fc
);
7410 var try_da
= fc
.DefiniteAssignment
;
7411 fc
.DefiniteAssignment
= da
;
7413 var res_fin
= fini
.FlowAnalysis (fc
);
7415 if (try_exit_dat
!= null) {
7417 // try block has global exit but we need to run definite assignment check
7418 // for parameter block out parameter after finally block because it's always
7419 // executed before exit
7421 foreach (var try_da_part
in try_exit_dat
)
7422 fc
.ParametersBlock
.CheckControlExit (fc
, fc
.DefiniteAssignment
| try_da_part
);
7424 try_exit_dat
= null;
7427 fc
.DefiniteAssignment
|= try_da
;
7428 return res_stmt
| res_fin
;
7431 public override Reachability
MarkReachable (Reachability rc
)
7434 // Mark finally block first for any exit statement in try block
7435 // to know whether the code which follows finally is reachable
7437 return fini
.MarkReachable (rc
) | base.MarkReachable (rc
);
7440 protected override void CloneTo (CloneContext clonectx
, Statement t
)
7442 TryFinally target
= (TryFinally
) t
;
7444 target
.stmt
= stmt
.Clone (clonectx
);
7446 target
.fini
= (ExplicitBlock
) clonectx
.LookupBlock (fini
);
7449 public override object Accept (StructuralVisitor visitor
)
7451 return visitor
.Visit (this);
7455 public class TryCatch
: ExceptionStatement
7458 List
<Catch
> clauses
;
7459 readonly bool inside_try_finally
;
7460 List
<Catch
> catch_sm
;
7462 public TryCatch (Block block
, List
<Catch
> catch_clauses
, Location l
, bool inside_try_finally
)
7466 this.clauses
= catch_clauses
;
7467 this.inside_try_finally
= inside_try_finally
;
7470 public List
<Catch
> Clauses
{
7476 public bool HasClauseWithAwait
{
7478 return catch_sm
!= null;
7482 public bool IsTryCatchFinally
{
7484 return inside_try_finally
;
7488 public override bool Resolve (BlockContext bc
)
7492 using (bc
.Set (ResolveContext
.Options
.TryScope
)) {
7494 parent_try_block
= bc
.CurrentTryBlock
;
7496 if (IsTryCatchFinally
) {
7497 ok
= Block
.Resolve (bc
);
7499 using (bc
.Set (ResolveContext
.Options
.TryWithCatchScope
)) {
7500 bc
.CurrentTryBlock
= this;
7501 ok
= Block
.Resolve (bc
);
7502 bc
.CurrentTryBlock
= parent_try_block
;
7507 var prev_catch
= bc
.CurrentTryCatch
;
7508 bc
.CurrentTryCatch
= this;
7510 for (int i
= 0; i
< clauses
.Count
; ++i
) {
7513 ok
&= c
.Resolve (bc
);
7515 if (c
.Block
.HasAwait
) {
7516 if (catch_sm
== null)
7517 catch_sm
= new List
<Catch
> ();
7522 if (c
.Filter
!= null)
7525 TypeSpec resolved_type
= c
.CatchType
;
7526 if (resolved_type
== null)
7529 for (int ii
= 0; ii
< clauses
.Count
; ++ii
) {
7533 if (clauses
[ii
].Filter
!= null)
7536 if (clauses
[ii
].IsGeneral
) {
7537 if (resolved_type
.BuiltinType
!= BuiltinTypeSpec
.Type
.Exception
)
7540 if (!bc
.Module
.DeclaringAssembly
.WrapNonExceptionThrows
)
7543 if (!bc
.Module
.PredefinedAttributes
.RuntimeCompatibility
.IsDefined
)
7546 bc
.Report
.Warning (1058, 1, c
.loc
,
7547 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7555 var ct
= clauses
[ii
].CatchType
;
7559 if (resolved_type
== ct
|| TypeSpec
.IsBaseClass (resolved_type
, ct
, true)) {
7560 bc
.Report
.Error (160, c
.loc
,
7561 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7562 ct
.GetSignatureForError ());
7568 bc
.CurrentTryCatch
= prev_catch
;
7570 return base.Resolve (bc
) && ok
;
7573 protected sealed override void DoEmit (EmitContext ec
)
7575 if (!inside_try_finally
)
7576 EmitTryBodyPrepare (ec
);
7580 LocalBuilder state_variable
= null;
7581 foreach (Catch c
in clauses
) {
7584 if (catch_sm
!= null) {
7585 if (state_variable
== null) {
7587 // Cannot reuse temp variable because non-catch path assumes the value is 0
7588 // which may not be true for reused local variable
7590 state_variable
= ec
.DeclareLocal (ec
.Module
.Compiler
.BuiltinTypes
.Int
, false);
7593 var index
= catch_sm
.IndexOf (c
);
7597 ec
.EmitInt (index
+ 1);
7598 ec
.Emit (OpCodes
.Stloc
, state_variable
);
7602 if (state_variable
== null) {
7603 if (!inside_try_finally
)
7604 ec
.EndExceptionBlock ();
7606 ec
.EndExceptionBlock ();
7608 ec
.Emit (OpCodes
.Ldloc
, state_variable
);
7610 var labels
= new Label
[catch_sm
.Count
+ 1];
7611 for (int i
= 0; i
< labels
.Length
; ++i
) {
7612 labels
[i
] = ec
.DefineLabel ();
7615 var end
= ec
.DefineLabel ();
7616 ec
.Emit (OpCodes
.Switch
, labels
);
7618 // 0 value is default label
7619 ec
.MarkLabel (labels
[0]);
7620 ec
.Emit (OpCodes
.Br
, end
);
7622 var atv
= ec
.AsyncThrowVariable
;
7624 for (int i
= 0; i
< catch_sm
.Count
; ++i
) {
7625 if (c
!= null && c
.Block
.HasReachableClosingBrace
)
7626 ec
.Emit (OpCodes
.Br
, end
);
7628 ec
.MarkLabel (labels
[i
+ 1]);
7631 ec
.Emit (OpCodes
.Stloc
, state_variable
);
7634 ec
.AsyncThrowVariable
= c
.Variable
;
7637 ec
.AsyncThrowVariable
= atv
;
7643 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
7645 var start_fc
= fc
.BranchDefiniteAssignment ();
7646 var res
= Block
.FlowAnalysis (fc
);
7648 DefiniteAssignmentBitSet try_fc
= res
? null : fc
.DefiniteAssignment
;
7650 foreach (var c
in clauses
) {
7651 fc
.BranchDefiniteAssignment (start_fc
);
7652 if (!c
.FlowAnalysis (fc
)) {
7654 try_fc
= fc
.DefiniteAssignment
;
7656 try_fc
&= fc
.DefiniteAssignment
;
7662 fc
.DefiniteAssignment
= try_fc
?? start_fc
;
7663 parent_try_block
= null;
7667 public override Reachability
MarkReachable (Reachability rc
)
7669 if (rc
.IsUnreachable
)
7672 base.MarkReachable (rc
);
7674 var tc_rc
= Block
.MarkReachable (rc
);
7676 foreach (var c
in clauses
)
7677 tc_rc
&= c
.MarkReachable (rc
);
7682 protected override void CloneTo (CloneContext clonectx
, Statement t
)
7684 TryCatch target
= (TryCatch
) t
;
7686 target
.Block
= clonectx
.LookupBlock (Block
);
7687 if (clauses
!= null){
7688 target
.clauses
= new List
<Catch
> ();
7689 foreach (Catch c
in clauses
)
7690 target
.clauses
.Add ((Catch
) c
.Clone (clonectx
));
7694 public override object Accept (StructuralVisitor visitor
)
7696 return visitor
.Visit (this);
7700 public class Using
: TryFinallyBlock
7702 public class VariableDeclaration
: BlockVariable
7704 Statement dispose_call
;
7706 public VariableDeclaration (FullNamedExpression type
, LocalVariable li
)
7711 public VariableDeclaration (LocalVariable li
, Location loc
)
7718 public VariableDeclaration (Expression expr
)
7721 loc
= expr
.Location
;
7727 public bool IsNested { get; private set; }
7731 public void EmitDispose (EmitContext ec
)
7733 dispose_call
.Emit (ec
);
7736 public override bool Resolve (BlockContext bc
)
7741 return base.Resolve (bc
, false);
7744 public Expression
ResolveExpression (BlockContext bc
)
7746 var e
= Initializer
.Resolve (bc
);
7750 li
= LocalVariable
.CreateCompilerGenerated (e
.Type
, bc
.CurrentBlock
, loc
);
7751 Initializer
= ResolveInitializer (bc
, Variable
, e
);
7755 protected override Expression
ResolveInitializer (BlockContext bc
, LocalVariable li
, Expression initializer
)
7757 if (li
.Type
.BuiltinType
== BuiltinTypeSpec
.Type
.Dynamic
) {
7758 initializer
= initializer
.Resolve (bc
);
7759 if (initializer
== null)
7762 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7763 Arguments args
= new Arguments (1);
7764 args
.Add (new Argument (initializer
));
7765 initializer
= new DynamicConversion (bc
.BuiltinTypes
.IDisposable
, 0, args
, initializer
.Location
).Resolve (bc
);
7766 if (initializer
== null)
7769 var var = LocalVariable
.CreateCompilerGenerated (initializer
.Type
, bc
.CurrentBlock
, loc
);
7770 dispose_call
= CreateDisposeCall (bc
, var);
7771 dispose_call
.Resolve (bc
);
7773 return base.ResolveInitializer (bc
, li
, new SimpleAssign (var.CreateReferenceExpression (bc
, loc
), initializer
, loc
));
7776 if (li
== Variable
) {
7777 CheckIDiposableConversion (bc
, li
, initializer
);
7778 dispose_call
= CreateDisposeCall (bc
, li
);
7779 dispose_call
.Resolve (bc
);
7782 return base.ResolveInitializer (bc
, li
, initializer
);
7785 protected virtual void CheckIDiposableConversion (BlockContext bc
, LocalVariable li
, Expression initializer
)
7789 if (type
.BuiltinType
!= BuiltinTypeSpec
.Type
.IDisposable
&& !CanConvertToIDisposable (bc
, type
)) {
7790 if (type
.IsNullableType
) {
7791 // it's handled in CreateDisposeCall
7795 if (type
!= InternalType
.ErrorType
) {
7796 bc
.Report
.SymbolRelatedToPreviousError (type
);
7797 var loc
= type_expr
== null ? initializer
.Location
: type_expr
.Location
;
7798 bc
.Report
.Error (1674, loc
, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7799 type
.GetSignatureForError ());
7806 static bool CanConvertToIDisposable (BlockContext bc
, TypeSpec type
)
7808 var target
= bc
.BuiltinTypes
.IDisposable
;
7809 var tp
= type
as TypeParameterSpec
;
7811 return Convert
.ImplicitTypeParameterConversion (null, tp
, target
) != null;
7813 return type
.ImplementsInterface (target
, false);
7816 protected virtual Statement
CreateDisposeCall (BlockContext bc
, LocalVariable lv
)
7818 var lvr
= lv
.CreateReferenceExpression (bc
, lv
.Location
);
7820 var loc
= lv
.Location
;
7822 var idt
= bc
.BuiltinTypes
.IDisposable
;
7823 var m
= bc
.Module
.PredefinedMembers
.IDisposableDispose
.Resolve (loc
);
7825 var dispose_mg
= MethodGroupExpr
.CreatePredefined (m
, idt
, loc
);
7826 dispose_mg
.InstanceExpression
= type
.IsNullableType
?
7827 new Cast (new TypeExpression (idt
, loc
), lvr
, loc
).Resolve (bc
) :
7831 // Hide it from symbol file via null location
7833 Statement dispose
= new StatementExpression (new Invocation (dispose_mg
, null), Location
.Null
);
7835 // Add conditional call when disposing possible null variable
7836 if (!TypeSpec
.IsValueType (type
) || type
.IsNullableType
)
7837 dispose
= new If (new Binary (Binary
.Operator
.Inequality
, lvr
, new NullLiteral (loc
)), dispose
, dispose
.loc
);
7842 public void ResolveDeclaratorInitializer (BlockContext bc
)
7844 Initializer
= base.ResolveInitializer (bc
, Variable
, Initializer
);
7847 public Statement
RewriteUsingDeclarators (BlockContext bc
, Statement stmt
)
7849 for (int i
= declarators
.Count
- 1; i
>= 0; --i
) {
7850 var d
= declarators
[i
];
7851 var vd
= new VariableDeclaration (d
.Variable
, d
.Variable
.Location
);
7852 vd
.Initializer
= d
.Initializer
;
7854 vd
.dispose_call
= CreateDisposeCall (bc
, d
.Variable
);
7855 vd
.dispose_call
.Resolve (bc
);
7857 stmt
= new Using (vd
, stmt
, d
.Variable
.Location
);
7864 public override object Accept (StructuralVisitor visitor
)
7866 return visitor
.Visit (this);
7870 VariableDeclaration decl
;
7872 public Using (VariableDeclaration decl
, Statement stmt
, Location loc
)
7878 public Using (Expression expr
, Statement stmt
, Location loc
)
7881 this.decl
= new VariableDeclaration (expr
);
7886 public Expression Expr
{
7888 return decl
.Variable
== null ? decl
.Initializer
: null;
7892 public BlockVariable Variables
{
7900 public override void Emit (EmitContext ec
)
7903 // Don't emit sequence point it will be set on variable declaration
7908 protected override void EmitTryBodyPrepare (EmitContext ec
)
7911 base.EmitTryBodyPrepare (ec
);
7914 protected override void EmitTryBody (EmitContext ec
)
7919 public override void EmitFinallyBody (EmitContext ec
)
7921 decl
.EmitDispose (ec
);
7924 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
7926 decl
.FlowAnalysis (fc
);
7927 return stmt
.FlowAnalysis (fc
);
7930 public override Reachability
MarkReachable (Reachability rc
)
7932 decl
.MarkReachable (rc
);
7933 return base.MarkReachable (rc
);
7936 public override bool Resolve (BlockContext ec
)
7938 VariableReference vr
;
7939 bool vr_locked
= false;
7941 using (ec
.Set (ResolveContext
.Options
.UsingInitializerScope
)) {
7942 if (decl
.Variable
== null) {
7943 vr
= decl
.ResolveExpression (ec
) as VariableReference
;
7945 vr_locked
= vr
.IsLockedByStatement
;
7946 vr
.IsLockedByStatement
= true;
7949 if (decl
.IsNested
) {
7950 decl
.ResolveDeclaratorInitializer (ec
);
7952 if (!decl
.Resolve (ec
))
7955 if (decl
.Declarators
!= null) {
7956 stmt
= decl
.RewriteUsingDeclarators (ec
, stmt
);
7964 var ok
= base.Resolve (ec
);
7967 vr
.IsLockedByStatement
= vr_locked
;
7972 protected override void CloneTo (CloneContext clonectx
, Statement t
)
7974 Using target
= (Using
) t
;
7976 target
.decl
= (VariableDeclaration
) decl
.Clone (clonectx
);
7977 target
.stmt
= stmt
.Clone (clonectx
);
7980 public override object Accept (StructuralVisitor visitor
)
7982 return visitor
.Visit (this);
7987 /// Implementation of the foreach C# statement
7989 public class Foreach
: LoopStatement
7991 abstract class IteratorStatement
: Statement
7993 protected readonly Foreach for_each
;
7995 protected IteratorStatement (Foreach
@foreach)
7997 this.for_each
= @foreach;
7998 this.loc
= @foreach.expr
.Location
;
8001 protected override void CloneTo (CloneContext clonectx
, Statement target
)
8003 throw new NotImplementedException ();
8006 public override void Emit (EmitContext ec
)
8008 if (ec
.EmitAccurateDebugInfo
) {
8009 ec
.Emit (OpCodes
.Nop
);
8015 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
8017 throw new NotImplementedException ();
8021 sealed class ArrayForeach
: IteratorStatement
8023 TemporaryVariableReference
[] lengths
;
8024 Expression
[] length_exprs
;
8025 StatementExpression
[] counter
;
8026 TemporaryVariableReference
[] variables
;
8028 TemporaryVariableReference copy
;
8030 public ArrayForeach (Foreach
@foreach, int rank
)
8033 counter
= new StatementExpression
[rank
];
8034 variables
= new TemporaryVariableReference
[rank
];
8035 length_exprs
= new Expression
[rank
];
8038 // Only use temporary length variables when dealing with
8039 // multi-dimensional arrays
8042 lengths
= new TemporaryVariableReference
[rank
];
8045 public override bool Resolve (BlockContext ec
)
8047 Block variables_block
= for_each
.variable
.Block
;
8048 copy
= TemporaryVariableReference
.Create (for_each
.expr
.Type
, variables_block
, loc
);
8051 int rank
= length_exprs
.Length
;
8052 Arguments list
= new Arguments (rank
);
8053 for (int i
= 0; i
< rank
; i
++) {
8054 var v
= TemporaryVariableReference
.Create (ec
.BuiltinTypes
.Int
, variables_block
, loc
);
8056 counter
[i
] = new StatementExpression (new UnaryMutator (UnaryMutator
.Mode
.PostIncrement
, v
, Location
.Null
));
8057 counter
[i
].Resolve (ec
);
8060 length_exprs
[i
] = new MemberAccess (copy
, "Length").Resolve (ec
);
8062 lengths
[i
] = TemporaryVariableReference
.Create (ec
.BuiltinTypes
.Int
, variables_block
, loc
);
8063 lengths
[i
].Resolve (ec
);
8065 Arguments args
= new Arguments (1);
8066 args
.Add (new Argument (new IntConstant (ec
.BuiltinTypes
, i
, loc
)));
8067 length_exprs
[i
] = new Invocation (new MemberAccess (copy
, "GetLength"), args
).Resolve (ec
);
8070 list
.Add (new Argument (v
));
8073 var access
= new ElementAccess (copy
, list
, loc
).Resolve (ec
);
8078 if (for_each
.type
is VarExpr
) {
8079 // Infer implicitly typed local variable from foreach array type
8080 var_type
= access
.Type
;
8082 var_type
= for_each
.type
.ResolveAsType (ec
);
8084 if (var_type
== null)
8087 access
= Convert
.ExplicitConversion (ec
, access
, var_type
, loc
);
8092 for_each
.variable
.Type
= var_type
;
8094 var prev_block
= ec
.CurrentBlock
;
8095 ec
.CurrentBlock
= variables_block
;
8096 var variable_ref
= new LocalVariableReference (for_each
.variable
, loc
).Resolve (ec
);
8097 ec
.CurrentBlock
= prev_block
;
8099 if (variable_ref
== null)
8102 for_each
.body
.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref
, access
, Location
.Null
), for_each
.type
.Location
));
8104 return for_each
.body
.Resolve (ec
);
8107 protected override void DoEmit (EmitContext ec
)
8109 copy
.EmitAssign (ec
, for_each
.expr
);
8111 int rank
= length_exprs
.Length
;
8112 Label
[] test
= new Label
[rank
];
8113 Label
[] loop
= new Label
[rank
];
8115 for (int i
= 0; i
< rank
; i
++) {
8116 test
[i
] = ec
.DefineLabel ();
8117 loop
[i
] = ec
.DefineLabel ();
8119 if (lengths
!= null)
8120 lengths
[i
].EmitAssign (ec
, length_exprs
[i
]);
8123 IntConstant zero
= new IntConstant (ec
.BuiltinTypes
, 0, loc
);
8124 for (int i
= 0; i
< rank
; i
++) {
8125 variables
[i
].EmitAssign (ec
, zero
);
8127 ec
.Emit (OpCodes
.Br
, test
[i
]);
8128 ec
.MarkLabel (loop
[i
]);
8131 for_each
.body
.Emit (ec
);
8133 ec
.MarkLabel (ec
.LoopBegin
);
8134 ec
.Mark (for_each
.expr
.Location
);
8136 for (int i
= rank
- 1; i
>= 0; i
--){
8137 counter
[i
].Emit (ec
);
8139 ec
.MarkLabel (test
[i
]);
8140 variables
[i
].Emit (ec
);
8142 if (lengths
!= null)
8143 lengths
[i
].Emit (ec
);
8145 length_exprs
[i
].Emit (ec
);
8147 ec
.Emit (OpCodes
.Blt
, loop
[i
]);
8150 ec
.MarkLabel (ec
.LoopEnd
);
8154 sealed class CollectionForeach
: IteratorStatement
, OverloadResolver
.IErrorHandler
8156 class RuntimeDispose
: Using
.VariableDeclaration
8158 public RuntimeDispose (LocalVariable lv
, Location loc
)
8164 protected override void CheckIDiposableConversion (BlockContext bc
, LocalVariable li
, Expression initializer
)
8166 // Defered to runtime check
8169 protected override Statement
CreateDisposeCall (BlockContext bc
, LocalVariable lv
)
8171 var idt
= bc
.BuiltinTypes
.IDisposable
;
8174 // Fabricates code like
8176 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
8179 var dispose_variable
= LocalVariable
.CreateCompilerGenerated (idt
, bc
.CurrentBlock
, loc
);
8181 var idisaposable_test
= new Binary (Binary
.Operator
.Inequality
, new CompilerAssign (
8182 dispose_variable
.CreateReferenceExpression (bc
, loc
),
8183 new As (lv
.CreateReferenceExpression (bc
, loc
), new TypeExpression (dispose_variable
.Type
, loc
), loc
),
8184 loc
), new NullLiteral (loc
));
8186 var m
= bc
.Module
.PredefinedMembers
.IDisposableDispose
.Resolve (loc
);
8188 var dispose_mg
= MethodGroupExpr
.CreatePredefined (m
, idt
, loc
);
8189 dispose_mg
.InstanceExpression
= dispose_variable
.CreateReferenceExpression (bc
, loc
);
8191 Statement dispose
= new StatementExpression (new Invocation (dispose_mg
, null));
8192 return new If (idisaposable_test
, dispose
, loc
);
8196 LocalVariable variable
;
8198 Statement statement
;
8199 ExpressionStatement init
;
8200 TemporaryVariableReference enumerator_variable
;
8201 bool ambiguous_getenumerator_name
;
8203 public CollectionForeach (Foreach
@foreach, LocalVariable
var, Expression expr
)
8206 this.variable
= var;
8210 void Error_WrongEnumerator (ResolveContext rc
, MethodSpec enumerator
)
8212 rc
.Report
.SymbolRelatedToPreviousError (enumerator
);
8213 rc
.Report
.Error (202, loc
,
8214 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
8215 enumerator
.ReturnType
.GetSignatureForError (), enumerator
.GetSignatureForError ());
8218 MethodGroupExpr
ResolveGetEnumerator (ResolveContext rc
)
8221 // Option 1: Try to match by name GetEnumerator first
8223 var mexpr
= Expression
.MemberLookup (rc
, false, expr
.Type
,
8224 "GetEnumerator", 0, Expression
.MemberLookupRestrictions
.ExactArity
, loc
); // TODO: What if CS0229 ?
8226 var mg
= mexpr
as MethodGroupExpr
;
8228 mg
.InstanceExpression
= expr
;
8229 Arguments args
= new Arguments (0);
8230 mg
= mg
.OverloadResolve (rc
, ref args
, this, OverloadResolver
.Restrictions
.ProbingOnly
| OverloadResolver
.Restrictions
.GetEnumeratorLookup
);
8232 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
8233 if (ambiguous_getenumerator_name
)
8236 if (mg
!= null && !mg
.BestCandidate
.IsStatic
&& mg
.BestCandidate
.IsPublic
) {
8242 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8245 PredefinedMember
<MethodSpec
> iface_candidate
= null;
8246 var ptypes
= rc
.Module
.PredefinedTypes
;
8247 var gen_ienumerable
= ptypes
.IEnumerableGeneric
;
8248 if (!gen_ienumerable
.Define ())
8249 gen_ienumerable
= null;
8251 var ifaces
= t
.Interfaces
;
8252 if (ifaces
!= null) {
8253 foreach (var iface
in ifaces
) {
8254 if (gen_ienumerable
!= null && iface
.MemberDefinition
== gen_ienumerable
.TypeSpec
.MemberDefinition
) {
8255 if (iface_candidate
!= null && iface_candidate
!= rc
.Module
.PredefinedMembers
.IEnumerableGetEnumerator
) {
8256 rc
.Report
.SymbolRelatedToPreviousError (expr
.Type
);
8257 rc
.Report
.Error (1640, loc
,
8258 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8259 expr
.Type
.GetSignatureForError (), gen_ienumerable
.TypeSpec
.GetSignatureForError ());
8264 // TODO: Cache this somehow
8265 iface_candidate
= new PredefinedMember
<MethodSpec
> (rc
.Module
, iface
,
8266 MemberFilter
.Method ("GetEnumerator", 0, ParametersCompiled
.EmptyReadOnlyParameters
, null));
8271 if (iface
.BuiltinType
== BuiltinTypeSpec
.Type
.IEnumerable
&& iface_candidate
== null) {
8272 iface_candidate
= rc
.Module
.PredefinedMembers
.IEnumerableGetEnumerator
;
8277 if (iface_candidate
== null) {
8278 if (expr
.Type
== InternalType
.DefaultType
) {
8279 rc
.Report
.Error (8312, loc
, "Use of default literal is not valid in this context");
8280 } else if (expr
.Type
!= InternalType
.ErrorType
) {
8281 rc
.Report
.Error (1579, loc
,
8282 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8283 expr
.Type
.GetSignatureForError (), "GetEnumerator");
8289 var method
= iface_candidate
.Resolve (loc
);
8293 mg
= MethodGroupExpr
.CreatePredefined (method
, expr
.Type
, loc
);
8294 mg
.InstanceExpression
= expr
;
8298 MethodGroupExpr
ResolveMoveNext (ResolveContext rc
, MethodSpec enumerator
)
8300 var ms
= MemberCache
.FindMember (enumerator
.ReturnType
,
8301 MemberFilter
.Method ("MoveNext", 0, ParametersCompiled
.EmptyReadOnlyParameters
, rc
.BuiltinTypes
.Bool
),
8302 BindingRestriction
.InstanceOnly
) as MethodSpec
;
8304 if (ms
== null || !ms
.IsPublic
) {
8305 Error_WrongEnumerator (rc
, enumerator
);
8309 return MethodGroupExpr
.CreatePredefined (ms
, enumerator
.ReturnType
, expr
.Location
);
8312 PropertySpec
ResolveCurrent (ResolveContext rc
, MethodSpec enumerator
)
8314 var ps
= MemberCache
.FindMember (enumerator
.ReturnType
,
8315 MemberFilter
.Property ("Current", null),
8316 BindingRestriction
.InstanceOnly
) as PropertySpec
;
8318 if (ps
== null || !ps
.IsPublic
) {
8319 Error_WrongEnumerator (rc
, enumerator
);
8326 public override bool Resolve (BlockContext ec
)
8328 bool is_dynamic
= expr
.Type
.BuiltinType
== BuiltinTypeSpec
.Type
.Dynamic
;
8331 expr
= Convert
.ImplicitConversionRequired (ec
, expr
, ec
.BuiltinTypes
.IEnumerable
, loc
);
8332 } else if (expr
.Type
.IsNullableType
) {
8333 expr
= new Nullable
.UnwrapCall (expr
).Resolve (ec
);
8336 var get_enumerator_mg
= ResolveGetEnumerator (ec
);
8337 if (get_enumerator_mg
== null) {
8341 var get_enumerator
= get_enumerator_mg
.BestCandidate
;
8342 enumerator_variable
= TemporaryVariableReference
.Create (get_enumerator
.ReturnType
, variable
.Block
, loc
);
8343 enumerator_variable
.Resolve (ec
);
8345 // Prepare bool MoveNext ()
8346 var move_next_mg
= ResolveMoveNext (ec
, get_enumerator
);
8347 if (move_next_mg
== null) {
8351 move_next_mg
.InstanceExpression
= enumerator_variable
;
8353 // Prepare ~T~ Current { get; }
8354 var current_prop
= ResolveCurrent (ec
, get_enumerator
);
8355 if (current_prop
== null) {
8359 var current_pe
= new PropertyExpr (current_prop
, loc
) { InstanceExpression = enumerator_variable }
.Resolve (ec
);
8360 if (current_pe
== null)
8363 VarExpr ve
= for_each
.type
as VarExpr
;
8367 // Source type is dynamic, set element type to dynamic too
8368 variable
.Type
= ec
.BuiltinTypes
.Dynamic
;
8370 // Infer implicitly typed local variable from foreach enumerable type
8371 variable
.Type
= current_pe
.Type
;
8375 // Explicit cast of dynamic collection elements has to be done at runtime
8376 current_pe
= EmptyCast
.Create (current_pe
, ec
.BuiltinTypes
.Dynamic
);
8379 variable
.Type
= for_each
.type
.ResolveAsType (ec
);
8381 if (variable
.Type
== null)
8384 current_pe
= Convert
.ExplicitConversion (ec
, current_pe
, variable
.Type
, loc
);
8385 if (current_pe
== null)
8389 var prev_block
= ec
.CurrentBlock
;
8390 ec
.CurrentBlock
= for_each
.variable
.Block
;
8391 var variable_ref
= new LocalVariableReference (variable
, loc
).Resolve (ec
);
8392 ec
.CurrentBlock
= prev_block
;
8393 if (variable_ref
== null)
8396 for_each
.body
.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref
, current_pe
, Location
.Null
), for_each
.type
.Location
));
8398 var init
= new Invocation
.Predefined (get_enumerator_mg
, null);
8400 statement
= new While (new BooleanExpression (new Invocation (move_next_mg
, null)),
8401 for_each
.body
, Location
.Null
);
8403 var enum_type
= enumerator_variable
.Type
;
8406 // Add Dispose method call when enumerator can be IDisposable
8408 if (!enum_type
.ImplementsInterface (ec
.BuiltinTypes
.IDisposable
, false)) {
8409 if (!enum_type
.IsSealed
&& !TypeSpec
.IsValueType (enum_type
)) {
8411 // Runtime Dispose check
8413 var vd
= new RuntimeDispose (enumerator_variable
.LocalInfo
, Location
.Null
);
8414 vd
.Initializer
= init
;
8415 statement
= new Using (vd
, statement
, Location
.Null
);
8418 // No Dispose call needed
8420 this.init
= new SimpleAssign (enumerator_variable
, init
, Location
.Null
);
8421 this.init
.Resolve (ec
);
8425 // Static Dispose check
8427 var vd
= new Using
.VariableDeclaration (enumerator_variable
.LocalInfo
, Location
.Null
);
8428 vd
.Initializer
= init
;
8429 statement
= new Using (vd
, statement
, Location
.Null
);
8432 return statement
.Resolve (ec
);
8435 protected override void DoEmit (EmitContext ec
)
8437 enumerator_variable
.LocalInfo
.CreateBuilder (ec
);
8440 init
.EmitStatement (ec
);
8442 statement
.Emit (ec
);
8445 #region IErrorHandler Members
8447 bool OverloadResolver
.IErrorHandler
.AmbiguousCandidates (ResolveContext ec
, MemberSpec best
, MemberSpec ambiguous
)
8449 ec
.Report
.SymbolRelatedToPreviousError (best
);
8450 ec
.Report
.Warning (278, 2, expr
.Location
,
8451 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8452 expr
.Type
.GetSignatureForError (), "enumerable",
8453 best
.GetSignatureForError (), ambiguous
.GetSignatureForError ());
8455 ambiguous_getenumerator_name
= true;
8459 bool OverloadResolver
.IErrorHandler
.ArgumentMismatch (ResolveContext rc
, MemberSpec best
, Argument arg
, int index
)
8464 bool OverloadResolver
.IErrorHandler
.NoArgumentMatch (ResolveContext rc
, MemberSpec best
)
8469 bool OverloadResolver
.IErrorHandler
.TypeInferenceFailed (ResolveContext rc
, MemberSpec best
)
8478 LocalVariable variable
;
8482 public Foreach (Expression type
, LocalVariable
var, Expression expr
, Statement stmt
, Block body
, Location l
)
8486 this.variable
= var;
8492 public Expression Expr
{
8493 get { return expr; }
8496 public Expression TypeExpression
{
8497 get { return type; }
8500 public LocalVariable Variable
{
8501 get { return variable; }
8504 public override Reachability
MarkReachable (Reachability rc
)
8506 base.MarkReachable (rc
);
8508 body
.MarkReachable (rc
);
8513 public override bool Resolve (BlockContext ec
)
8515 expr
= expr
.Resolve (ec
);
8520 ec
.Report
.Error (186, loc
, "Use of null is not valid in this context");
8524 body
.AddStatement (Statement
);
8526 if (expr
.Type
.BuiltinType
== BuiltinTypeSpec
.Type
.String
) {
8527 Statement
= new ArrayForeach (this, 1);
8528 } else if (expr
.Type
is ArrayContainer
) {
8529 Statement
= new ArrayForeach (this, ((ArrayContainer
) expr
.Type
).Rank
);
8531 if (expr
.eclass
== ExprClass
.MethodGroup
|| expr
is AnonymousMethodExpression
) {
8532 ec
.Report
.Error (446, expr
.Location
, "Foreach statement cannot operate on a `{0}'",
8533 expr
.ExprClassName
);
8537 Statement
= new CollectionForeach (this, variable
, expr
);
8540 return base.Resolve (ec
);
8543 protected override void DoEmit (EmitContext ec
)
8545 Label old_begin
= ec
.LoopBegin
, old_end
= ec
.LoopEnd
;
8546 ec
.LoopBegin
= ec
.DefineLabel ();
8547 ec
.LoopEnd
= ec
.DefineLabel ();
8549 ec
.BeginCompilerScope (variable
.Block
.Explicit
.GetDebugSymbolScopeIndex ());
8550 body
.Explicit
.DisableDebugScopeIndex ();
8552 variable
.CreateBuilder (ec
);
8554 Statement
.Emit (ec
);
8558 ec
.LoopBegin
= old_begin
;
8559 ec
.LoopEnd
= old_end
;
8562 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
8564 expr
.FlowAnalysis (fc
);
8566 var da
= fc
.BranchDefiniteAssignment ();
8567 body
.FlowAnalysis (fc
);
8568 fc
.DefiniteAssignment
= da
;
8572 protected override void CloneTo (CloneContext clonectx
, Statement t
)
8574 Foreach target
= (Foreach
) t
;
8576 target
.type
= type
.Clone (clonectx
);
8577 target
.expr
= expr
.Clone (clonectx
);
8578 target
.body
= (Block
) body
.Clone (clonectx
);
8579 target
.Statement
= Statement
.Clone (clonectx
);
8582 public override object Accept (StructuralVisitor visitor
)
8584 return visitor
.Visit (this);
8588 class SentinelStatement
: Statement
8590 protected override void CloneTo (CloneContext clonectx
, Statement target
)
8594 protected override void DoEmit (EmitContext ec
)
8596 var l
= ec
.DefineLabel ();
8598 ec
.Emit (OpCodes
.Br_S
, l
);
8601 protected override bool DoFlowAnalysis (FlowAnalysisContext fc
)
8603 throw new NotImplementedException ();