Example #1
0
        // insert the implicit "return" statement at the end of the method body
        // Normally, we wouldn't bother attaching syntax trees to compiler-generated nodes, but these
        // ones are going to have sequence points.
        internal static BoundBlock AppendImplicitReturn(BoundStatement node, MethodSymbol method = null, CSharpSyntaxNode syntax = null)
        {
            if (syntax == null)
            {
                syntax = node.Syntax;
            }

            BoundStatement ret =
                (object)method != null && (object)method.IteratorElementType != null
                ? BoundYieldBreakStatement.Synthesized(syntax) as BoundStatement
                : BoundReturnStatement.Synthesized(syntax, null);

            if (syntax.Kind == SyntaxKind.Block)
            {
                var blockSyntax = (BlockSyntax)syntax;

                ret = new BoundSequencePointWithSpan(
                    blockSyntax,
                    ret,
                    blockSyntax.CloseBraceToken.Span)
                { WasCompilerGenerated = true };
            }

            switch (node.Kind)
            {
                case BoundKind.Block:
                    var block = (BoundBlock)node;
                    return block.Update(block.LocalsOpt, block.Statements.Add(ret));

                default:
                    return new BoundBlock(syntax, ImmutableArray<LocalSymbol>.Empty, ImmutableArray.Create(ret, node));
            }
        }
Example #2
0
        // insert the implicit "return" statement at the end of the method body
        // Normally, we wouldn't bother attaching syntax trees to compiler-generated nodes, but these
        // ones are going to have sequence points.
        internal static BoundBlock AppendImplicitReturn(BoundStatement node, MethodSymbol method = null, CSharpSyntaxNode syntax = null)
        {
            if (syntax == null)
            {
                syntax = node.Syntax;
            }

            BoundStatement ret =
                (object)method != null && (object)method.IteratorElementType != null
                ? BoundYieldBreakStatement.Synthesized(syntax) as BoundStatement
                : BoundReturnStatement.Synthesized(syntax, null);

            if (syntax.Kind == SyntaxKind.Block)
            {
                var blockSyntax = (BlockSyntax)syntax;

                ret = new BoundSequencePointWithSpan(
                    blockSyntax,
                    ret,
                    blockSyntax.CloseBraceToken.Span)
                {
                    WasCompilerGenerated = true
                };
            }

            switch (node.Kind)
            {
            case BoundKind.Block:
                var block = (BoundBlock)node;
                return(block.Update(block.LocalsOpt, block.Statements.Add(ret)));

            default:
                return(new BoundBlock(syntax, ImmutableArray <LocalSymbol> .Empty, ImmutableArray.Create(ret, node)));
            }
        }
Example #3
0
        public override BoundNode VisitIfStatement(BoundIfStatement node)
        {
            Debug.Assert(node != null);
            var rewrittenCondition   = VisitExpression(node.Condition);
            var rewrittenConsequence = VisitStatement(node.Consequence);
            var rewrittenAlternative = VisitStatement(node.AlternativeOpt);
            var syntax = (IfStatementSyntax)node.Syntax;

            // EnC: We need to insert a hidden sequence point to handle function remapping in case
            // the containing method is edited while methods invoked in the condition are being executed.
            var result = RewriteIfStatement(syntax, node.Locals, AddConditionSequencePoint(rewrittenCondition, node), rewrittenConsequence, rewrittenAlternative, node.HasErrors);

            // add sequence point before the whole statement
            if (this.GenerateDebugInfo && !node.WasCompilerGenerated)
            {
                result = new BoundSequencePointWithSpan(
                    syntax,
                    result,
                    TextSpan.FromBounds(
                        syntax.IfKeyword.SpanStart,
                        syntax.CloseParenToken.Span.End),
                    node.HasErrors);
            }

            return(result);
        }
Example #4
0
        // insert the implicit "return" statement at the end of the method body
        // Normally, we wouldn't bother attaching syntax trees to compiler-generated nodes, but these
        // ones are going to have sequence points.
        internal static BoundBlock AppendImplicitReturn(BoundBlock body, MethodSymbol method, CSharpSyntaxNode syntax = null)
        {
            Debug.Assert(body != null);
            Debug.Assert(method != null);

            if (syntax == null)
            {
                syntax = body.Syntax;
            }

            Debug.Assert(body.WasCompilerGenerated || syntax.IsKind(SyntaxKind.Block) || syntax.IsKind(SyntaxKind.ArrowExpressionClause));

            BoundStatement ret = method.IsIterator
                ? (BoundStatement)BoundYieldBreakStatement.Synthesized(syntax)
                : BoundReturnStatement.Synthesized(syntax, null);

            // Implicitly added return for async method does not need sequence points since lowering would add one.
            if (syntax.IsKind(SyntaxKind.Block) && !method.IsAsync)
            {
                var blockSyntax = (BlockSyntax)syntax;

                ret = new BoundSequencePointWithSpan(
                    blockSyntax,
                    ret,
                    blockSyntax.CloseBraceToken.Span)
                {
                    WasCompilerGenerated = true
                };
            }

            return(body.Update(body.Locals, body.Statements.Add(ret)));
        }
        public override BoundNode VisitIfStatement(BoundIfStatement node)
        {
            Debug.Assert(node != null);
            var rewrittenCondition = VisitExpression(node.Condition);
            var rewrittenConsequence = VisitStatement(node.Consequence);
            var rewrittenAlternative = VisitStatement(node.AlternativeOpt);
            var syntax = (IfStatementSyntax)node.Syntax;

            // EnC: We need to insert a hidden sequence point to handle function remapping in case 
            // the containing method is edited while methods invoked in the condition are being executed.
            var result = RewriteIfStatement(syntax, AddConditionSequencePoint(rewrittenCondition, node), rewrittenConsequence, rewrittenAlternative, node.HasErrors);

            // add sequence point before the whole statement
            if (this.GenerateDebugInfo && !node.WasCompilerGenerated)
            {
                result = new BoundSequencePointWithSpan(
                    syntax,
                    result,
                    TextSpan.FromBounds(
                        syntax.IfKeyword.SpanStart,
                        syntax.CloseParenToken.Span.End),
                    node.HasErrors);
            }

            return result;
        }
