Beispiel #1
0
        internal BoundAwaitableInfo BindAwaitInfo(
            BoundAwaitableValuePlaceholder placeholder,
            SyntaxNode node,
            BindingDiagnosticBag diagnostics,
            ref bool hasErrors,
            BoundExpression?expressionOpt = null
            )
        {
            bool hasGetAwaitableErrors = !GetAwaitableExpressionInfo(
                expressionOpt ?? placeholder,
                placeholder,
                out bool isDynamic,
                out BoundExpression? getAwaiter,
                out PropertySymbol? isCompleted,
                out MethodSymbol? getResult,
                getAwaiterGetResultCall: out _,
                node,
                diagnostics
                );

            hasErrors |= hasGetAwaitableErrors;

            return(new BoundAwaitableInfo(
                       node,
                       placeholder,
                       isDynamic: isDynamic,
                       getAwaiter,
                       isCompleted,
                       getResult,
                       hasErrors: hasGetAwaitableErrors
                       )
            {
                WasCompilerGenerated = true
            });
        }
Beispiel #2
0
        private BoundAwaitExpression BindAwait(
            BoundExpression expression,
            SyntaxNode node,
            BindingDiagnosticBag diagnostics
            )
        {
            bool hasErrors   = false;
            var  placeholder = new BoundAwaitableValuePlaceholder(
                expression.Syntax,
                GetValEscape(expression, LocalScopeDepth),
                expression.Type
                );

            ReportBadAwaitDiagnostics(node, node.Location, diagnostics, ref hasErrors);
            var info = BindAwaitInfo(
                placeholder,
                node,
                diagnostics,
                ref hasErrors,
                expressionOpt: expression
                );

            // Spec 7.7.7.2:
            // The expression await t is classified the same way as the expression (t).GetAwaiter().GetResult(). Thus,
            // if the return type of GetResult is void, the await-expression is classified as nothing. If it has a
            // non-void return type T, the await-expression is classified as a value of type T.
            TypeSymbol awaitExpressionType =
                info.GetResult?.ReturnType
                ?? (hasErrors ? CreateErrorType() : Compilation.DynamicType);

            return(new BoundAwaitExpression(node, expression, info, awaitExpressionType, hasErrors));
        }
