Пример #1
0
        private BoundLambda ReallyBind(NamedTypeSymbol delegateType)
        {
            var returnType = DelegateReturnType(delegateType);

            LambdaSymbol lambdaSymbol;
            Binder       lambdaBodyBinder;
            BoundBlock   block;

            var diagnostics = DiagnosticBag.GetInstance();

            // when binding for real (not for return inference), there is still
            // a good chance that we could reuse a body of a lambda previously bound for
            // return type inference.
            MethodSymbol cacheKey = GetCacheKey(delegateType);

            BoundLambda returnInferenceLambda;

            if (returnInferenceCache.TryGetValue(cacheKey, out returnInferenceLambda) && returnInferenceLambda.InferredFromSingleType)
            {
                var lambdaSym     = returnInferenceLambda.Symbol;
                var lambdaRetType = lambdaSym.ReturnType;
                if (lambdaRetType == returnType)
                {
                    lambdaSymbol     = lambdaSym;
                    lambdaBodyBinder = returnInferenceLambda.Binder;
                    block            = returnInferenceLambda.Body;
                    diagnostics.AddRange(returnInferenceLambda.Diagnostics);

                    goto haveLambdaBodyAndBinders;
                }
            }

            var parameters = DelegateParameters(delegateType);

            lambdaSymbol     = new LambdaSymbol(binder.Compilation, binder.ContainingMemberOrLambda, this.unboundLambda, parameters, returnType);
            lambdaBodyBinder = new ExecutableCodeBinder(this.unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, binder));
            block            = BindLambdaBody(lambdaSymbol, ref lambdaBodyBinder, diagnostics);
            ValidateUnsafeParameters(diagnostics, parameters);

haveLambdaBodyAndBinders:

            bool reachableEndpoint = ControlFlowPass.Analyze(binder.Compilation, lambdaSymbol, block, diagnostics);

            if (reachableEndpoint)
            {
                if (DelegateNeedsReturn(delegateType))
                {
                    // Not all code paths return a value in {0} of type '{1}'
                    diagnostics.Add(ErrorCode.ERR_AnonymousReturnExpected, lambdaSymbol.Locations[0], this.MessageID.Localize(), delegateType);
                }
                else
                {
                    block = FlowAnalysisPass.AppendImplicitReturn(block, lambdaSymbol, this.unboundLambda.Syntax);
                }
            }

            if (IsAsync && !ErrorFacts.PreventsSuccessfulDelegateConversion(diagnostics))
            {
                if ((object)returnType != null && // Can be null if "delegateType" is not actually a delegate type.
                    returnType.SpecialType != SpecialType.System_Void &&
                    returnType != binder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task) &&
                    returnType.OriginalDefinition != binder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T))
                {
                    // Cannot convert async {0} to delegate type '{1}'. An async {0} may return void, Task or Task<T>, none of which are convertible to '{1}'.
                    diagnostics.Add(ErrorCode.ERR_CantConvAsyncAnonFuncReturns, lambdaSymbol.Locations[0], lambdaSymbol.MessageID.Localize(), delegateType);
                }
            }

            if (IsAsync)
            {
                Debug.Assert(lambdaSymbol.IsAsync);
                SourceMemberMethodSymbol.ReportAsyncParameterErrors(lambdaSymbol, diagnostics, lambdaSymbol.Locations[0]);
            }

            var result = new BoundLambda(this.unboundLambda.Syntax, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType)
            {
                WasCompilerGenerated = this.unboundLambda.WasCompilerGenerated
            };

            return(result);
        }