Example #6
0
        internal static BoundBlock ConstructDestructorBody(MethodSymbol method, BoundBlock block)
        {
            var syntax = block.Syntax;

            Debug.Assert(method.MethodKind == MethodKind.Destructor);
            Debug.Assert(syntax.Kind() == SyntaxKind.Block);

            // If this is a destructor and a base type has a Finalize method (see GetBaseTypeFinalizeMethod for exact
            // requirements), then we need to call that method in a finally block.  Otherwise, just return block as-is.
            // NOTE: the Finalize method need not be a destructor or be overridden by the current method.
            MethodSymbol baseTypeFinalize = GetBaseTypeFinalizeMethod(method);

            if ((object)baseTypeFinalize != null)
            {
                BoundStatement baseFinalizeCall = new BoundSequencePointWithSpan( //sequence point to mimic Dev10
                    syntax,
                    new BoundExpressionStatement(
                        syntax,
                        BoundCall.Synthesized(
                            syntax,
                            new BoundBaseReference(
                                syntax,
                                method.ContainingType)
                {
                    WasCompilerGenerated = true
                },
                            baseTypeFinalize)
                        )
                {
                    WasCompilerGenerated = true
                },
                    ((BlockSyntax)syntax).CloseBraceToken.Span);

                return(new BoundBlock(
                           syntax,
                           ImmutableArray <LocalSymbol> .Empty,
                           ImmutableArray <LocalFunctionSymbol> .Empty,
                           ImmutableArray.Create <BoundStatement>(
                               new BoundTryStatement(
                                   syntax,
                                   block,
                                   ImmutableArray <BoundCatchBlock> .Empty,
                                   new BoundBlock(
                                       syntax,
                                       ImmutableArray <LocalSymbol> .Empty,
                                       ImmutableArray <LocalFunctionSymbol> .Empty,
                                       ImmutableArray.Create <BoundStatement>(
                                           baseFinalizeCall)
                                       )
                {
                    WasCompilerGenerated = true
                }
                                   )
                {
                    WasCompilerGenerated = true
                })));
            }

            return(block);
        }
 /// <summary>
 /// Add sequence point |here|:
 ///
 /// foreach (|Type var| in expr) { }
 /// </summary>
 /// <remarks>
 /// Hit every iteration.
 /// </remarks>
 private void AddForEachIterationVariableSequencePoint(ForEachStatementSyntax forEachSyntax, ref BoundStatement iterationVarDecl)
 {
     if (this.GenerateDebugInfo)
     {
         TextSpan iterationVarDeclSpan = TextSpan.FromBounds(forEachSyntax.Type.SpanStart, forEachSyntax.Identifier.Span.End);
         iterationVarDecl = new BoundSequencePointWithSpan(forEachSyntax, iterationVarDecl, iterationVarDeclSpan);
     }
 }
 /// <summary>
 /// Add sequence point |here|:
 ///
 /// |foreach| (Type var in expr) { }
 /// </summary>
 /// <remarks>
 /// Hit once, before looping begins.
 /// </remarks>
 private void AddForEachKeywordSequencePoint(ForEachStatementSyntax forEachSyntax, ref BoundStatement result)
 {
     if (this.GenerateDebugInfo)
     {
         BoundSequencePointWithSpan foreachKeywordSequencePoint = new BoundSequencePointWithSpan(forEachSyntax, null, forEachSyntax.ForEachKeyword.Span);
         result = new BoundStatementList(forEachSyntax, ImmutableArray.Create <BoundStatement>(foreachKeywordSequencePoint, result));
     }
 }
        /// <summary>
        /// Add sequence point |here|:
        ///
        /// |foreach| (Type var in expr) { }
        /// </summary>
        /// <remarks>
        /// Hit once, before looping begins.
        /// </remarks>
        public override BoundStatement InstrumentForEachStatement(BoundForEachStatement original, BoundStatement rewritten)
        {
            var forEachSyntax = (CommonForEachStatementSyntax)original.Syntax;
            BoundSequencePointWithSpan foreachKeywordSequencePoint = new BoundSequencePointWithSpan(forEachSyntax, null, forEachSyntax.ForEachKeyword.Span);

            return(new BoundStatementList(forEachSyntax,
                                          ImmutableArray.Create <BoundStatement>(foreachKeywordSequencePoint,
                                                                                 base.InstrumentForEachStatement(original, rewritten))));
        }
        public override BoundNode VisitDoStatement(BoundDoStatement node)
        {
            Debug.Assert(node != null);

            var rewrittenCondition = (BoundExpression)Visit(node.Condition);
            var rewrittenBody = (BoundStatement)Visit(node.Body);
            var startLabel = new GeneratedLabelSymbol("start");

            var syntax = node.Syntax;

            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(syntax, AddConditionSequencePoint(rewrittenCondition, node), true, startLabel);

            if (this.GenerateDebugInfo)
            {
                var doSyntax = (DoStatementSyntax)syntax;
                var span = TextSpan.FromBounds(
                    doSyntax.WhileKeyword.SpanStart,
                    doSyntax.SemicolonToken.Span.End);

                ifConditionGotoStart = new BoundSequencePointWithSpan(doSyntax, ifConditionGotoStart, span);
            }

            // do
            //   body
            // while (condition);
            //
            // becomes
            //
            // start: 
            // {
            //   body
            //   continue:
            //   sequence point
            //   GotoIfTrue condition start;
            // }
            // break:

            if (!node.InnerLocals.IsDefaultOrEmpty)
            {
                return BoundStatementList.Synthesized(syntax, node.HasErrors,
                    new BoundLabelStatement(syntax, startLabel),
                    new BoundBlock(syntax,
                                   node.InnerLocals,
                                   ImmutableArray.Create<BoundStatement>(rewrittenBody,
                                                                         new BoundLabelStatement(syntax, node.ContinueLabel),
                                                                         ifConditionGotoStart)),
                    new BoundLabelStatement(syntax, node.BreakLabel));
            }

            return BoundStatementList.Synthesized(syntax, node.HasErrors,
                new BoundLabelStatement(syntax, startLabel),
                rewrittenBody,
                new BoundLabelStatement(syntax, node.ContinueLabel),
                ifConditionGotoStart,
                new BoundLabelStatement(syntax, node.BreakLabel));
        }
        public override BoundNode VisitDoStatement(BoundDoStatement node)
        {
            Debug.Assert(node != null);

            var rewrittenCondition = (BoundExpression)Visit(node.Condition);
            var rewrittenBody      = (BoundStatement)Visit(node.Body);
            var startLabel         = new GeneratedLabelSymbol("start");

            var syntax = node.Syntax;

            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(syntax, rewrittenCondition, true, startLabel);

            if (this.generateDebugInfo)
            {
                var doSyntax = (DoStatementSyntax)syntax;
                var span     = TextSpan.FromBounds(
                    doSyntax.WhileKeyword.SpanStart,
                    doSyntax.SemicolonToken.Span.End);

                ifConditionGotoStart = new BoundSequencePointWithSpan(doSyntax, ifConditionGotoStart, span);
            }

            // do
            //   body
            // while (condition);
            //
            // becomes
            //
            // start:
            // {
            //   body
            //   continue:
            //   sequence point
            //   GotoIfTrue condition start;
            // }
            // break:

            if (!node.InnerLocals.IsDefaultOrEmpty)
            {
                return(BoundStatementList.Synthesized(syntax, node.HasErrors,
                                                      new BoundLabelStatement(syntax, startLabel),
                                                      new BoundBlock(syntax,
                                                                     node.InnerLocals,
                                                                     ImmutableArray.Create <BoundStatement>(rewrittenBody,
                                                                                                            new BoundLabelStatement(syntax, node.ContinueLabel),
                                                                                                            ifConditionGotoStart)),
                                                      new BoundLabelStatement(syntax, node.BreakLabel)));
            }

            return(BoundStatementList.Synthesized(syntax, node.HasErrors,
                                                  new BoundLabelStatement(syntax, startLabel),
                                                  rewrittenBody,
                                                  new BoundLabelStatement(syntax, node.ContinueLabel),
                                                  ifConditionGotoStart,
                                                  new BoundLabelStatement(syntax, node.BreakLabel)));
        }
Example #12
0
        /// <summary>
        /// Add sequence point |here|:
        ///
        /// |foreach| (Type var in expr) { }
        /// </summary>
        /// <remarks>
        /// Hit once, before looping begins.
        /// </remarks>
        public override BoundStatement InstrumentForEachStatement(BoundForEachStatement original, BoundStatement rewritten)
        {
            var forEachSyntax = (CommonForEachStatementSyntax)original.Syntax;
            var span          = forEachSyntax.AwaitKeyword != default
                ? TextSpan.FromBounds(forEachSyntax.AwaitKeyword.Span.Start, forEachSyntax.ForEachKeyword.Span.End)
                : forEachSyntax.ForEachKeyword.Span;

            var foreachKeywordSequencePoint = new BoundSequencePointWithSpan(forEachSyntax, null, span);

            return(new BoundStatementList(forEachSyntax,
                                          ImmutableArray.Create <BoundStatement>(foreachKeywordSequencePoint,
                                                                                 base.InstrumentForEachStatement(original, rewritten))));
        }
        private BoundStatement RewriteWhileStatement(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenCondition,
            TextSpan conditionSequencePointSpan,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            var            startLabel           = new GeneratedLabelSymbol("start");
            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);

            if (this.GenerateDebugInfo)
            {
                ifConditionGotoStart = new BoundSequencePointWithSpan(syntax, ifConditionGotoStart, conditionSequencePointSpan);
            }

            // while (condition)
            //   body;
            //
            // becomes
            //
            // goto continue;
            // start:
            // {
            //     body
            //     continue:
            //     GotoIfTrue condition start;
            // }
            // break:

            BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel);

            if (this.GenerateDebugInfo)
            {
                // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This
                // jump may be a target of another jump (for example if loops are nested) and that would give the
                // impression that the previous statement is being re-executed.
                gotoContinue = new BoundSequencePoint(null, gotoContinue);
            }

            return(BoundStatementList.Synthesized(syntax, hasErrors,
                                                  gotoContinue,
                                                  new BoundLabelStatement(syntax, startLabel),
                                                  rewrittenBody,
                                                  new BoundLabelStatement(syntax, continueLabel),
                                                  ifConditionGotoStart,
                                                  new BoundLabelStatement(syntax, breakLabel)));
        }
        private BoundStatement RewriteWhileStatement(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenCondition,
            TextSpan conditionSequencePointSpan,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            var startLabel = new GeneratedLabelSymbol("start");
            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);

            if (this.GenerateDebugInfo)
            {
                ifConditionGotoStart = new BoundSequencePointWithSpan(syntax, ifConditionGotoStart, conditionSequencePointSpan);
            }

            // while (condition) 
            //   body;
            //
            // becomes
            //
            // goto continue;
            // start: 
            // {
            //     body
            //     continue:
            //     GotoIfTrue condition start;
            // }
            // break:

            BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel);
            if (this.GenerateDebugInfo)
            {
                // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This
                // jump may be a target of another jump (for example if loops are nested) and that would give the
                // impression that the previous statement is being re-executed.
                gotoContinue = new BoundSequencePoint(null, gotoContinue);
            }

            return BoundStatementList.Synthesized(syntax, hasErrors,
                gotoContinue,
                new BoundLabelStatement(syntax, startLabel),
                rewrittenBody,
                new BoundLabelStatement(syntax, continueLabel),
                ifConditionGotoStart,
                new BoundLabelStatement(syntax, breakLabel));
        }
Example #15
0
        public override BoundNode VisitDoStatement(BoundDoStatement node)
        {
            Debug.Assert(node != null);

            var rewrittenCondition = (BoundExpression)Visit(node.Condition);
            var rewrittenBody      = (BoundStatement)Visit(node.Body);
            var startLabel         = new GeneratedLabelSymbol("start");

            var syntax = node.Syntax;

            // EnC: We need to insert a hidden sequence point to handle function remapping in case
            // the containing method is edited while methods invoked in the condition are being executed.
            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(syntax, AddConditionSequencePoint(rewrittenCondition, node), true, startLabel);

            if (this.GenerateDebugInfo)
            {
                var doSyntax = (DoStatementSyntax)syntax;
                var span     = TextSpan.FromBounds(
                    doSyntax.WhileKeyword.SpanStart,
                    doSyntax.SemicolonToken.Span.End);

                ifConditionGotoStart = new BoundSequencePointWithSpan(doSyntax, ifConditionGotoStart, span);
            }

            // do
            //   body
            // while (condition);
            //
            // becomes
            //
            // start:
            // {
            //   body
            //   continue:
            //   sequence point
            //   GotoIfTrue condition start;
            // }
            // break:

            return(BoundStatementList.Synthesized(syntax, node.HasErrors,
                                                  new BoundLabelStatement(syntax, startLabel),
                                                  rewrittenBody,
                                                  new BoundLabelStatement(syntax, node.ContinueLabel),
                                                  ifConditionGotoStart,
                                                  new BoundLabelStatement(syntax, node.BreakLabel)));
        }
        public override BoundNode VisitDoStatement(BoundDoStatement node)
        {
            Debug.Assert(node != null);

            var rewrittenCondition = (BoundExpression)Visit(node.Condition);
            var rewrittenBody = (BoundStatement)Visit(node.Body);
            var startLabel = new GeneratedLabelSymbol("start");

            var syntax = node.Syntax;

            // EnC: We need to insert a hidden sequence point to handle function remapping in case 
            // the containing method is edited while methods invoked in the condition are being executed.
            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(syntax, AddConditionSequencePoint(rewrittenCondition, node), true, startLabel);

            if (this.GenerateDebugInfo)
            {
                var doSyntax = (DoStatementSyntax)syntax;
                var span = TextSpan.FromBounds(
                    doSyntax.WhileKeyword.SpanStart,
                    doSyntax.SemicolonToken.Span.End);

                ifConditionGotoStart = new BoundSequencePointWithSpan(doSyntax, ifConditionGotoStart, span);
            }

            // do
            //   body
            // while (condition);
            //
            // becomes
            //
            // start: 
            // {
            //   body
            //   continue:
            //   sequence point
            //   GotoIfTrue condition start;
            // }
            // break:

            return BoundStatementList.Synthesized(syntax, node.HasErrors,
                new BoundLabelStatement(syntax, startLabel),
                rewrittenBody,
                new BoundLabelStatement(syntax, node.ContinueLabel),
                ifConditionGotoStart,
                new BoundLabelStatement(syntax, node.BreakLabel));
        }
