diff --git a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs index 48d24a282d272..0a918b45d7cdf 100644 --- a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs @@ -100,7 +100,6 @@ internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNo Debug.Assert((object)disposableInterface != null); bool hasErrors = ReportUseSite(disposableInterface, diagnostics, hasAwait ? awaitKeyword : usingKeyword); - Conversion iDisposableConversion; ImmutableArray declarationsOpt = default; BoundMultipleLocalDeclarations? multipleDeclarationsOpt = null; BoundExpression? expressionOpt = null; @@ -111,7 +110,7 @@ internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNo if (isExpression) { expressionOpt = usingBinderOpt!.BindTargetExpression(diagnostics, originalBinder); - hasErrors |= !populateDisposableConversionOrDisposeMethod(fromExpression: true, out iDisposableConversion, out patternDisposeInfo, out awaitableTypeOpt); + hasErrors |= !bindDisposable(fromExpression: true, out patternDisposeInfo, out awaitableTypeOpt); } else { @@ -124,13 +123,12 @@ internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNo if (declarationTypeOpt.IsDynamic()) { - iDisposableConversion = Conversion.ImplicitDynamic; patternDisposeInfo = null; awaitableTypeOpt = null; } else { - hasErrors |= !populateDisposableConversionOrDisposeMethod(fromExpression: false, out iDisposableConversion, out patternDisposeInfo, out awaitableTypeOpt); + hasErrors |= !bindDisposable(fromExpression: false, out patternDisposeInfo, out awaitableTypeOpt); } } @@ -156,7 +154,7 @@ internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNo // In the future it might be better to have a separate shared type that we add the info to, and have the callers create the appropriate bound nodes from it if (isUsingDeclaration) { - return new BoundUsingLocalDeclarations(syntax, patternDisposeInfo, iDisposableConversion, awaitOpt, declarationsOpt, hasErrors); + return new BoundUsingLocalDeclarations(syntax, patternDisposeInfo, awaitOpt, declarationsOpt, hasErrors); } else { @@ -167,17 +165,16 @@ internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNo usingBinderOpt.Locals, multipleDeclarationsOpt, expressionOpt, - iDisposableConversion, boundBody, awaitOpt, patternDisposeInfo, hasErrors); } - bool populateDisposableConversionOrDisposeMethod(bool fromExpression, out Conversion iDisposableConversion, out MethodArgumentInfo? patternDisposeInfo, out TypeSymbol? awaitableType) + bool bindDisposable(bool fromExpression, out MethodArgumentInfo? patternDisposeInfo, out TypeSymbol? awaitableType) { CompoundUseSiteInfo useSiteInfo = originalBinder.GetNewCompoundUseSiteInfo(diagnostics); - iDisposableConversion = classifyConversion(fromExpression, disposableInterface, ref useSiteInfo); + Conversion iDisposableConversion = classifyConversion(fromExpression, disposableInterface, ref useSiteInfo); patternDisposeInfo = null; awaitableType = null; @@ -261,12 +258,18 @@ Conversion classifyConversion(bool fromExpression, TypeSymbol targetInterface, r if (fromExpression) { Debug.Assert(expressionOpt is { }); - return conversions.ClassifyImplicitConversionFromExpression(expressionOpt, targetInterface, ref useSiteInfo); + var result = conversions.ClassifyImplicitConversionFromExpression(expressionOpt, targetInterface, ref useSiteInfo); + + Debug.Assert(expressionOpt.Type?.IsDynamic() != true || result.Kind == ConversionKind.ImplicitDynamic); + return result; } else { Debug.Assert(declarationTypeOpt is { }); - return conversions.ClassifyImplicitConversionFromType(declarationTypeOpt, targetInterface, ref useSiteInfo); + var result = conversions.ClassifyImplicitConversionFromType(declarationTypeOpt, targetInterface, ref useSiteInfo); + + Debug.Assert(!declarationTypeOpt.IsDynamic() || result.Kind == ConversionKind.ImplicitDynamic); + return result; } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index f91bc3bd98c5e..944d3b80181f3 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -933,7 +933,6 @@ --> - @@ -1099,7 +1098,6 @@ - diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 9dc7410eaf847..0b26553221935 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -3034,31 +3034,28 @@ public BoundMultipleLocalDeclarations Update(ImmutableArray localDeclarations, bool hasErrors = false) + public BoundUsingLocalDeclarations(SyntaxNode syntax, MethodArgumentInfo? patternDisposeInfoOpt, BoundAwaitableInfo? awaitOpt, ImmutableArray localDeclarations, bool hasErrors = false) : base(BoundKind.UsingLocalDeclarations, syntax, localDeclarations, hasErrors || awaitOpt.HasErrors() || localDeclarations.HasErrors()) { RoslynDebug.Assert(!localDeclarations.IsDefault, "Field 'localDeclarations' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); this.PatternDisposeInfoOpt = patternDisposeInfoOpt; - this.IDisposableConversion = iDisposableConversion; this.AwaitOpt = awaitOpt; } public MethodArgumentInfo? PatternDisposeInfoOpt { get; } - public Conversion IDisposableConversion { get; } - public BoundAwaitableInfo? AwaitOpt { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitUsingLocalDeclarations(this); - public BoundUsingLocalDeclarations Update(MethodArgumentInfo? patternDisposeInfoOpt, Conversion iDisposableConversion, BoundAwaitableInfo? awaitOpt, ImmutableArray localDeclarations) + public BoundUsingLocalDeclarations Update(MethodArgumentInfo? patternDisposeInfoOpt, BoundAwaitableInfo? awaitOpt, ImmutableArray localDeclarations) { - if (patternDisposeInfoOpt != this.PatternDisposeInfoOpt || iDisposableConversion != this.IDisposableConversion || awaitOpt != this.AwaitOpt || localDeclarations != this.LocalDeclarations) + if (patternDisposeInfoOpt != this.PatternDisposeInfoOpt || awaitOpt != this.AwaitOpt || localDeclarations != this.LocalDeclarations) { - var result = new BoundUsingLocalDeclarations(this.Syntax, patternDisposeInfoOpt, iDisposableConversion, awaitOpt, localDeclarations, this.HasErrors); + var result = new BoundUsingLocalDeclarations(this.Syntax, patternDisposeInfoOpt, awaitOpt, localDeclarations, this.HasErrors); result.CopyAttributes(this); return result; } @@ -3709,7 +3706,7 @@ public BoundForEachDeconstructStep Update(BoundDeconstructionAssignmentOperator internal sealed partial class BoundUsingStatement : BoundStatement { - public BoundUsingStatement(SyntaxNode syntax, ImmutableArray locals, BoundMultipleLocalDeclarations? declarationsOpt, BoundExpression? expressionOpt, Conversion iDisposableConversion, BoundStatement body, BoundAwaitableInfo? awaitOpt, MethodArgumentInfo? patternDisposeInfoOpt, bool hasErrors = false) + public BoundUsingStatement(SyntaxNode syntax, ImmutableArray locals, BoundMultipleLocalDeclarations? declarationsOpt, BoundExpression? expressionOpt, BoundStatement body, BoundAwaitableInfo? awaitOpt, MethodArgumentInfo? patternDisposeInfoOpt, bool hasErrors = false) : base(BoundKind.UsingStatement, syntax, hasErrors || declarationsOpt.HasErrors() || expressionOpt.HasErrors() || body.HasErrors() || awaitOpt.HasErrors()) { @@ -3719,7 +3716,6 @@ public BoundUsingStatement(SyntaxNode syntax, ImmutableArray locals this.Locals = locals; this.DeclarationsOpt = declarationsOpt; this.ExpressionOpt = expressionOpt; - this.IDisposableConversion = iDisposableConversion; this.Body = body; this.AwaitOpt = awaitOpt; this.PatternDisposeInfoOpt = patternDisposeInfoOpt; @@ -3732,8 +3728,6 @@ public BoundUsingStatement(SyntaxNode syntax, ImmutableArray locals public BoundExpression? ExpressionOpt { get; } - public Conversion IDisposableConversion { get; } - public BoundStatement Body { get; } public BoundAwaitableInfo? AwaitOpt { get; } @@ -3742,11 +3736,11 @@ public BoundUsingStatement(SyntaxNode syntax, ImmutableArray locals [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitUsingStatement(this); - public BoundUsingStatement Update(ImmutableArray locals, BoundMultipleLocalDeclarations? declarationsOpt, BoundExpression? expressionOpt, Conversion iDisposableConversion, BoundStatement body, BoundAwaitableInfo? awaitOpt, MethodArgumentInfo? patternDisposeInfoOpt) + public BoundUsingStatement Update(ImmutableArray locals, BoundMultipleLocalDeclarations? declarationsOpt, BoundExpression? expressionOpt, BoundStatement body, BoundAwaitableInfo? awaitOpt, MethodArgumentInfo? patternDisposeInfoOpt) { - if (locals != this.Locals || declarationsOpt != this.DeclarationsOpt || expressionOpt != this.ExpressionOpt || iDisposableConversion != this.IDisposableConversion || body != this.Body || awaitOpt != this.AwaitOpt || patternDisposeInfoOpt != this.PatternDisposeInfoOpt) + if (locals != this.Locals || declarationsOpt != this.DeclarationsOpt || expressionOpt != this.ExpressionOpt || body != this.Body || awaitOpt != this.AwaitOpt || patternDisposeInfoOpt != this.PatternDisposeInfoOpt) { - var result = new BoundUsingStatement(this.Syntax, locals, declarationsOpt, expressionOpt, iDisposableConversion, body, awaitOpt, patternDisposeInfoOpt, this.HasErrors); + var result = new BoundUsingStatement(this.Syntax, locals, declarationsOpt, expressionOpt, body, awaitOpt, patternDisposeInfoOpt, this.HasErrors); result.CopyAttributes(this); return result; } @@ -10375,7 +10369,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor { BoundAwaitableInfo? awaitOpt = (BoundAwaitableInfo?)this.Visit(node.AwaitOpt); ImmutableArray localDeclarations = this.VisitList(node.LocalDeclarations); - return node.Update(node.PatternDisposeInfoOpt, node.IDisposableConversion, awaitOpt, localDeclarations); + return node.Update(node.PatternDisposeInfoOpt, awaitOpt, localDeclarations); } public override BoundNode? VisitLocalFunctionStatement(BoundLocalFunctionStatement node) { @@ -10469,7 +10463,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundExpression? expressionOpt = (BoundExpression?)this.Visit(node.ExpressionOpt); BoundStatement body = (BoundStatement)this.Visit(node.Body); BoundAwaitableInfo? awaitOpt = (BoundAwaitableInfo?)this.Visit(node.AwaitOpt); - return node.Update(node.Locals, declarationsOpt, expressionOpt, node.IDisposableConversion, body, awaitOpt, node.PatternDisposeInfoOpt); + return node.Update(node.Locals, declarationsOpt, expressionOpt, body, awaitOpt, node.PatternDisposeInfoOpt); } public override BoundNode? VisitFixedStatement(BoundFixedStatement node) { @@ -12207,7 +12201,7 @@ public NullabilityRewriter(ImmutableDictionary new TreeDumperNode("usingLocalDeclarations", null, new TreeDumperNode[] { new TreeDumperNode("patternDisposeInfoOpt", node.PatternDisposeInfoOpt, null), - new TreeDumperNode("iDisposableConversion", node.IDisposableConversion, null), new TreeDumperNode("awaitOpt", null, new TreeDumperNode[] { Visit(node.AwaitOpt, null) }), new TreeDumperNode("localDeclarations", null, from x in node.LocalDeclarations select Visit(x, null)), new TreeDumperNode("hasErrors", node.HasErrors, null) @@ -14409,7 +14402,6 @@ private BoundTreeDumperNodeProducer() new TreeDumperNode("locals", node.Locals, null), new TreeDumperNode("declarationsOpt", null, new TreeDumperNode[] { Visit(node.DeclarationsOpt, null) }), new TreeDumperNode("expressionOpt", null, new TreeDumperNode[] { Visit(node.ExpressionOpt, null) }), - new TreeDumperNode("iDisposableConversion", node.IDisposableConversion, null), new TreeDumperNode("body", null, new TreeDumperNode[] { Visit(node.Body, null) }), new TreeDumperNode("awaitOpt", null, new TreeDumperNode[] { Visit(node.AwaitOpt, null) }), new TreeDumperNode("patternDisposeInfoOpt", node.PatternDisposeInfoOpt, null), diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs index 05a5678e72fb8..6197b910404b9 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs @@ -52,7 +52,6 @@ public override BoundNode VisitUsingStatement(BoundUsingStatement node) tryBlock, node.Locals, node.DeclarationsOpt.LocalDeclarations, - node.IDisposableConversion, node.PatternDisposeInfoOpt, node.AwaitOpt, awaitKeyword); @@ -63,7 +62,6 @@ private BoundStatement MakeDeclarationUsingStatement(SyntaxNode syntax, BoundBlock body, ImmutableArray locals, ImmutableArray declarations, - Conversion iDisposableConversion, MethodArgumentInfo? patternDisposeInfo, BoundAwaitableInfo? awaitOpt, SyntaxToken awaitKeyword) @@ -73,7 +71,7 @@ private BoundStatement MakeDeclarationUsingStatement(SyntaxNode syntax, BoundBlock result = body; for (int i = declarations.Length - 1; i >= 0; i--) //NB: inner-to-outer = right-to-left { - result = RewriteDeclarationUsingStatement(syntax, declarations[i], result, iDisposableConversion, awaitKeyword, awaitOpt, patternDisposeInfo); + result = RewriteDeclarationUsingStatement(syntax, declarations[i], result, awaitKeyword, awaitOpt, patternDisposeInfo); } // Declare all locals in a single, top-level block so that the scope is correct in the debugger @@ -96,7 +94,6 @@ private BoundStatement MakeLocalUsingDeclarationStatement(BoundUsingLocalDeclara body, ImmutableArray.Empty, usingDeclarations.LocalDeclarations, - usingDeclarations.IDisposableConversion, usingDeclarations.PatternDisposeInfoOpt, awaitOpt: usingDeclarations.AwaitOpt, awaitKeyword: syntax.AwaitKeyword); @@ -155,7 +152,8 @@ private BoundBlock MakeExpressionUsingStatement(BoundUsingStatement node, BoundB BoundAssignmentOperator tempAssignment; BoundLocal boundTemp; - if (expressionType is null || expressionType.IsDynamic()) + + if (expressionType.IsDynamic()) { // IDisposable temp = (IDisposable) expr; // or @@ -169,7 +167,7 @@ private BoundBlock MakeExpressionUsingStatement(BoundUsingStatement node, BoundB BoundExpression tempInit = MakeConversionNode( expressionSyntax, rewrittenExpression, - Conversion.GetTrivialConversion(node.IDisposableConversion.Kind), + Conversion.ImplicitDynamic, iDisposableType, @checked: false, constantValueOpt: rewrittenExpression.ConstantValue); @@ -208,7 +206,6 @@ private BoundBlock RewriteDeclarationUsingStatement( SyntaxNode usingSyntax, BoundLocalDeclaration localDeclaration, BoundBlock tryBlock, - Conversion iDisposableConversion, SyntaxToken awaitKeywordOpt, BoundAwaitableInfo? awaitOpt, MethodArgumentInfo? patternDisposeInfo) @@ -246,7 +243,7 @@ private BoundBlock RewriteDeclarationUsingStatement( BoundExpression tempInit = MakeConversionNode( declarationSyntax, boundLocal, - iDisposableConversion, + Conversion.ImplicitDynamic, iDisposableType, @checked: false); diff --git a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs index 749d927d36d7a..ed32c02b22286 100644 --- a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs @@ -197,24 +197,7 @@ public override BoundNode VisitUsingStatement(BoundUsingStatement node) var declarations = (BoundMultipleLocalDeclarations?)this.Visit(node.DeclarationsOpt); var expression = (BoundExpression?)this.Visit(node.ExpressionOpt); var body = (BoundStatement)this.Visit(node.Body); - Conversion disposableConversion = RewriteConversion(node.IDisposableConversion); - return node.Update(newLocals, declarations, expression, disposableConversion, body, node.AwaitOpt, node.PatternDisposeInfoOpt); - } - - private Conversion RewriteConversion(Conversion conversion) - { - switch (conversion.Kind) - { - case ConversionKind.ExplicitUserDefined: - case ConversionKind.ImplicitUserDefined: - Debug.Assert(conversion.ConstrainedToTypeOpt is null); - Debug.Assert(conversion.Method is not null); - return new Conversion(conversion.Kind, VisitMethodSymbol(conversion.Method), conversion.IsExtensionMethod); - case ConversionKind.MethodGroup: - throw ExceptionUtilities.UnexpectedValue(conversion.Kind); - default: - return conversion; - } + return node.Update(newLocals, declarations, expression, body, node.AwaitOpt, node.PatternDisposeInfoOpt); } [return: NotNullIfNotNull("type")]