Пример #2
0
        /// <summary>
        /// Introduce a frame around the translation of the given node.
        /// </summary>
        /// <param name="node">The node whose translation should be translated to contain a frame</param>
        /// <param name="frame">The frame for the translated node</param>
        /// <param name="F">A function that computes the translation of the node.  It receives lists of added statements and added symbols</param>
        /// <returns>The translated statement, as returned from F</returns>
        private T IntroduceFrame <T>(BoundNode node, LambdaFrame frame, Func <ArrayBuilder <BoundExpression>, ArrayBuilder <LocalSymbol>, T> F)
        {
            NamedTypeSymbol frameType    = frame.ConstructIfGeneric(StaticCast <TypeSymbol> .From(currentTypeParameters));
            LocalSymbol     framePointer = new LambdaFrameLocalSymbol(this.topLevelMethod, frameType, CompilationState);

            CSharpSyntaxNode syntax = node.Syntax;

            // assign new frame to the frame variable
            CompilationState.AddSynthesizedMethod(frame.Constructor, FlowAnalysisPass.AppendImplicitReturn(MethodCompiler.BindMethodBody(frame.Constructor, CompilationState, null), frame.Constructor));

            var prologue = ArrayBuilder <BoundExpression> .GetInstance();

            MethodSymbol constructor = frame.Constructor.AsMember(frameType);

            Debug.Assert(frameType == constructor.ContainingType);
            var newFrame = new BoundObjectCreationExpression(
                syntax: syntax,
                constructor: constructor);

            prologue.Add(new BoundAssignmentOperator(syntax,
                                                     new BoundLocal(syntax, framePointer, null, frameType),
                                                     newFrame,
                                                     frameType));

            CapturedSymbolReplacement oldInnermostFrameProxy = null;

            if ((object)innermostFramePointer != null)
            {
                proxies.TryGetValue(innermostFramePointer, out oldInnermostFrameProxy);
                if (analysis.needsParentFrame.Contains(node))
                {
                    var             capturedFrame = new LambdaCapturedVariable(frame, innermostFramePointer);
                    FieldSymbol     frameParent   = capturedFrame.AsMember(frameType);
                    BoundExpression left          = new BoundFieldAccess(syntax, new BoundLocal(syntax, framePointer, null, frameType), frameParent, null);
                    BoundExpression right         = FrameOfType(syntax, frameParent.Type as NamedTypeSymbol);
                    BoundExpression assignment    = new BoundAssignmentOperator(syntax, left, right, left.Type);

                    if (this.currentMethod.MethodKind == MethodKind.Constructor && capturedFrame.Type == this.currentMethod.ContainingType && !this.seenBaseCall)
                    {
                        // Containing method is a constructor
                        // Initialization statement for the "this" proxy must be inserted
                        // after the constructor initializer statement block
                        // This insertion will be done by the delegate F
                        Debug.Assert(thisProxyInitDeferred == null);
                        thisProxyInitDeferred = assignment;
                    }
                    else
                    {
                        prologue.Add(assignment);
                    }

                    if (CompilationState.Emitting)
                    {
                        CompilationState.ModuleBuilderOpt.AddSynthesizedDefinition(frame, capturedFrame);
                    }

                    proxies[innermostFramePointer] = new CapturedToFrameSymbolReplacement(capturedFrame);
                }
            }

            // Capture any parameters of this block.  This would typically occur
            // at the top level of a method or lambda with captured parameters.
            // TODO: speed up the following by computing it in analysis.
            foreach (var v in analysis.variablesCaptured)
            {
                BoundNode varNode;
                if (!analysis.variableBlock.TryGetValue(v, out varNode) ||
                    varNode != node ||
                    analysis.declaredInsideExpressionLambda.Contains(v))
                {
                    continue;
                }

                InitVariableProxy(syntax, v, framePointer, prologue);
            }

            Symbol oldInnermostFramePointer = innermostFramePointer;

            innermostFramePointer = framePointer;
            var addedLocals = ArrayBuilder <LocalSymbol> .GetInstance();

            addedLocals.Add(framePointer);
            framePointers.Add(frame, framePointer);

            var result = F(prologue, addedLocals);

            framePointers.Remove(frame);
            innermostFramePointer = oldInnermostFramePointer;

            if ((object)innermostFramePointer != null)
            {
                if (oldInnermostFrameProxy != null)
                {
                    proxies[innermostFramePointer] = oldInnermostFrameProxy;
                }
                else
                {
                    proxies.Remove(innermostFramePointer);
                }
            }

            return(result);
        }