Example #17
0
        // insert the implicit "return" statement at the end of the method body
        // Normally, we wouldn't bother attaching syntax trees to compiler-generated nodes, but these
        // ones are going to have sequence points.
        internal static BoundBlock AppendImplicitReturn(BoundStatement node, MethodSymbol method, CSharpSyntaxNode syntax = null)
        {
            Debug.Assert(method != null);

            if (syntax == null)
            {
                syntax = node.Syntax;
            }

            BoundStatement ret = method.IsIterator
                ? (BoundStatement)BoundYieldBreakStatement.Synthesized(syntax)
                : BoundReturnStatement.Synthesized(syntax, null);

            if (syntax.Kind() == SyntaxKind.Block)
            {
                // Implicitly added return for async method does not need sequence points since lowering would add one.
                if (!method.IsAsync)
                {
                    var blockSyntax = (BlockSyntax)syntax;

                    ret = new BoundSequencePointWithSpan(
                        blockSyntax,
                        ret,
                        blockSyntax.CloseBraceToken.Span)
                    {
                        WasCompilerGenerated = true
                    };
                }
            }

            switch (node.Kind)
            {
            case BoundKind.Block:
                var block = (BoundBlock)node;
                return(block.Update(block.Locals, block.Statements.Add(ret)));

            default:
                return(new BoundBlock(syntax, ImmutableArray <LocalSymbol> .Empty, ImmutableArray.Create(ret, node)));
            }
        }
Example #18
0
        // insert the implicit "return" statement at the end of the method body
        // Normally, we wouldn't bother attaching syntax trees to compiler-generated nodes, but these
        // ones are going to have sequence points.
        internal static BoundBlock AppendImplicitReturn(BoundStatement node, MethodSymbol method, CSharpSyntaxNode syntax = null)
        {
            Debug.Assert(method != null);

            if (syntax == null)
            {
                syntax = node.Syntax;
            }

            BoundStatement ret = method.IsIterator
                ? (BoundStatement)BoundYieldBreakStatement.Synthesized(syntax)
                : BoundReturnStatement.Synthesized(syntax, null);

            if (syntax.Kind() == SyntaxKind.Block)
            {
                // Implicitly added return for async method does not need sequence points since lowering would add one.
                if (!method.IsAsync)
                {
                    var blockSyntax = (BlockSyntax)syntax;

                    ret = new BoundSequencePointWithSpan(
                        blockSyntax,
                        ret,
                        blockSyntax.CloseBraceToken.Span)
                    { WasCompilerGenerated = true };
                }
            }

            switch (node.Kind)
            {
                case BoundKind.Block:
                    var block = (BoundBlock)node;
                    return block.Update(block.Locals, block.Statements.Add(ret));

                default:
                    return new BoundBlock(syntax, ImmutableArray<LocalSymbol>.Empty, ImmutableArray.Create(ret, node));
            }
        }
        public override BoundNode VisitIfStatement(BoundIfStatement node)
        {
            Debug.Assert(node != null);
            var rewrittenCondition = VisitExpression(node.Condition);
            var rewrittenConsequence = VisitStatement(node.Consequence);
            var rewrittenAlternative = VisitStatement(node.AlternativeOpt);
            var syntax = (IfStatementSyntax)node.Syntax;
            var result = RewriteIfStatement(syntax, node.Locals, rewrittenCondition, rewrittenConsequence, rewrittenAlternative, node.HasErrors);

            // add sequence point before the whole statement
            if (this.generateDebugInfo && !node.WasCompilerGenerated)
            {
                result = new BoundSequencePointWithSpan(
                    syntax,
                    result,
                    TextSpan.FromBounds(
                        syntax.IfKeyword.SpanStart,
                        syntax.CloseParenToken.Span.End),
                    node.HasErrors);
            }

            return result;
        }
Example #20
0
        public override BoundNode VisitIfStatement(BoundIfStatement node)
        {
            Debug.Assert(node != null);
            var rewrittenCondition   = VisitExpression(node.Condition);
            var rewrittenConsequence = VisitStatement(node.Consequence);
            var rewrittenAlternative = VisitStatement(node.AlternativeOpt);
            var syntax = (IfStatementSyntax)node.Syntax;
            var result = RewriteIfStatement(syntax, rewrittenCondition, rewrittenConsequence, rewrittenAlternative, node.HasErrors);

            // add sequence point before the whole statement
            if (this.generateDebugInfo && !node.WasCompilerGenerated)
            {
                result = new BoundSequencePointWithSpan(
                    syntax,
                    result,
                    TextSpan.FromBounds(
                        syntax.IfKeyword.SpanStart,
                        syntax.CloseParenToken.Span.End),
                    node.HasErrors);
            }

            return(result);
        }
        internal static BoundBlock ConstructDestructorBody(CSharpSyntaxNode syntax, MethodSymbol method, BoundBlock block)
        {
            Debug.Assert(method.MethodKind == MethodKind.Destructor);
            Debug.Assert(syntax.Kind == SyntaxKind.Block);

            // If this is a destructor and a base type has a Finalize method (see GetBaseTypeFinalizeMethod for exact 
            // requirements), then we need to call that method in a finally block.  Otherwise, just return block as-is.
            // NOTE: the Finalize method need not be a destructor or be overridden by the current method.
            MethodSymbol baseTypeFinalize = GetBaseTypeFinalizeMethod(method);

            if ((object)baseTypeFinalize != null)
            {
                BoundStatement baseFinalizeCall = new BoundSequencePointWithSpan( //sequence point to mimic Dev10
                    syntax,
                    new BoundExpressionStatement(
                        syntax,
                        BoundCall.Synthesized(
                            syntax,
                            new BoundBaseReference(
                                syntax,
                                method.ContainingType)
                { WasCompilerGenerated = true },
                            baseTypeFinalize)
                        )
                { WasCompilerGenerated = true },
                    ((BlockSyntax)syntax).CloseBraceToken.Span);

                return new BoundBlock(
                    syntax,
                    ImmutableArray<LocalSymbol>.Empty,
                    ImmutableArray.Create<BoundStatement>(
                        new BoundTryStatement(
                            syntax,
                            block,
                            ImmutableArray<BoundCatchBlock>.Empty,
                            new BoundBlock(
                                syntax,
                                ImmutableArray<LocalSymbol>.Empty,
                                ImmutableArray.Create<BoundStatement>(
                                    baseFinalizeCall)
                            )
                { WasCompilerGenerated = true }
                        )
                { WasCompilerGenerated = true }));
            }

            return block;
        }
        /// <summary>
        /// Lower "using (expression) statement" to a try-finally block.
        /// </summary>
        private BoundBlock RewriteExpressionUsingStatement(BoundUsingStatement node, BoundBlock tryBlock)
        {
            Debug.Assert(node.ExpressionOpt != null);
            Debug.Assert(node.DeclarationsOpt == null);

            // See comments in BuildUsingTryFinally for the details of the lowering to try-finally.
            //
            // SPEC: A using statement of the form "using (expression) statement; " has the
            // SPEC: same three possible expansions [ as "using (ResourceType r = expression) statement; ]
            // SPEC: but in this case ResourceType is implicitly the compile-time type of the expression,
            // SPEC: and the resource variable is inaccessible to and invisible to the embedded statement.
            //
            // DELIBERATE SPEC VIOLATION:
            //
            // The spec quote above implies that the expression must have a type; in fact we allow
            // the expression to be null.
            //
            // If expr is the constant null then we can elide the whole thing and simply generate the statement.

            BoundExpression rewrittenExpression = (BoundExpression)Visit(node.ExpressionOpt);

            if (rewrittenExpression.ConstantValue == ConstantValue.Null)
            {
                return(tryBlock);
            }

            // Otherwise, we lower "using(expression) statement;" as follows:
            //
            // * If the expression is of type dynamic then we lower as though the user had written
            //
            //   using(IDisposable temp = (IDisposable)expression) statement;
            //
            //   Note that we have to do the conversion early, not in the finally block, because
            //   if the conversion fails at runtime with an exception then the exception must happen
            //   before the statement runs.
            //
            // * Otherwise we lower as though the user had written
            //
            //   using(ResourceType temp = expression) statement;
            //

            TypeSymbol           expressionType   = rewrittenExpression.Type;
            CSharpSyntaxNode     expressionSyntax = rewrittenExpression.Syntax;
            UsingStatementSyntax usingSyntax      = (UsingStatementSyntax)node.Syntax;

            BoundAssignmentOperator tempAssignment;
            BoundLocal boundTemp;

            if ((object)expressionType == null || expressionType.IsDynamic())
            {
                // IDisposable temp = (IDisposable) expr;
                BoundExpression tempInit = MakeConversion(
                    expressionSyntax,
                    rewrittenExpression,
                    node.IDisposableConversion.Kind,
                    this.compilation.GetSpecialType(SpecialType.System_IDisposable),
                    @checked: false,
                    constantValueOpt: rewrittenExpression.ConstantValue);

                boundTemp = this.factory.StoreToTemp(tempInit, out tempAssignment);
            }
            else
            {
                // ResourceType temp = expr;
                boundTemp = this.factory.StoreToTemp(rewrittenExpression, tempKind: TempKind.Using, store: out tempAssignment);
            }

            BoundStatement expressionStatement = new BoundExpressionStatement(expressionSyntax, tempAssignment);

            if (this.generateDebugInfo)
            {
                // NOTE: unlike in the assignment case, the sequence point is on the using keyword, not the expression.
                expressionStatement = new BoundSequencePointWithSpan(usingSyntax, expressionStatement, usingSyntax.UsingKeyword.Span);
            }

            BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp);

            // { ResourceType temp = expr; try { ... } finally { ... } }
            return(new BoundBlock(
                       syntax: usingSyntax,
                       localsOpt: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol),
                       statements: ImmutableArray.Create <BoundStatement>(expressionStatement, tryFinally)));
        }
