/// <summary> /// Rewrite a using statement into a try finally statement. Two forms are possible: /// 1) using (expr) stmt /// 2) using (C c = expr) stmt /// /// The former is handled by RewriteExpressionUsingStatement and the latter is handled by /// RewriteDeclarationUsingStatement (called in a loop, once for each local declared). /// </summary> /// <remarks> /// It would be more in line with our usual pattern to rewrite using to try-finally /// in the ControlFlowRewriter, but if we don't do it here the BoundMultipleLocalDeclarations /// will be rewritten into a form that makes them harder to separate. /// </remarks> public override BoundNode VisitUsingStatement(BoundUsingStatement node) { BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); BoundBlock tryBlock = rewrittenBody.Kind == BoundKind.Block ? (BoundBlock)rewrittenBody : BoundBlock.SynthesizedNoLocals(node.Syntax, rewrittenBody); if (node.ExpressionOpt != null) { return RewriteExpressionUsingStatement(node, tryBlock); } else { Debug.Assert(node.DeclarationsOpt != null); SyntaxNode usingSyntax = node.Syntax; Conversion idisposableConversion = node.IDisposableConversion; ImmutableArray<BoundLocalDeclaration> declarations = node.DeclarationsOpt.LocalDeclarations; BoundBlock result = tryBlock; int numDeclarations = declarations.Length; for (int i = numDeclarations - 1; i >= 0; i--) //NB: inner-to-outer = right-to-left { result = RewriteDeclarationUsingStatement(usingSyntax, declarations[i], result, idisposableConversion); } // Declare all locals in a single, top-level block so that the scope is correct in the debugger // (Dev10 has them all come into scope at once, not per-declaration.) return new BoundBlock( usingSyntax, node.Locals, ImmutableArray<LocalFunctionSymbol>.Empty, ImmutableArray.Create<BoundStatement>(result)); } }
/// <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) { Debug.Assert(node.Locals.IsEmpty); // TODO: This might not be a valid assumption in presence of semicolon operator. 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, out tempAssignment, kind: SynthesizedLocalKind.Using); } BoundStatement expressionStatement = new BoundExpressionStatement(expressionSyntax, tempAssignment); if (this.generateDebugInfo) { expressionStatement = AddSequencePoint(usingSyntax, expressionStatement); } BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp); // { ResourceType temp = expr; try { ... } finally { ... } } return new BoundBlock( syntax: usingSyntax, locals: node.Locals.Add(boundTemp.LocalSymbol), statements: ImmutableArray.Create<BoundStatement>(expressionStatement, tryFinally)); }
public override BoundNode VisitUsingStatement(BoundUsingStatement node) { throw ExceptionUtilities.Unreachable; // using statements have been lowered away by now }
public override BoundStatement InstrumentUsingTargetCapture(BoundUsingStatement original, BoundStatement usingTargetCapture) { return Previous.InstrumentUsingTargetCapture(original, usingTargetCapture); }
public override BoundStatement InstrumentUsingTargetCapture(BoundUsingStatement original, BoundStatement usingTargetCapture) { return AddSequencePoint((UsingStatementSyntax)original.Syntax, base.InstrumentUsingTargetCapture(original, usingTargetCapture)); }
public override BoundStatement InstrumentUsingTargetCapture(BoundUsingStatement original, BoundStatement usingTargetCapture) { return AddDynamicAnalysis(original, base.InstrumentUsingTargetCapture(original, usingTargetCapture)); }
public virtual BoundStatement InstrumentUsingTargetCapture(BoundUsingStatement original, BoundStatement usingTargetCapture) { Debug.Assert(!original.WasCompilerGenerated); Debug.Assert(original.Syntax.Kind() == SyntaxKind.UsingStatement); return usingTargetCapture; }
public override BoundStatement InstrumentUsingTargetCapture(BoundUsingStatement original, BoundStatement usingTargetCapture) { return(AddSequencePoint((UsingStatementSyntax)original.Syntax, base.InstrumentUsingTargetCapture(original, usingTargetCapture))); }
public virtual BoundStatement InstrumentUsingTargetCapture(BoundUsingStatement original, BoundStatement usingTargetCapture) { Debug.Assert(!original.WasCompilerGenerated); Debug.Assert(original.Syntax.Kind() == SyntaxKind.UsingStatement); return(usingTargetCapture); }
/// <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) { Debug.Assert(node.Locals.IsEmpty); // TODO: This might not be a valid assumption in presence of semicolon operator. 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; SyntaxNode expressionSyntax = rewrittenExpression.Syntax; UsingStatementSyntax usingSyntax = (UsingStatementSyntax)node.Syntax; BoundAssignmentOperator tempAssignment; BoundLocal boundTemp; if ((object)expressionType == null || expressionType.IsDynamic()) { // IDisposable temp = (IDisposable) expr; BoundExpression tempInit = MakeConversionNode( expressionSyntax, rewrittenExpression, Conversion.GetTrivialConversion(node.IDisposableConversion.Kind), _compilation.GetSpecialType(SpecialType.System_IDisposable), @checked: false, constantValueOpt: rewrittenExpression.ConstantValue); boundTemp = _factory.StoreToTemp(tempInit, out tempAssignment, kind: SynthesizedLocalKind.Using); } else { // ResourceType temp = expr; boundTemp = _factory.StoreToTemp(rewrittenExpression, out tempAssignment, syntaxOpt: usingSyntax, kind: SynthesizedLocalKind.Using); } BoundStatement expressionStatement = new BoundExpressionStatement(expressionSyntax, tempAssignment); if (this.Instrument) { expressionStatement = _instrumenter.InstrumentUsingTargetCapture(node, expressionStatement); } BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp); // { ResourceType temp = expr; try { ... } finally { ... } } return(new BoundBlock( syntax: usingSyntax, locals: node.Locals.Add(boundTemp.LocalSymbol), localFunctions: ImmutableArray <LocalFunctionSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>(expressionStatement, tryFinally))); }
/// <summary> /// Rewrite a using statement into a try finally statement. Four forms are possible: /// 1) using (expr) stmt /// 2) await using (expr) stmt /// 3) using (C c = expr) stmt /// 4) await using (C c = expr) stmt /// /// The first two are handled by RewriteExpressionUsingStatement and the latter two are handled by /// RewriteDeclarationUsingStatement (called in a loop, once for each local declared). /// /// For the async variants, `IAsyncDisposable` is used instead of `IDisposable` and we produce /// `... await expr.DisposeAsync() ...` instead of `... expr.Dispose() ...`. /// </summary> /// <remarks> /// It would be more in line with our usual pattern to rewrite using to try-finally /// in the ControlFlowRewriter, but if we don't do it here the BoundMultipleLocalDeclarations /// will be rewritten into a form that makes them harder to separate. /// </remarks> public override BoundNode VisitUsingStatement(BoundUsingStatement node) { BoundStatement?rewrittenBody = VisitStatement(node.Body); Debug.Assert(rewrittenBody is { });
public override BoundNode VisitUsingStatement(BoundUsingStatement node) { Fail(node); return(null); }
/// <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))); }
public override BoundStatement InstrumentUsingTargetCapture(BoundUsingStatement original, BoundStatement usingTargetCapture) { return(AddDynamicAnalysis(original, base.InstrumentUsingTargetCapture(original, usingTargetCapture))); }
public override BoundStatement InstrumentUsingTargetCapture(BoundUsingStatement original, BoundStatement usingTargetCapture) { return(Previous.InstrumentUsingTargetCapture(original, usingTargetCapture)); }