Пример #3
0
        private BoundLambda ReallyBind(NamedTypeSymbol delegateType)
        {
            var     invokeMethod = DelegateInvokeMethod(delegateType);
            RefKind refKind;
            var     returnType = DelegateReturnType(invokeMethod, out refKind);

            LambdaSymbol lambdaSymbol;
            Binder       lambdaBodyBinder;
            BoundBlock   block;

            var diagnostics = DiagnosticBag.GetInstance();

            // when binding for real (not for return inference), there is still
            // a good chance that we could reuse a body of a lambda previously bound for
            // return type inference.
            var cacheKey = ReturnInferenceCacheKey.Create(delegateType, IsAsync);

            BoundLambda returnInferenceLambda;

            if (_returnInferenceCache.TryGetValue(cacheKey, out returnInferenceLambda) && returnInferenceLambda.InferredFromSingleType)
            {
                lambdaSymbol = returnInferenceLambda.Symbol;
                if ((object)LambdaSymbol.InferenceFailureReturnType != lambdaSymbol.ReturnType &&
                    lambdaSymbol.ReturnType == returnType && lambdaSymbol.RefKind == refKind)
                {
                    lambdaBodyBinder = returnInferenceLambda.Binder;
                    block            = returnInferenceLambda.Body;
                    diagnostics.AddRange(returnInferenceLambda.Diagnostics);

                    goto haveLambdaBodyAndBinders;
                }
            }

            lambdaSymbol = new LambdaSymbol(
                binder.Compilation,
                binder.ContainingMemberOrLambda,
                _unboundLambda,
                cacheKey.ParameterTypes,
                cacheKey.ParameterRefKinds,
                refKind,
                returnType);
            lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, binder));
            block            = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics);

            ((ExecutableCodeBinder)lambdaBodyBinder).ValidateIteratorMethods(diagnostics);
            ValidateUnsafeParameters(diagnostics, cacheKey.ParameterTypes);

haveLambdaBodyAndBinders:

            bool reachableEndpoint = ControlFlowPass.Analyze(binder.Compilation, lambdaSymbol, block, diagnostics);

            if (reachableEndpoint)
            {
                if (DelegateNeedsReturn(invokeMethod))
                {
                    // Not all code paths return a value in {0} of type '{1}'
                    diagnostics.Add(ErrorCode.ERR_AnonymousReturnExpected, lambdaSymbol.DiagnosticLocation, this.MessageID.Localize(), delegateType);
                }
                else
                {
                    block = FlowAnalysisPass.AppendImplicitReturn(block, lambdaSymbol);
                }
            }

            if (IsAsync && !ErrorFacts.PreventsSuccessfulDelegateConversion(diagnostics))
            {
                if ((object)returnType != null && // Can be null if "delegateType" is not actually a delegate type.
                    returnType.SpecialType != SpecialType.System_Void &&
                    !returnType.IsNonGenericTaskType(binder.Compilation) &&
                    !returnType.IsGenericTaskType(binder.Compilation))
                {
                    // Cannot convert async {0} to delegate type '{1}'. An async {0} may return void, Task or Task&lt;T&gt;, none of which are convertible to '{1}'.
                    diagnostics.Add(ErrorCode.ERR_CantConvAsyncAnonFuncReturns, lambdaSymbol.DiagnosticLocation, lambdaSymbol.MessageID.Localize(), delegateType);
                }
            }

            if (IsAsync)
            {
                Debug.Assert(lambdaSymbol.IsAsync);
                SourceMemberMethodSymbol.ReportAsyncParameterErrors(lambdaSymbol.Parameters, diagnostics, lambdaSymbol.DiagnosticLocation);
            }

            var result = new BoundLambda(_unboundLambda.Syntax, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType, inferReturnType: false)
            {
                WasCompilerGenerated = _unboundLambda.WasCompilerGenerated
            };

            return(result);
        }