Example #23
0
 /// <summary>
 /// Add sequence point |here|:
 /// 
 /// |foreach| (Type var in expr) { }
 /// </summary>
 /// <remarks>
 /// Hit once, before looping begins.
 /// </remarks>
 public override BoundStatement InstrumentForEachStatement(BoundForEachStatement original, BoundStatement rewritten)
 {
     var forEachSyntax = (CommonForEachStatementSyntax)original.Syntax;
     BoundSequencePointWithSpan foreachKeywordSequencePoint = new BoundSequencePointWithSpan(forEachSyntax, null, forEachSyntax.ForEachKeyword.Span);
     return new BoundStatementList(forEachSyntax, 
                                     ImmutableArray.Create<BoundStatement>(foreachKeywordSequencePoint,
                                                                         base.InstrumentForEachStatement(original, rewritten)));
 }
        private BoundStatement RewriteWhileStatement(
            CSharpSyntaxNode syntax,
            ImmutableArray <LocalSymbol> innerLocals,
            BoundExpression rewrittenCondition,
            TextSpan conditionSequencePointSpan,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            if (!innerLocals.IsDefaultOrEmpty)
            {
                var walker = new AnyLocalCapturedInALambdaWalker(innerLocals);

                if (walker.Analyze(rewrittenCondition) || walker.Analyze(rewrittenBody))
                {
                    // If any inner local is captured within a lambda, we need to enter scope-block
                    // always from the top, that is where an instance of a display class will be created.
                    // The IL will be less optimal, but this shouldn't be a problem, given presence of lambdas.

                    // while (condition)
                    //   body;
                    //
                    // becomes
                    //
                    // continue:
                    // {
                    //     GotoIfFalse condition break;
                    //     body
                    //     goto continue;
                    // }
                    // break:

                    // TODO: We could perform more fine analysis.
                    // If locals declared in condition (the innerLocals) are captured, but not referenced in the body, we could use optimal IL by creating
                    // another block around the condition and use it as a scope for the locals declared in condition.
                    // This optimization can be applied to 'for' as well, while-body === for-body + increment.
                    // Note however that the scope adjusments will likely be observable during debugging, in locals window.

                    BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel);

                    if (this.generateDebugInfo)
                    {
                        ifNotConditionGotoBreak = new BoundSequencePointWithSpan(syntax, ifNotConditionGotoBreak, conditionSequencePointSpan);
                    }

                    return(BoundStatementList.Synthesized(syntax, hasErrors,
                                                          new BoundLabelStatement(syntax, continueLabel),
                                                          new BoundBlock(syntax,
                                                                         innerLocals,
                                                                         ImmutableArray.Create <BoundStatement>(
                                                                             ifNotConditionGotoBreak,
                                                                             rewrittenBody,
                                                                             new BoundGotoStatement(syntax, continueLabel))),
                                                          new BoundLabelStatement(syntax, breakLabel)));
                }
            }

            var            startLabel           = new GeneratedLabelSymbol("start");
            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);

            if (this.generateDebugInfo)
            {
                ifConditionGotoStart = new BoundSequencePointWithSpan(syntax, ifConditionGotoStart, conditionSequencePointSpan);
            }

            // while (condition)
            //   body;
            //
            // becomes
            //
            // goto continue;
            // start:
            // {
            //     body
            //     continue:
            //     GotoIfTrue condition start;
            // }
            // break:

            BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel);

            if (this.generateDebugInfo)
            {
                // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This
                // jump may be a target of another jump (for example if loops are nested) and that would give the
                // impression that the previous statement is being re-executed.
                gotoContinue = new BoundSequencePoint(null, gotoContinue);
            }

            if (!innerLocals.IsDefaultOrEmpty)
            {
                return(BoundStatementList.Synthesized(syntax, hasErrors,
                                                      gotoContinue,
                                                      new BoundLabelStatement(syntax, startLabel),
                                                      new BoundBlock(syntax,
                                                                     innerLocals,
                                                                     ImmutableArray.Create <BoundStatement>(
                                                                         rewrittenBody,
                                                                         new BoundLabelStatement(syntax, continueLabel),
                                                                         ifConditionGotoStart)),
                                                      new BoundLabelStatement(syntax, breakLabel)));
            }

            return(BoundStatementList.Synthesized(syntax, hasErrors,
                                                  gotoContinue,
                                                  new BoundLabelStatement(syntax, startLabel),
                                                  rewrittenBody,
                                                  new BoundLabelStatement(syntax, continueLabel),
                                                  ifConditionGotoStart,
                                                  new BoundLabelStatement(syntax, breakLabel)));
        }
        private BoundStatement RewriteForStatement(
            CSharpSyntaxNode syntax,
            ImmutableArray<LocalSymbol> outerLocals,
            BoundStatement rewrittenInitializer,
            BoundExpression rewrittenCondition,
            CSharpSyntaxNode conditionSyntaxOpt,
            TextSpan conditionSpanOpt,
            BoundStatement rewrittenIncrement,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            Debug.Assert(rewrittenBody != null);

            // The sequence point behavior exhibited here is different from that of the native compiler.  In the native
            // compiler, if you have something like 
            //
            // for([|int i = 0, j = 0|]; ; [|i++, j++|])
            //
            // then all the initializers are treated as a single sequence point, as are
            // all the loop incrementors.
            //
            // We now make each one individually a sequence point:
            //
            // for([|int i = 0|], [|j = 0|]; ; [|i++|], [|j++|])
            //
            // If we decide that we want to preserve the native compiler stepping behavior
            // then we'll need to be a bit fancy here. The initializer and increment statements
            // can contain lambdas whose bodies need to have sequence points inserted, so we
            // need to make sure we visit the children. But we'll also need to make sure that
            // we do not generate one sequence point for each statement in the initializers
            // and the incrementors.

            var statementBuilder = ArrayBuilder<BoundStatement>.GetInstance();
            if (rewrittenInitializer != null)
            {
                statementBuilder.Add(rewrittenInitializer);
            }

            var startLabel = new GeneratedLabelSymbol("start");

            // for (initializer; condition; increment)
            //   body;
            //
            // becomes the following (with block added for locals)
            //
            // {
            //   initializer;
            //   goto end;
            // start:
            //   body;
            // continue:
            //   increment;
            // end:
            //   GotoIfTrue condition start;
            // break:
            // }

            var endLabel = new GeneratedLabelSymbol("end");

            //  initializer;
            //  goto end;

            // Mark the initial jump as hidden.
            // We do it to tell that this is not a part of previous statement.
            // This jump may be a target of another jump (for example if loops are nested) and that will make 
            // impression of the previous statement being re-executed
            var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel));
            statementBuilder.Add(gotoEnd);

            // start:
            //   body;
            statementBuilder.Add(new BoundLabelStatement(syntax, startLabel));

            statementBuilder.Add(rewrittenBody);

            // continue:
            //   increment;
            statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel));
            if (rewrittenIncrement != null)
            {
                statementBuilder.Add(rewrittenIncrement);
            }

            // end:
            //   GotoIfTrue condition start;
            statementBuilder.Add(new BoundLabelStatement(syntax, endLabel));
            BoundStatement branchBack = null;
            if (rewrittenCondition != null)
            {
                branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);
            }
            else
            {
                branchBack = new BoundGotoStatement(syntax, startLabel);
            }

            if (this.GenerateDebugInfo)
            {
                if (!conditionSpanOpt.IsEmpty)
                {
                    branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSpanOpt);
                }
                else
                {
                    // hidden sequence point if there is no condition
                    branchBack = new BoundSequencePoint(conditionSyntaxOpt, branchBack);
                }
            }

            statementBuilder.Add(branchBack);

            // break:
            statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel));

            var statements = statementBuilder.ToImmutableAndFree();
            return new BoundBlock(syntax, outerLocals, statements, hasErrors);
        }
 /// <summary>
 /// Add sequence point |here|:
 /// 
 /// |foreach| (Type var in expr) { }
 /// </summary>
 /// <remarks>
 /// Hit once, before looping begins.
 /// </remarks>
 private void AddForEachKeywordSequencePoint(ForEachStatementSyntax forEachSyntax, ref BoundStatement result)
 {
     if (this.GenerateDebugInfo)
     {
         BoundSequencePointWithSpan foreachKeywordSequencePoint = new BoundSequencePointWithSpan(forEachSyntax, null, forEachSyntax.ForEachKeyword.Span);
         result = new BoundStatementList(forEachSyntax, ImmutableArray.Create<BoundStatement>(foreachKeywordSequencePoint, result));
     }
 }
        private BoundStatement RewriteForStatement(
            CSharpSyntaxNode syntax,
            ImmutableArray<LocalSymbol> outerLocals,
            BoundStatement rewrittenInitializer,
            ImmutableArray<LocalSymbol> innerLocals,
            BoundExpression rewrittenCondition,
            SyntaxNodeOrToken conditionSyntax,
            BoundStatement rewrittenIncrement,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            Debug.Assert(rewrittenBody != null);

            // The sequence point behavior exhibited here is different from that of the native compiler.  In the native
            // compiler, if you have something like 
            //
            // for(int i = 0, j = 0; ; i++, j++)
            //     ^--------------^    ^------^   
            //
            // then all the initializers are treated as a single sequence point, as are
            // all the loop incrementers.
            //
            // We now make each one individually a sequence point:
            //
            // for(int i = 0, j = 0; ; i++, j++)
            //     ^-------^  ^---^    ^-^  ^-^
            //
            // If we decide that we want to preserve the native compiler stepping behavior
            // then we'll need to be a bit fancy here. The initializer and increment statements
            // can contain lambdas whose bodies need to have sequence points inserted, so we
            // need to make sure we visit the children. But we'll also need to make sure that
            // we do not generate one sequence point for each statement in the initializers
            // and the incrementers.

            var statementBuilder = ArrayBuilder<BoundStatement>.GetInstance();
            if (rewrittenInitializer != null)
            {
                statementBuilder.Add(rewrittenInitializer);
            }

            var startLabel = new GeneratedLabelSymbol("start");

            if (!innerLocals.IsDefaultOrEmpty)
            {
                var walker = new AnyLocalCapturedInALambdaWalker(innerLocals);

                if (walker.Analyze(rewrittenCondition) || walker.Analyze(rewrittenIncrement) || walker.Analyze(rewrittenBody))
                {
                    // If any inner local is captured within a lambda, we need to enter scope-block
                    // always from the top, that is where an instance of a display class will be created.
                    // The IL will be less optimal, but this shouldn't be a problem, given presence of lambdas.

                    // for (initializer; condition; increment)
                    //   body;
                    //
                    // becomes the following (with
                    // block added for locals)
                    //
                    // {
                    //   initializer;
                    // start:
                    //   {
                    //     GotoIfFalse condition break;
                    //     body;
                    // continue:
                    //     increment;
                    //     goto start;
                    //   }
                    // break:
                    // }

                    // start:
                    statementBuilder.Add(new BoundLabelStatement(syntax, startLabel));

                    var blockBuilder = ArrayBuilder<BoundStatement>.GetInstance();

                    //   GotoIfFalse condition break;
                    if (rewrittenCondition != null)
                    {
                        BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel);

                        if (this.generateDebugInfo)
                        {
                            if (conditionSyntax.IsToken)
                            {
                                ifNotConditionGotoBreak = new BoundSequencePointWithSpan(syntax, ifNotConditionGotoBreak, conditionSyntax.Span);
                            }
                            else
                            {
                                ifNotConditionGotoBreak = new BoundSequencePoint((CSharpSyntaxNode)conditionSyntax.AsNode(), ifNotConditionGotoBreak);
                            }
                        }

                        blockBuilder.Add(ifNotConditionGotoBreak);
                    }

                    // body;
                    blockBuilder.Add(rewrittenBody);

                    // continue:
                    //   increment;
                    blockBuilder.Add(new BoundLabelStatement(syntax, continueLabel));
                    if (rewrittenIncrement != null)
                    {
                        blockBuilder.Add(rewrittenIncrement);
                    }

                    //     goto start;
                    blockBuilder.Add(new BoundGotoStatement(syntax, startLabel));

                    statementBuilder.Add(new BoundBlock(syntax, innerLocals, blockBuilder.ToImmutableAndFree()));

                    // break:
                    statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel));

                    return new BoundBlock(syntax, outerLocals, statementBuilder.ToImmutableAndFree(), hasErrors);
                }
            }

            var endLabel = new GeneratedLabelSymbol("end");

            // for (initializer; condition; increment)
            //   body;
            //
            // becomes the following (with
            // block added for locals)
            //
            // {
            //   initializer;
            //   goto end;
            // start:
            //   body;
            // continue:
            //   increment;
            // end:
            //   GotoIfTrue condition start;
            // break:
            // }

            //  initializer;
            //  goto end;

            //mark the initial jump as hidden.
            //We do it to tell that this is not a part of previous statement.
            //This jump may be a target of another jump (for example if loops are nested) and that will make 
            //impression of the previous statement being re-executed
            var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel));
            statementBuilder.Add(gotoEnd);

            // start:
            //   body;
            statementBuilder.Add(new BoundLabelStatement(syntax, startLabel));

            ArrayBuilder<BoundStatement> saveBuilder = null;

            if (!innerLocals.IsDefaultOrEmpty)
            {
                saveBuilder = statementBuilder;
                statementBuilder = ArrayBuilder<BoundStatement>.GetInstance();
            }

            statementBuilder.Add(rewrittenBody);

            // continue:
            //   increment;
            statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel));
            if (rewrittenIncrement != null)
            {
                statementBuilder.Add(rewrittenIncrement);
            }

            // end:
            //   GotoIfTrue condition start;
            statementBuilder.Add(new BoundLabelStatement(syntax, endLabel));
            BoundStatement branchBack = null;
            if (rewrittenCondition != null)
            {
                branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);
            }
            else
            {
                branchBack = new BoundGotoStatement(syntax, startLabel);
            }

            if (this.generateDebugInfo)
            {
                if (conditionSyntax.IsToken)
                {
                    branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSyntax.Span);
                }
                else
                {
                    //if there is no condition, make this a hidden point so that 
                    //it does not count as a part of previous statement
                    branchBack = new BoundSequencePoint((CSharpSyntaxNode)conditionSyntax.AsNode(), branchBack);
                }
            }

            statementBuilder.Add(branchBack);

            if (!innerLocals.IsDefaultOrEmpty)
            {
                var block = new BoundBlock(syntax, innerLocals, statementBuilder.ToImmutableAndFree());
                statementBuilder = saveBuilder;
                statementBuilder.Add(block);
            }

            // break:
            statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel));

            var statements = statementBuilder.ToImmutableAndFree();
            return new BoundBlock(syntax, outerLocals, statements, hasErrors);
        }
        /// <summary>
        /// Lowers a lock statement to a try-finally block that calls Monitor.Enter and Monitor.Exit
        /// before and after the body, respectively.
        /// 
        /// C# 4.0 version
        /// 
        /// L locked;
        /// bool flag = false;
        /// try {
        ///     locked = `argument`;
        ///     Monitor.Enter(locked, ref flag);
        ///     `body`
        /// } finally {
        ///     if (flag) Monitor.Exit(locked);
        /// }
        ///
        /// Pre-4.0 version
        /// 
        /// L locked = `argument`;
        /// Monitor.Enter(locked, ref flag);
        /// try {
        ///     `body`
        /// } finally {
        ///     Monitor.Exit(locked);
        /// }
        /// </summary>
        public override BoundNode VisitLockStatement(BoundLockStatement node)
        {
            LockStatementSyntax lockSyntax = (LockStatementSyntax)node.Syntax;

            BoundExpression rewrittenArgument = VisitExpression(node.Argument);
            BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body);

            TypeSymbol argumentType = rewrittenArgument.Type;
            if ((object)argumentType == null)
            {
                // This isn't particularly elegant, but hopefully locking on null is
                // not very common.
                Debug.Assert(rewrittenArgument.ConstantValue == ConstantValue.Null);
                argumentType = this.compilation.GetSpecialType(SpecialType.System_Object);
                rewrittenArgument = MakeLiteral(
                    rewrittenArgument.Syntax,
                    rewrittenArgument.ConstantValue,
                    argumentType); //need to have a non-null type here for TempHelpers.StoreToTemp.
            }
            if (argumentType.Kind == SymbolKind.TypeParameter)
            {
                // If the argument has a type parameter type, then we'll box it right away
                // so that the same object is passed to both Monitor.Enter and Monitor.Exit.
                argumentType = this.compilation.GetSpecialType(SpecialType.System_Object);

                rewrittenArgument = MakeConversion(
                    rewrittenArgument.Syntax,
                    rewrittenArgument,
                    ConversionKind.Boxing,
                    argumentType,
                    @checked: false,
                    constantValueOpt: rewrittenArgument.ConstantValue);
            }

            BoundAssignmentOperator assignmentToLockTemp;
            BoundLocal boundLockTemp = this.factory.StoreToTemp(rewrittenArgument, out assignmentToLockTemp, kind: SynthesizedLocalKind.Lock);

            BoundStatement boundLockTempInit = new BoundExpressionStatement(lockSyntax, assignmentToLockTemp);
            if (this.GenerateDebugInfo)
            {
                boundLockTempInit = new BoundSequencePointWithSpan( // NOTE: the lock temp is uninitialized at this sequence point.
                    lockSyntax,
                    boundLockTempInit,
                    TextSpan.FromBounds(lockSyntax.LockKeyword.SpanStart, lockSyntax.CloseParenToken.Span.End));
            }

            BoundExpression exitCallExpr;

            MethodSymbol exitMethod;
            if (TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Exit, out exitMethod))
            {
                exitCallExpr = BoundCall.Synthesized(
                    lockSyntax,
                    null,
                    exitMethod,
                    boundLockTemp);
            }
            else
            {
                exitCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType);
            }

            BoundStatement exitCall = new BoundExpressionStatement(
                lockSyntax,
                exitCallExpr);

            MethodSymbol enterMethod;

            if ((TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter2, out enterMethod, isOptional: true) ||
                 TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter, out enterMethod)) && // If we didn't find the overload introduced in .NET 4.0, then use the older one. 
                enterMethod.ParameterCount == 2)
            {
                // C# 4.0 version
                // L locked;
                // bool flag = false;
                // try {
                //     locked = `argument`;
                //     Monitor.Enter(locked, ref flag);
                //     `body`
                // } finally {
                //     if (flag) Monitor.Exit(locked);
                // }

                TypeSymbol boolType = this.compilation.GetSpecialType(SpecialType.System_Boolean);
                BoundAssignmentOperator assignmentToTemp;
                BoundLocal boundFlagTemp = this.factory.StoreToTemp(
                    MakeLiteral(rewrittenArgument.Syntax, ConstantValue.False, boolType),
                    kind: SynthesizedLocalKind.LockTaken,
                    store: out assignmentToTemp);

                BoundStatement boundFlagTempInit = new BoundExpressionStatement(lockSyntax, assignmentToTemp);
                if (this.GenerateDebugInfo)
                {
                    // hide the preamble code, we should not stop until we get to " locked = `argument`; "
                    boundFlagTempInit = new BoundSequencePoint(null, boundFlagTempInit);
                }

                BoundStatement enterCall = new BoundExpressionStatement(
                    lockSyntax,
                    BoundCall.Synthesized(
                        lockSyntax,
                        null,
                        enterMethod,
                        boundLockTemp,
                        boundFlagTemp));

                exitCall = RewriteIfStatement(
                    lockSyntax,
                    ImmutableArray<LocalSymbol>.Empty,
                    boundFlagTemp,
                    exitCall,
                    null,
                    node.HasErrors);

                return new BoundBlock(
                    lockSyntax,
                    node.Locals.Concat(ImmutableArray.Create<LocalSymbol>(boundLockTemp.LocalSymbol, boundFlagTemp.LocalSymbol)),
                    ImmutableArray.Create<BoundStatement>(
                        boundFlagTempInit,
                        new BoundTryStatement(
                            lockSyntax,
                            BoundBlock.SynthesizedNoLocals(lockSyntax, boundLockTempInit, enterCall, rewrittenBody),
                            ImmutableArray<BoundCatchBlock>.Empty,
                            BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall))));
            }
            else
            {
                BoundExpression enterCallExpr;

                if ((object)enterMethod != null)
                {
                    Debug.Assert(enterMethod.ParameterCount == 1);
                    // Pre-4.0 version
                    // L locked = `argument`;
                    // Monitor.Enter(locked, ref flag); //NB: before try-finally so we don't Exit if an exception prevents us from acquiring the lock.
                    // try {
                    //     `body`
                    // } finally {
                    //     Monitor.Exit(locked);
                    // }

                    enterCallExpr = BoundCall.Synthesized(
                        lockSyntax,
                        null,
                        enterMethod,
                        boundLockTemp);
                }
                else
                {
                    enterCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType);
                }

                BoundStatement enterCall = new BoundExpressionStatement(
                    lockSyntax,
                    enterCallExpr);

                return new BoundBlock(
                    lockSyntax,
                    node.Locals.Add(boundLockTemp.LocalSymbol),
                    ImmutableArray.Create<BoundStatement>(
                        boundLockTempInit,
                        enterCall,
                        new BoundTryStatement(
                            lockSyntax,
                            BoundBlock.SynthesizedNoLocals(lockSyntax, rewrittenBody),
                            ImmutableArray<BoundCatchBlock>.Empty,
                            BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall))));
            }
        }
        private BoundStatement RewriteForStatement(
            CSharpSyntaxNode syntax,
            ImmutableArray <LocalSymbol> outerLocals,
            BoundStatement rewrittenInitializer,
            ImmutableArray <LocalSymbol> innerLocals,
            BoundExpression rewrittenCondition,
            SyntaxNodeOrToken conditionSyntax,
            BoundStatement rewrittenIncrement,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            Debug.Assert(rewrittenBody != null);

            // The sequence point behavior exhibited here is different from that of the native compiler.  In the native
            // compiler, if you have something like
            //
            // for(int i = 0, j = 0; ; i++, j++)
            //     ^--------------^    ^------^
            //
            // then all the initializers are treated as a single sequence point, as are
            // all the loop incrementers.
            //
            // We now make each one individually a sequence point:
            //
            // for(int i = 0, j = 0; ; i++, j++)
            //     ^-------^  ^---^    ^-^  ^-^
            //
            // If we decide that we want to preserve the native compiler stepping behavior
            // then we'll need to be a bit fancy here. The initializer and increment statements
            // can contain lambdas whose bodies need to have sequence points inserted, so we
            // need to make sure we visit the children. But we'll also need to make sure that
            // we do not generate one sequence point for each statement in the initializers
            // and the incrementers.

            var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance();

            if (rewrittenInitializer != null)
            {
                statementBuilder.Add(rewrittenInitializer);
            }

            var startLabel = new GeneratedLabelSymbol("start");

            if (!innerLocals.IsDefaultOrEmpty)
            {
                var walker = new AnyLocalCapturedInALambdaWalker(innerLocals);

                if (walker.Analyze(rewrittenCondition) || walker.Analyze(rewrittenIncrement) || walker.Analyze(rewrittenBody))
                {
                    // If any inner local is captured within a lambda, we need to enter scope-block
                    // always from the top, that is where an instance of a display class will be created.
                    // The IL will be less optimal, but this shouldn't be a problem, given presence of lambdas.

                    // for (initializer; condition; increment)
                    //   body;
                    //
                    // becomes the following (with
                    // block added for locals)
                    //
                    // {
                    //   initializer;
                    // start:
                    //   {
                    //     GotoIfFalse condition break;
                    //     body;
                    // continue:
                    //     increment;
                    //     goto start;
                    //   }
                    // break:
                    // }

                    // start:
                    statementBuilder.Add(new BoundLabelStatement(syntax, startLabel));

                    var blockBuilder = ArrayBuilder <BoundStatement> .GetInstance();

                    //   GotoIfFalse condition break;
                    if (rewrittenCondition != null)
                    {
                        BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel);

                        if (this.generateDebugInfo)
                        {
                            if (conditionSyntax.IsToken)
                            {
                                ifNotConditionGotoBreak = new BoundSequencePointWithSpan(syntax, ifNotConditionGotoBreak, conditionSyntax.Span);
                            }
                            else
                            {
                                ifNotConditionGotoBreak = new BoundSequencePoint((CSharpSyntaxNode)conditionSyntax.AsNode(), ifNotConditionGotoBreak);
                            }
                        }

                        blockBuilder.Add(ifNotConditionGotoBreak);
                    }

                    // body;
                    blockBuilder.Add(rewrittenBody);

                    // continue:
                    //   increment;
                    blockBuilder.Add(new BoundLabelStatement(syntax, continueLabel));
                    if (rewrittenIncrement != null)
                    {
                        blockBuilder.Add(rewrittenIncrement);
                    }

                    //     goto start;
                    blockBuilder.Add(new BoundGotoStatement(syntax, startLabel));

                    statementBuilder.Add(new BoundBlock(syntax, innerLocals, blockBuilder.ToImmutableAndFree()));

                    // break:
                    statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel));

                    return(new BoundBlock(syntax, outerLocals, statementBuilder.ToImmutableAndFree(), hasErrors));
                }
            }

            var endLabel = new GeneratedLabelSymbol("end");

            // for (initializer; condition; increment)
            //   body;
            //
            // becomes the following (with
            // block added for locals)
            //
            // {
            //   initializer;
            //   goto end;
            // start:
            //   body;
            // continue:
            //   increment;
            // end:
            //   GotoIfTrue condition start;
            // break:
            // }

            //  initializer;
            //  goto end;

            //mark the initial jump as hidden.
            //We do it to tell that this is not a part of previous statement.
            //This jump may be a target of another jump (for example if loops are nested) and that will make
            //impression of the previous statement being re-executed
            var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel));

            statementBuilder.Add(gotoEnd);

            // start:
            //   body;
            statementBuilder.Add(new BoundLabelStatement(syntax, startLabel));

            ArrayBuilder <BoundStatement> saveBuilder = null;

            if (!innerLocals.IsDefaultOrEmpty)
            {
                saveBuilder      = statementBuilder;
                statementBuilder = ArrayBuilder <BoundStatement> .GetInstance();
            }

            statementBuilder.Add(rewrittenBody);

            // continue:
            //   increment;
            statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel));
            if (rewrittenIncrement != null)
            {
                statementBuilder.Add(rewrittenIncrement);
            }

            // end:
            //   GotoIfTrue condition start;
            statementBuilder.Add(new BoundLabelStatement(syntax, endLabel));
            BoundStatement branchBack = null;

            if (rewrittenCondition != null)
            {
                branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);
            }
            else
            {
                branchBack = new BoundGotoStatement(syntax, startLabel);
            }

            if (this.generateDebugInfo)
            {
                if (conditionSyntax.IsToken)
                {
                    branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSyntax.Span);
                }
                else
                {
                    //if there is no condition, make this a hidden point so that
                    //it does not count as a part of previous statement
                    branchBack = new BoundSequencePoint((CSharpSyntaxNode)conditionSyntax.AsNode(), branchBack);
                }
            }

            statementBuilder.Add(branchBack);

            if (!innerLocals.IsDefaultOrEmpty)
            {
                var block = new BoundBlock(syntax, innerLocals, statementBuilder.ToImmutableAndFree());
                statementBuilder = saveBuilder;
                statementBuilder.Add(block);
            }

            // break:
            statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel));

            var statements = statementBuilder.ToImmutableAndFree();

            return(new BoundBlock(syntax, outerLocals, statements, hasErrors));
        }