Beispiel #3
0
        internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNode syntax, SyntaxToken usingKeyword, SyntaxToken awaitKeyword, Binder originalBinder, UsingStatementBinder usingBinderOpt, DiagnosticBag diagnostics)
        {
            bool isUsingDeclaration = syntax.Kind() == SyntaxKind.LocalDeclarationStatement;
            bool isExpression       = !isUsingDeclaration && syntax.Kind() != SyntaxKind.VariableDeclaration;
            bool hasAwait           = awaitKeyword != default;

            Debug.Assert(isUsingDeclaration || usingBinderOpt != null);

            TypeSymbol disposableInterface = getDisposableInterface(hasAwait);

            Debug.Assert((object)disposableInterface != null);
            bool hasErrors = ReportUseSiteDiagnostics(disposableInterface, diagnostics, hasAwait ? awaitKeyword : usingKeyword);

            Conversion iDisposableConversion = Conversion.NoConversion;
            ImmutableArray <BoundLocalDeclaration> declarationsOpt         = default;
            BoundMultipleLocalDeclarations         multipleDeclarationsOpt = null;
            BoundExpression expressionOpt      = null;
            AwaitableInfo   awaitOpt           = null;
            TypeSymbol      declarationTypeOpt = null;
            MethodSymbol    disposeMethodOpt   = null;
            TypeSymbol      awaitableTypeOpt   = null;

            if (isExpression)
            {
                expressionOpt = usingBinderOpt.BindTargetExpression(diagnostics, originalBinder);
                hasErrors    |= !populateDisposableConversionOrDisposeMethod(fromExpression: true);
            }
            else
            {
                VariableDeclarationSyntax declarationSyntax = isUsingDeclaration ? ((LocalDeclarationStatementSyntax)syntax).Declaration : (VariableDeclarationSyntax)syntax;
                originalBinder.BindForOrUsingOrFixedDeclarations(declarationSyntax, LocalDeclarationKind.UsingVariable, diagnostics, out declarationsOpt);

                Debug.Assert(!declarationsOpt.IsEmpty);
                multipleDeclarationsOpt = new BoundMultipleLocalDeclarations(declarationSyntax, declarationsOpt);
                declarationTypeOpt      = declarationsOpt[0].DeclaredType.Type;

                if (declarationTypeOpt.IsDynamic())
                {
                    iDisposableConversion = Conversion.ImplicitDynamic;
                }
                else
                {
                    hasErrors |= !populateDisposableConversionOrDisposeMethod(fromExpression: false);
                }
            }

            if (hasAwait)
            {
                BoundAwaitableValuePlaceholder placeholderOpt;
                if (awaitableTypeOpt is null)
                {
                    placeholderOpt = null;
                }
                else
                {
                    hasErrors     |= ReportUseSiteDiagnostics(awaitableTypeOpt, diagnostics, awaitKeyword);
                    placeholderOpt = new BoundAwaitableValuePlaceholder(syntax, awaitableTypeOpt).MakeCompilerGenerated();
                }

                // even if we don't have a proper value to await, we'll still report bad usages of `await`
                awaitOpt = originalBinder.BindAwaitInfo(placeholderOpt, syntax, awaitKeyword.GetLocation(), diagnostics, ref hasErrors);
            }

            // This is not awesome, but its factored.
            // In the future it might be better to have a seperate 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, disposeMethodOpt, iDisposableConversion, awaitOpt, declarationsOpt, hasErrors));
            }
            else
            {
                BoundStatement boundBody = originalBinder.BindPossibleEmbeddedStatement(usingBinderOpt._syntax.Statement, diagnostics);

                return(new BoundUsingStatement(
                           usingBinderOpt._syntax,
                           usingBinderOpt.Locals,
                           multipleDeclarationsOpt,
                           expressionOpt,
                           iDisposableConversion,
                           boundBody,
                           awaitOpt,
                           disposeMethodOpt,
                           hasErrors));
            }

            // initializes iDisposableConversion, awaitableTypeOpt and disposeMethodOpt
            bool populateDisposableConversionOrDisposeMethod(bool fromExpression)
            {
                HashSet <DiagnosticInfo> useSiteDiagnostics = null;

                iDisposableConversion = classifyConversion(fromExpression, disposableInterface, ref useSiteDiagnostics);

                diagnostics.Add(syntax, useSiteDiagnostics);

                if (iDisposableConversion.IsImplicit)
                {
                    if (hasAwait)
                    {
                        awaitableTypeOpt = originalBinder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_ValueTask);
                    }
                    return(true);
                }

                TypeSymbol type = fromExpression ? expressionOpt.Type : declarationTypeOpt;

                // If this is a ref struct, or we're in a valid asynchronous using, try binding via pattern.
                // We won't need to try and bind a second time if it fails, as async dispose can't be pattern based (ref structs are not allowed in async methods)
                if (!(type is null) && (type.IsRefLikeType || hasAwait))
                {
                    BoundExpression receiver = fromExpression
                                               ? expressionOpt
                                               : new BoundLocal(syntax, declarationsOpt[0].LocalSymbol, null, type)
                    {
                        WasCompilerGenerated = true
                    };

                    disposeMethodOpt = originalBinder.TryFindDisposePatternMethod(receiver, syntax, hasAwait, diagnostics);
                    if (!(disposeMethodOpt is null))
                    {
                        if (hasAwait)
                        {
                            awaitableTypeOpt = disposeMethodOpt.ReturnType;
                        }
                        return(true);
                    }
                }

                if (type is null || !type.IsErrorType())
                {
                    // Retry with a different assumption about whether the `using` is async
                    TypeSymbol alternateInterface    = getDisposableInterface(!hasAwait);
                    HashSet <DiagnosticInfo> ignored = null;
                    Conversion alternateConversion   = classifyConversion(fromExpression, alternateInterface, ref ignored);

                    bool      wrongAsync = alternateConversion.IsImplicit;
                    ErrorCode errorCode  = wrongAsync
                        ? (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDispWrongAsync : ErrorCode.ERR_NoConvToIDispWrongAsync)
                        : (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDisp : ErrorCode.ERR_NoConvToIDisp);

                    Error(diagnostics, errorCode, syntax, declarationTypeOpt ?? expressionOpt.Display);
                }

                return(false);
            }

            Conversion classifyConversion(bool fromExpression, TypeSymbol targetInterface, ref HashSet <DiagnosticInfo> diag)
            {
                return(fromExpression ?
                       originalBinder.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt, targetInterface, ref diag) :
                       originalBinder.Conversions.ClassifyImplicitConversionFromType(declarationTypeOpt, targetInterface, ref diag));
            }

            TypeSymbol getDisposableInterface(bool isAsync)
            {
                return(isAsync
                    ? originalBinder.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable)
                    : originalBinder.Compilation.GetSpecialType(SpecialType.System_IDisposable));
            }
        }
        internal override BoundStatement BindUsingStatementParts(DiagnosticBag diagnostics, Binder originalBinder)
        {
            ExpressionSyntax          expressionSyntax  = TargetExpressionSyntax;
            VariableDeclarationSyntax declarationSyntax = _syntax.Declaration;
            bool hasAwait = _syntax.AwaitKeyword.Kind() != default;

            Debug.Assert((expressionSyntax == null) ^ (declarationSyntax == null)); // Can't have both or neither.

            TypeSymbol iDisposable = hasAwait
                ? this.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable)
                : this.Compilation.GetSpecialType(SpecialType.System_IDisposable);

            Debug.Assert((object)iDisposable != null);
            bool hasErrors = ReportUseSiteDiagnostics(iDisposable, diagnostics, hasAwait ? _syntax.AwaitKeyword : _syntax.UsingKeyword);

            Conversion iDisposableConversion = Conversion.NoConversion;
            BoundMultipleLocalDeclarations declarationsOpt = null;
            BoundExpression expressionOpt = null;
            AwaitableInfo   awaitOpt      = null;

            if (expressionSyntax != null)
            {
                expressionOpt = this.BindTargetExpression(diagnostics, originalBinder);

                HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                iDisposableConversion = originalBinder.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt, iDisposable, ref useSiteDiagnostics);
                diagnostics.Add(expressionSyntax, useSiteDiagnostics);

                if (!iDisposableConversion.IsImplicit)
                {
                    TypeSymbol expressionType = expressionOpt.Type;
                    if ((object)expressionType == null || !expressionType.IsErrorType())
                    {
                        Error(diagnostics, hasAwait ? ErrorCode.ERR_NoConvToIAsyncDisp : ErrorCode.ERR_NoConvToIDisp, expressionSyntax, expressionOpt.Display);
                    }
                    hasErrors = true;
                }
            }
            else
            {
                ImmutableArray <BoundLocalDeclaration> declarations;
                originalBinder.BindForOrUsingOrFixedDeclarations(declarationSyntax, LocalDeclarationKind.UsingVariable, diagnostics, out declarations);

                Debug.Assert(!declarations.IsEmpty);

                declarationsOpt = new BoundMultipleLocalDeclarations(declarationSyntax, declarations);

                TypeSymbol declType = declarations[0].DeclaredType.Type;

                if (declType.IsDynamic())
                {
                    iDisposableConversion = Conversion.ImplicitDynamic;
                }
                else
                {
                    HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                    iDisposableConversion = originalBinder.Conversions.ClassifyImplicitConversionFromType(declType, iDisposable, ref useSiteDiagnostics);
                    diagnostics.Add(declarationSyntax, useSiteDiagnostics);

                    if (!iDisposableConversion.IsImplicit)
                    {
                        if (!declType.IsErrorType())
                        {
                            Error(diagnostics, hasAwait ? ErrorCode.ERR_NoConvToIAsyncDisp : ErrorCode.ERR_NoConvToIDisp, declarationSyntax, declType);
                        }

                        hasErrors = true;
                    }
                }
            }

            if (hasAwait)
            {
                TypeSymbol taskType = this.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_ValueTask);
                hasErrors |= ReportUseSiteDiagnostics(taskType, diagnostics, _syntax.AwaitKeyword);

                var             resource    = (SyntaxNode)expressionSyntax ?? declarationSyntax;
                BoundExpression placeholder = new BoundAwaitableValuePlaceholder(resource, taskType).MakeCompilerGenerated();
                awaitOpt = BindAwaitInfo(placeholder, resource, _syntax.AwaitKeyword.GetLocation(), diagnostics, ref hasErrors);
            }

            BoundStatement boundBody = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics);

            Debug.Assert(GetDeclaredLocalsForScope(_syntax) == this.Locals);
            return(new BoundUsingStatement(
                       _syntax,
                       this.Locals,
                       declarationsOpt,
                       expressionOpt,
                       iDisposableConversion,
                       boundBody,
                       awaitOpt,
                       hasErrors));
        }
 public override BoundNode VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node)
 {
     return(_placeholderMap[node]);
 }