Пример #4
0
        private BoundLambda ReallyBind(NamedTypeSymbol delegateType)
        {
            var     invokeMethod = DelegateInvokeMethod(delegateType);
            RefKind refKind;
            var     returnType = DelegateReturnType(invokeMethod, out refKind);

            LambdaSymbol lambdaSymbol;
            Binder       lambdaBodyBinder;
            BoundBlock   block;

            var diagnostics = DiagnosticBag.GetInstance();

            // when binding for real (not for return inference), there is still
            // a good chance that we could reuse a body of a lambda previously bound for
            // return type inference.
            MethodSymbol cacheKey = GetCacheKey(delegateType);

            BoundLambda returnInferenceLambda;

            if (_returnInferenceCache.TryGetValue(cacheKey, out returnInferenceLambda) && returnInferenceLambda.InferredFromSingleType && returnInferenceLambda.Symbol.ReturnType == returnType)
            {
                lambdaSymbol = returnInferenceLambda.Symbol;
                Debug.Assert(lambdaSymbol.RefKind == refKind);

                lambdaBodyBinder = returnInferenceLambda.Binder;
                block            = returnInferenceLambda.Body;
                diagnostics.AddRange(returnInferenceLambda.Diagnostics);

                goto haveLambdaBodyAndBinders;
            }

            var parameters = DelegateParameters(invokeMethod);

            lambdaSymbol = new LambdaSymbol(
                binder.Compilation,
                binder.ContainingMemberOrLambda,
                _unboundLambda,
                parameters,
                refKind,
                returnType);
            lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, binder));
            block            = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics);

            ((ExecutableCodeBinder)lambdaBodyBinder).ValidateIteratorMethods(diagnostics);
            ValidateUnsafeParameters(diagnostics, parameters);

haveLambdaBodyAndBinders:

            bool reachableEndpoint = ControlFlowPass.Analyze(binder.Compilation, lambdaSymbol, block, diagnostics);

            if (reachableEndpoint)
            {
                if (DelegateNeedsReturn(invokeMethod))
                {
                    // Not all code paths return a value in {0} of type '{1}'
                    diagnostics.Add(ErrorCode.ERR_AnonymousReturnExpected, lambdaSymbol.Locations[0], this.MessageID.Localize(), delegateType);
                }
                else
                {
                    block = FlowAnalysisPass.AppendImplicitReturn(block, lambdaSymbol);
                }
            }

            if (IsAsync && !ErrorFacts.PreventsSuccessfulDelegateConversion(diagnostics))
            {
                if ((object)returnType != null && // Can be null if "delegateType" is not actually a delegate type.
                    returnType.SpecialType != SpecialType.System_Void &&
                    !returnType.IsNonGenericTaskType(binder.Compilation) &&
                    !returnType.IsGenericTaskType(binder.Compilation))
                {
                    // Cannot convert async {0} to delegate type '{1}'. An async {0} may return void, Task or Task&lt;T&gt;, none of which are convertible to '{1}'.
                    diagnostics.Add(ErrorCode.ERR_CantConvAsyncAnonFuncReturns, lambdaSymbol.Locations[0], lambdaSymbol.MessageID.Localize(), delegateType);
                }
            }

            if (IsAsync)
            {
                Debug.Assert(lambdaSymbol.IsAsync);
                SourceMemberMethodSymbol.ReportAsyncParameterErrors(lambdaSymbol.Parameters, diagnostics, lambdaSymbol.Locations[0]);
            }

            // This is an attempt to get a repro for https://devdiv.visualstudio.com/DevDiv/_workitems?id=278481
            if ((object)returnType != null && returnType.SpecialType != SpecialType.System_Void &&
                !block.HasErrors && !diagnostics.HasAnyResolvedErrors() && block.Statements.Length > 0)
            {
                BoundStatement first = block.Statements[0];
                if (first.Kind == BoundKind.ReturnStatement)
                {
                    var returnStmt = (BoundReturnStatement)first;
                    if (returnStmt.ExpressionOpt != null && (object)returnStmt.ExpressionOpt.Type == null)
                    {
                        throw ExceptionUtilities.Unreachable;
                    }
                }
            }

            var result = new BoundLambda(_unboundLambda.Syntax, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType, inferReturnType: false)
            {
                WasCompilerGenerated = _unboundLambda.WasCompilerGenerated
            };

            return(result);
        }