Example #30
0
        /// <summary>
        /// Lowers a lock statement to a try-finally block that calls Monitor.Enter and Monitor.Exit
        /// before and after the body, respectively.
        ///
        /// C# 4.0 version
        ///
        /// L locked;
        /// bool flag = false;
        /// try {
        ///     locked = `argument`;
        ///     Monitor.Enter(locked, ref flag);
        ///     `body`
        /// } finally {
        ///     if (flag) Monitor.Exit(locked);
        /// }
        ///
        /// Pre-4.0 version
        ///
        /// L locked = `argument`;
        /// Monitor.Enter(locked, ref flag);
        /// try {
        ///     `body`
        /// } finally {
        ///     Monitor.Exit(locked);
        /// }
        /// </summary>
        public override BoundNode VisitLockStatement(BoundLockStatement node)
        {
            LockStatementSyntax lockSyntax = (LockStatementSyntax)node.Syntax;

            BoundExpression rewrittenArgument = VisitExpression(node.Argument);
            BoundStatement  rewrittenBody     = (BoundStatement)Visit(node.Body);

            TypeSymbol argumentType = rewrittenArgument.Type;

            if ((object)argumentType == null)
            {
                // This isn't particularly elegant, but hopefully locking on null is
                // not very common.
                Debug.Assert(rewrittenArgument.ConstantValue == ConstantValue.Null);
                argumentType      = this.compilation.GetSpecialType(SpecialType.System_Object);
                rewrittenArgument = MakeLiteral(
                    rewrittenArgument.Syntax,
                    rewrittenArgument.ConstantValue,
                    argumentType); //need to have a non-null type here for TempHelpers.StoreToTemp.
            }
            if (argumentType.Kind == SymbolKind.TypeParameter)
            {
                // If the argument has a type parameter type, then we'll box it right away
                // so that the same object is passed to both Monitor.Enter and Monitor.Exit.
                argumentType = this.compilation.GetSpecialType(SpecialType.System_Object);

                rewrittenArgument = MakeConversion(
                    rewrittenArgument.Syntax,
                    rewrittenArgument,
                    ConversionKind.Boxing,
                    argumentType,
                    @checked: false,
                    constantValueOpt: rewrittenArgument.ConstantValue);
            }

            BoundAssignmentOperator assignmentToLockTemp;
            BoundLocal boundLockTemp = this.factory.StoreToTemp(rewrittenArgument, tempKind: TempKind.Lock, store: out assignmentToLockTemp);

            BoundStatement boundLockTempInit = new BoundExpressionStatement(lockSyntax, assignmentToLockTemp);

            if (this.generateDebugInfo)
            {
                boundLockTempInit = new BoundSequencePointWithSpan( // NOTE: the lock temp is uninitialized at this sequence point.
                    lockSyntax,
                    boundLockTempInit,
                    TextSpan.FromBounds(lockSyntax.LockKeyword.SpanStart, lockSyntax.CloseParenToken.Span.End));
            }

            BoundExpression exitCallExpr;

            MethodSymbol exitMethod;

            if (TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Exit, out exitMethod))
            {
                exitCallExpr = BoundCall.Synthesized(
                    lockSyntax,
                    null,
                    exitMethod,
                    boundLockTemp);
            }
            else
            {
                exitCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType);
            }

            BoundStatement exitCall = new BoundExpressionStatement(
                lockSyntax,
                exitCallExpr);

            MethodSymbol enterMethod;

            if ((TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter2, out enterMethod, isOptional: true) ||
                 TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter, out enterMethod)) && // If we didn't find the overload introduced in .NET 4.0, then use the older one.
                enterMethod.ParameterCount == 2)
            {
                // C# 4.0 version
                // L locked;
                // bool flag = false;
                // try {
                //     locked = `argument`;
                //     Monitor.Enter(locked, ref flag);
                //     `body`
                // } finally {
                //     if (flag) Monitor.Exit(locked);
                // }

                TypeSymbol boolType = this.compilation.GetSpecialType(SpecialType.System_Boolean);
                BoundAssignmentOperator assignmentToTemp;
                BoundLocal boundFlagTemp = this.factory.StoreToTemp(
                    MakeLiteral(rewrittenArgument.Syntax, ConstantValue.False, boolType),
                    tempKind: TempKind.LockTaken,
                    store: out assignmentToTemp);

                BoundStatement boundFlagTempInit = new BoundExpressionStatement(lockSyntax, assignmentToTemp);
                if (this.generateDebugInfo)
                {
                    // hide the preamble code, we should not stop until we get to " locked = `argument`; "
                    boundFlagTempInit = new BoundSequencePoint(null, boundFlagTempInit);
                }

                BoundStatement enterCall = new BoundExpressionStatement(
                    lockSyntax,
                    BoundCall.Synthesized(
                        lockSyntax,
                        null,
                        enterMethod,
                        boundLockTemp,
                        boundFlagTemp));

                exitCall = RewriteIfStatement(
                    lockSyntax,
                    boundFlagTemp,
                    exitCall,
                    null,
                    node.HasErrors);

                return(new BoundBlock(
                           lockSyntax,
                           ImmutableArray.Create <LocalSymbol>(boundLockTemp.LocalSymbol, boundFlagTemp.LocalSymbol),
                           ImmutableArray.Create <BoundStatement>(
                               boundFlagTempInit,
                               new BoundTryStatement(
                                   lockSyntax,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, boundLockTempInit, enterCall, rewrittenBody),
                                   ImmutableArray <BoundCatchBlock> .Empty,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall)))));
            }
            else
            {
                BoundExpression enterCallExpr;

                if ((object)enterMethod != null)
                {
                    Debug.Assert(enterMethod.ParameterCount == 1);
                    // Pre-4.0 version
                    // L locked = `argument`;
                    // Monitor.Enter(locked, ref flag); //NB: before try-finally so we don't Exit if an exception prevents us from acquiring the lock.
                    // try {
                    //     `body`
                    // } finally {
                    //     Monitor.Exit(locked);
                    // }

                    enterCallExpr = BoundCall.Synthesized(
                        lockSyntax,
                        null,
                        enterMethod,
                        boundLockTemp);
                }
                else
                {
                    enterCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType);
                }

                BoundStatement enterCall = new BoundExpressionStatement(
                    lockSyntax,
                    enterCallExpr);

                return(new BoundBlock(
                           lockSyntax,
                           ImmutableArray.Create <LocalSymbol>(boundLockTemp.LocalSymbol),
                           ImmutableArray.Create <BoundStatement>(
                               boundLockTempInit,
                               enterCall,
                               new BoundTryStatement(
                                   lockSyntax,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, rewrittenBody),
                                   ImmutableArray <BoundCatchBlock> .Empty,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall)))));
            }
        }
        private BoundStatement RewriteForStatement(
            CSharpSyntaxNode syntax,
            ImmutableArray <LocalSymbol> outerLocals,
            BoundStatement rewrittenInitializer,
            BoundExpression rewrittenCondition,
            CSharpSyntaxNode conditionSyntaxOpt,
            TextSpan conditionSpanOpt,
            BoundStatement rewrittenIncrement,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            Debug.Assert(rewrittenBody != null);

            // The sequence point behavior exhibited here is different from that of the native compiler.  In the native
            // compiler, if you have something like
            //
            // for([|int i = 0, j = 0|]; ; [|i++, j++|])
            //
            // then all the initializers are treated as a single sequence point, as are
            // all the loop incrementers.
            //
            // We now make each one individually a sequence point:
            //
            // for([|int i = 0|], [|j = 0|]; ; [|i++|], [|j++|])
            //
            // If we decide that we want to preserve the native compiler stepping behavior
            // then we'll need to be a bit fancy here. The initializer and increment statements
            // can contain lambdas whose bodies need to have sequence points inserted, so we
            // need to make sure we visit the children. But we'll also need to make sure that
            // we do not generate one sequence point for each statement in the initializers
            // and the incrementers.

            var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance();

            if (rewrittenInitializer != null)
            {
                statementBuilder.Add(rewrittenInitializer);
            }

            var startLabel = new GeneratedLabelSymbol("start");

            // for (initializer; condition; increment)
            //   body;
            //
            // becomes the following (with block added for locals)
            //
            // {
            //   initializer;
            //   goto end;
            // start:
            //   body;
            // continue:
            //   increment;
            // end:
            //   GotoIfTrue condition start;
            // break:
            // }

            var endLabel = new GeneratedLabelSymbol("end");

            //  initializer;
            //  goto end;

            // Mark the initial jump as hidden.
            // We do it to tell that this is not a part of previous statement.
            // This jump may be a target of another jump (for example if loops are nested) and that will make
            // impression of the previous statement being re-executed
            var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel));

            statementBuilder.Add(gotoEnd);

            // start:
            //   body;
            statementBuilder.Add(new BoundLabelStatement(syntax, startLabel));

            statementBuilder.Add(rewrittenBody);

            // continue:
            //   increment;
            statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel));
            if (rewrittenIncrement != null)
            {
                statementBuilder.Add(rewrittenIncrement);
            }

            // end:
            //   GotoIfTrue condition start;
            statementBuilder.Add(new BoundLabelStatement(syntax, endLabel));
            BoundStatement branchBack = null;

            if (rewrittenCondition != null)
            {
                branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);
            }
            else
            {
                branchBack = new BoundGotoStatement(syntax, startLabel);
            }

            if (this.GenerateDebugInfo)
            {
                if (!conditionSpanOpt.IsEmpty)
                {
                    branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSpanOpt);
                }
                else
                {
                    // hidden sequence point if there is no condition
                    branchBack = new BoundSequencePoint(conditionSyntaxOpt, branchBack);
                }
            }

            statementBuilder.Add(branchBack);

            // break:
            statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel));

            var statements = statementBuilder.ToImmutableAndFree();

            return(new BoundBlock(syntax, outerLocals, statements, hasErrors));
        }
        // insert the implicit "return" statement at the end of the method body
        // Normally, we wouldn't bother attaching syntax trees to compiler-generated nodes, but these
        // ones are going to have sequence points.
        internal static BoundBlock AppendImplicitReturn(BoundBlock body, MethodSymbol method, CSharpSyntaxNode syntax = null)
        {
            Debug.Assert(body != null);
            Debug.Assert(method != null);

            if (syntax == null)
            {
                syntax = body.Syntax;
            }

            Debug.Assert(body.WasCompilerGenerated || syntax.IsKind(SyntaxKind.Block) || syntax.IsKind(SyntaxKind.ArrowExpressionClause));

            BoundStatement ret = method.IsIterator
                ? (BoundStatement)BoundYieldBreakStatement.Synthesized(syntax)
                : BoundReturnStatement.Synthesized(syntax, null);

            // Implicitly added return for async method does not need sequence points since lowering would add one.
            if (syntax.IsKind(SyntaxKind.Block) && !method.IsAsync)
            {
                var blockSyntax = (BlockSyntax)syntax;

                ret = new BoundSequencePointWithSpan(
                    blockSyntax,
                    ret,
                    blockSyntax.CloseBraceToken.Span)
                { WasCompilerGenerated = true };
            }

            return body.Update(body.Locals, body.Statements.Add(ret));
        }
        private BoundStatement RewriteWhileStatement(
            CSharpSyntaxNode syntax,
            ImmutableArray<LocalSymbol> innerLocals,
            BoundExpression rewrittenCondition,
            TextSpan conditionSequencePointSpan,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            if (!innerLocals.IsDefaultOrEmpty)
            {
                var walker = new AnyLocalCapturedInALambdaWalker(innerLocals);

                if (walker.Analyze(rewrittenCondition) || walker.Analyze(rewrittenBody))
                {
                    // If any inner local is captured within a lambda, we need to enter scope-block
                    // always from the top, that is where an instance of a display class will be created.
                    // The IL will be less optimal, but this shouldn't be a problem, given presence of lambdas.

                    // while (condition) 
                    //   body;
                    //
                    // becomes
                    //
                    // continue:
                    // {
                    //     GotoIfFalse condition break;
                    //     body
                    //     goto continue;
                    // }
                    // break:

                    // TODO: We could perform more fine analysis. 
                    // If locals declared in condition (the innerLocals) are captured, but not referenced in the body, we could use optimal IL by creating
                    // another block around the condition and use it as a scope for the locals declared in condition.
                    // This optimization can be applied to 'for' as well, while-body === for-body + increment.
                    // Note however that the scope adjusments will likely be observable during debugging, in locals window.

                    BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel);

                    if (this.GenerateDebugInfo)
                    {
                        ifNotConditionGotoBreak = new BoundSequencePointWithSpan(syntax, ifNotConditionGotoBreak, conditionSequencePointSpan);
                    }

                    return BoundStatementList.Synthesized(syntax, hasErrors,
                        new BoundLabelStatement(syntax, continueLabel),
                        new BoundBlock(syntax,
                                       innerLocals,
                                       ImmutableArray.Create(
                                            ifNotConditionGotoBreak,
                                            rewrittenBody,
                                            new BoundGotoStatement(syntax, continueLabel))),
                        new BoundLabelStatement(syntax, breakLabel));
                }
            }

            var startLabel = new GeneratedLabelSymbol("start");
            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);

            if (this.GenerateDebugInfo)
            {
                ifConditionGotoStart = new BoundSequencePointWithSpan(syntax, ifConditionGotoStart, conditionSequencePointSpan);
            }

            // while (condition) 
            //   body;
            //
            // becomes
            //
            // goto continue;
            // start: 
            // {
            //     body
            //     continue:
            //     GotoIfTrue condition start;
            // }
            // break:

            BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel);
            if (this.GenerateDebugInfo)
            {
                // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This
                // jump may be a target of another jump (for example if loops are nested) and that would give the
                // impression that the previous statement is being re-executed.
                gotoContinue = new BoundSequencePoint(null, gotoContinue);
            }

            if (!innerLocals.IsDefaultOrEmpty)
            {
                return BoundStatementList.Synthesized(syntax, hasErrors,
                    gotoContinue,
                    new BoundLabelStatement(syntax, startLabel),
                    new BoundBlock(syntax,
                                   innerLocals,
                                   ImmutableArray.Create<BoundStatement>(
                                        rewrittenBody,
                                        new BoundLabelStatement(syntax, continueLabel),
                                        ifConditionGotoStart)),
                    new BoundLabelStatement(syntax, breakLabel));
            }

            return BoundStatementList.Synthesized(syntax, hasErrors,
                gotoContinue,
                new BoundLabelStatement(syntax, startLabel),
                rewrittenBody,
                new BoundLabelStatement(syntax, continueLabel),
                ifConditionGotoStart,
                new BoundLabelStatement(syntax, breakLabel));
        }
        /// <summary>
        /// Lower "using (expression) statement" to a try-finally block.
        /// </summary>
        private BoundBlock RewriteExpressionUsingStatement(BoundUsingStatement node, BoundBlock tryBlock)
        {
            Debug.Assert(node.ExpressionOpt != null);
            Debug.Assert(node.DeclarationsOpt == null);

            // See comments in BuildUsingTryFinally for the details of the lowering to try-finally.
            //
            // SPEC: A using statement of the form "using (expression) statement; " has the 
            // SPEC: same three possible expansions [ as "using (ResourceType r = expression) statement; ]
            // SPEC: but in this case ResourceType is implicitly the compile-time type of the expression,
            // SPEC: and the resource variable is inaccessible to and invisible to the embedded statement.
            //
            // DELIBERATE SPEC VIOLATION: 
            //
            // The spec quote above implies that the expression must have a type; in fact we allow
            // the expression to be null.
            //
            // If expr is the constant null then we can elide the whole thing and simply generate the statement. 

            BoundExpression rewrittenExpression = (BoundExpression)Visit(node.ExpressionOpt);
            if (rewrittenExpression.ConstantValue == ConstantValue.Null)
            {
                return tryBlock;
            }

            // Otherwise, we lower "using(expression) statement;" as follows:
            //
            // * If the expression is of type dynamic then we lower as though the user had written
            //
            //   using(IDisposable temp = (IDisposable)expression) statement;
            //
            //   Note that we have to do the conversion early, not in the finally block, because
            //   if the conversion fails at runtime with an exception then the exception must happen
            //   before the statement runs.
            //
            // * Otherwise we lower as though the user had written
            // 
            //   using(ResourceType temp = expression) statement;
            //

            TypeSymbol expressionType = rewrittenExpression.Type;
            CSharpSyntaxNode expressionSyntax = rewrittenExpression.Syntax;
            UsingStatementSyntax usingSyntax = (UsingStatementSyntax)node.Syntax;

            BoundAssignmentOperator tempAssignment;
            BoundLocal boundTemp;
            if ((object)expressionType == null || expressionType.IsDynamic())
            {
                // IDisposable temp = (IDisposable) expr;
                BoundExpression tempInit = MakeConversion(
                    expressionSyntax,
                    rewrittenExpression,
                    node.IDisposableConversion.Kind,
                    this.compilation.GetSpecialType(SpecialType.System_IDisposable),
                    @checked: false,
                    constantValueOpt: rewrittenExpression.ConstantValue);

                boundTemp = this.factory.StoreToTemp(tempInit, out tempAssignment);
            }
            else
            {
                // ResourceType temp = expr;
                boundTemp = this.factory.StoreToTemp(rewrittenExpression, tempKind: TempKind.Using, store: out tempAssignment);
            }

            BoundStatement expressionStatement = new BoundExpressionStatement(expressionSyntax, tempAssignment);
            if (this.generateDebugInfo)
            {
                // NOTE: unlike in the assignment case, the sequence point is on the using keyword, not the expression.
                expressionStatement = new BoundSequencePointWithSpan(usingSyntax, expressionStatement, usingSyntax.UsingKeyword.Span);
            }

            BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp);

            // { ResourceType temp = expr; try { ... } finally { ... } }
            return new BoundBlock(
                syntax: usingSyntax,
                localsOpt: ImmutableArray.Create<LocalSymbol>(boundTemp.LocalSymbol),
                statements: ImmutableArray.Create<BoundStatement>(expressionStatement, tryFinally));
        }
 /// <summary>
 /// Add sequence point |here|:
 /// 
 /// foreach (|Type var| in expr) { }
 /// </summary>
 /// <remarks>
 /// Hit every iteration.
 /// </remarks>
 private void AddForEachIterationVariableSequencePoint(ForEachStatementSyntax forEachSyntax, ref BoundStatement iterationVarDecl)
 {
     if (this.GenerateDebugInfo)
     {
         TextSpan iterationVarDeclSpan = TextSpan.FromBounds(forEachSyntax.Type.SpanStart, forEachSyntax.Identifier.Span.End);
         iterationVarDecl = new BoundSequencePointWithSpan(forEachSyntax, iterationVarDecl, iterationVarDeclSpan);
     }
 }