Beispiel #6
0
 public override BoundNode VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node)
 {
     Fail(node);
     return(null);
 }
Beispiel #7
0
        internal override BoundStatement BindUsingStatementParts(DiagnosticBag diagnostics, Binder originalBinder)
        {
            ExpressionSyntax          expressionSyntax  = TargetExpressionSyntax;
            VariableDeclarationSyntax declarationSyntax = _syntax.Declaration;
            bool hasAwait = _syntax.AwaitKeyword.Kind() != default;

            Debug.Assert((expressionSyntax == null) ^ (declarationSyntax == null)); // Can't have both or neither.
            TypeSymbol disposableInterface = getDisposableInterface(hasAwait);

            Debug.Assert((object)disposableInterface != null);
            bool hasErrors = ReportUseSiteDiagnostics(disposableInterface, diagnostics, hasAwait ? _syntax.AwaitKeyword : _syntax.UsingKeyword);

            Conversion iDisposableConversion = Conversion.NoConversion;
            BoundMultipleLocalDeclarations declarationsOpt = null;
            BoundExpression expressionOpt      = null;
            AwaitableInfo   awaitOpt           = null;
            TypeSymbol      declarationTypeOpt = null;

            if (expressionSyntax != null)
            {
                expressionOpt = this.BindTargetExpression(diagnostics, originalBinder);
                hasErrors    |= !initConversion(fromExpression: true);
            }
            else
            {
                ImmutableArray <BoundLocalDeclaration> declarations;
                originalBinder.BindForOrUsingOrFixedDeclarations(declarationSyntax, LocalDeclarationKind.UsingVariable, diagnostics, out declarations);

                Debug.Assert(!declarations.IsEmpty);
                declarationsOpt    = new BoundMultipleLocalDeclarations(declarationSyntax, declarations);
                declarationTypeOpt = declarations[0].DeclaredType.Type;

                if (declarationTypeOpt.IsDynamic())
                {
                    iDisposableConversion = Conversion.ImplicitDynamic;
                }
                else
                {
                    hasErrors |= !initConversion(fromExpression: false);
                }
            }

            if (hasAwait)
            {
                TypeSymbol taskType = this.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_ValueTask);
                hasErrors |= ReportUseSiteDiagnostics(taskType, diagnostics, _syntax.AwaitKeyword);

                var             resource    = (SyntaxNode)expressionSyntax ?? declarationSyntax;
                BoundExpression placeholder = new BoundAwaitableValuePlaceholder(resource, taskType).MakeCompilerGenerated();
                awaitOpt = BindAwaitInfo(placeholder, resource, _syntax.AwaitKeyword.GetLocation(), diagnostics, ref hasErrors);
            }

            BoundStatement boundBody = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics);

            Debug.Assert(GetDeclaredLocalsForScope(_syntax) == this.Locals);
            return(new BoundUsingStatement(
                       _syntax,
                       this.Locals,
                       declarationsOpt,
                       expressionOpt,
                       iDisposableConversion,
                       boundBody,
                       awaitOpt,
                       hasErrors));

            bool initConversion(bool fromExpression)
            {
                HashSet <DiagnosticInfo> useSiteDiagnostics = null;

                iDisposableConversion = classifyConversion(fromExpression, disposableInterface, ref useSiteDiagnostics);

                diagnostics.Add(fromExpression ? (CSharpSyntaxNode)expressionSyntax : declarationSyntax, useSiteDiagnostics);

                if (iDisposableConversion.IsImplicit)
                {
                    return(true);
                }

                TypeSymbol type = fromExpression ? expressionOpt.Type : declarationTypeOpt;

                if (type is null || !type.IsErrorType())
                {
                    // Retry with a different assumption about whether the `using` is async
                    TypeSymbol alternateInterface    = getDisposableInterface(!hasAwait);
                    HashSet <DiagnosticInfo> ignored = null;
                    Conversion alternateConversion   = classifyConversion(fromExpression, alternateInterface, ref ignored);

                    bool      wrongAsync = alternateConversion.IsImplicit;
                    ErrorCode errorCode  = wrongAsync
                        ? (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDispWrongAsync : ErrorCode.ERR_NoConvToIDispWrongAsync)
                        : (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDisp : ErrorCode.ERR_NoConvToIDisp);

                    Error(diagnostics, errorCode, (CSharpSyntaxNode)declarationSyntax ?? expressionSyntax, declarationTypeOpt ?? expressionOpt.Display);
                }

                return(false);
            }

            Conversion classifyConversion(bool fromExpression, TypeSymbol targetInterface, ref HashSet <DiagnosticInfo> diag)
            {
                return(fromExpression ?
                       originalBinder.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt, targetInterface, ref diag) :
                       originalBinder.Conversions.ClassifyImplicitConversionFromType(declarationTypeOpt, targetInterface, ref diag));
            }

            TypeSymbol getDisposableInterface(bool isAsync)
            {
                return(isAsync
                    ? this.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable)
                    : this.Compilation.GetSpecialType(SpecialType.System_IDisposable));
            }
        }