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); }
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<T>, 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); }
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<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.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); }
/// <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); }
//TODO: it might be nice to make this a static method on Compiler private void CompileMethod( MethodSymbol methodSymbol, ref ProcessedFieldInitializers processedInitializers, SynthesizedSubmissionFields previousSubmissionFields, TypeCompilationState compilationState) { cancellationToken.ThrowIfCancellationRequested(); SourceMethodSymbol sourceMethod = methodSymbol as SourceMethodSymbol; if (methodSymbol.IsAbstract) { if ((object)sourceMethod != null) { bool diagsWritten; sourceMethod.SetDiagnostics(ImmutableArray <Diagnostic> .Empty, out diagsWritten); if (diagsWritten && !methodSymbol.IsImplicitlyDeclared && compilation.EventQueue != null) { compilation.SymbolDeclaredEvent(methodSymbol); } } return; } // get cached diagnostics if not building and we have 'em bool calculateDiagnosticsOnly = moduleBeingBuilt == null; if (calculateDiagnosticsOnly && ((object)sourceMethod != null)) { var cachedDiagnostics = sourceMethod.Diagnostics; if (!cachedDiagnostics.IsDefault) { this.diagnostics.AddRange(cachedDiagnostics); return; } } ConsList <Imports> oldDebugImports = compilationState.CurrentDebugImports; // In order to avoid generating code for methods with errors, we create a diagnostic bag just for this method. DiagnosticBag diagsForCurrentMethod = DiagnosticBag.GetInstance(); try { bool includeInitializersInBody; BoundBlock body; // if synthesized method returns its body in lowered form if (methodSymbol.SynthesizesLoweredBoundBody) { if (moduleBeingBuilt != null) { methodSymbol.GenerateMethodBody(compilationState, diagsForCurrentMethod); this.diagnostics.AddRange(diagsForCurrentMethod); } return; } //EDMAURER initializers that have been analyzed but not yet lowered. BoundStatementList analyzedInitializers = null; ConsList <Imports> debugImports; if (methodSymbol.IsScriptConstructor) { // rewrite top-level statements and script variable declarations to a list of statements and assignments, respectively: BoundStatementList initializerStatements = InitializerRewriter.Rewrite(processedInitializers.BoundInitializers, methodSymbol); // the lowered script initializers should not be treated as initializers anymore but as a method body: body = new BoundBlock(initializerStatements.Syntax, ImmutableArray <LocalSymbol> .Empty, initializerStatements.Statements) { WasCompilerGenerated = true }; includeInitializersInBody = false; debugImports = null; } else { // do not emit initializers if we are invoking another constructor of this class: includeInitializersInBody = !processedInitializers.BoundInitializers.IsDefaultOrEmpty && !HasThisConstructorInitializer(methodSymbol); // lower initializers just once. the lowered tree will be reused when emitting all constructors // with field initializers. Once lowered, these initializers will be stashed in processedInitializers.LoweredInitializers // (see later in this method). Don't bother lowering _now_ if this particular ctor won't have the initializers // appended to its body. if (includeInitializersInBody && processedInitializers.LoweredInitializers == null) { analyzedInitializers = InitializerRewriter.Rewrite(processedInitializers.BoundInitializers, methodSymbol); processedInitializers.HasErrors = processedInitializers.HasErrors || analyzedInitializers.HasAnyErrors; // These analyses check for diagnostics in lambdas. // Control flow analysis and implicit return insertion are unnecessary. DataFlowPass.Analyze(compilation, methodSymbol, analyzedInitializers, diagsForCurrentMethod, requireOutParamsAssigned: false); DiagnosticsPass.IssueDiagnostics(compilation, analyzedInitializers, diagsForCurrentMethod, methodSymbol); } body = Compiler.BindMethodBody(methodSymbol, diagsForCurrentMethod, this.generateDebugInfo, out debugImports); } #if DEBUG // If the method is a synthesized static or instance constructor, then debugImports will be null and we will use the value // from the first field initializer. if (this.generateDebugInfo) { if ((methodSymbol.MethodKind == MethodKind.Constructor || methodSymbol.MethodKind == MethodKind.StaticConstructor) && methodSymbol.IsImplicitlyDeclared) { // There was no body to bind, so we didn't get anything from Compiler.BindMethodBody. Debug.Assert(debugImports == null); // Either there were no field initializers or we grabbed debug imports from the first one. Debug.Assert(processedInitializers.BoundInitializers.IsDefaultOrEmpty || processedInitializers.FirstDebugImports != null); } } #endif debugImports = debugImports ?? processedInitializers.FirstDebugImports; // Associate these debug imports with all methods generated from this one. compilationState.CurrentDebugImports = debugImports; if (body != null && methodSymbol is SourceMethodSymbol) { // TODO: Do we need to issue warnings for non-SourceMethodSymbol methods, like synthesized ctors? DiagnosticsPass.IssueDiagnostics(compilation, body, diagsForCurrentMethod, methodSymbol); } BoundBlock flowAnalyzedBody = null; if (body != null) { flowAnalyzedBody = FlowAnalysisPass.Rewrite(methodSymbol, body, diagsForCurrentMethod); } bool hasErrors = hasDeclarationErrors || diagsForCurrentMethod.HasAnyErrors() || processedInitializers.HasErrors; // Record whether or not the bound tree for the lowered method body (including any initializers) contained any // errors (note: errors, not diagnostics). SetGlobalErrorIfTrue(hasErrors); bool diagsWritten = false; var actualDiagnostics = diagsForCurrentMethod.ToReadOnly(); if (sourceMethod != null) { actualDiagnostics = sourceMethod.SetDiagnostics(actualDiagnostics, out diagsWritten); } if (diagsWritten && !methodSymbol.IsImplicitlyDeclared && compilation.EventQueue != null) { var lazySemanticModel = body == null ? null : new Lazy <SemanticModel>(() => { var syntax = body.Syntax; var semanticModel = (CSharpSemanticModel)compilation.GetSemanticModel(syntax.SyntaxTree); var memberModel = semanticModel.GetMemberModel(syntax); if (memberModel != null) { memberModel.AddBoundTreeForStandaloneSyntax(syntax, body); } return(semanticModel); }); compilation.EventQueue.Enqueue(new CompilationEvent.SymbolDeclared(compilation, methodSymbol, lazySemanticModel)); } // Don't lower if we're not emitting or if there were errors. // Methods that had binding errors are considered too broken to be lowered reliably. if (calculateDiagnosticsOnly || hasErrors) { this.diagnostics.AddRange(actualDiagnostics); return; } // ############################ // LOWERING AND EMIT // Any errors generated below here are considered Emit diagnostics // and will not be reported to callers Compilation.GetDiagnostics() BoundStatement loweredBody = (flowAnalyzedBody == null) ? null : Compiler.LowerStatement(this.generateDebugInfo, methodSymbol, flowAnalyzedBody, previousSubmissionFields, compilationState, diagsForCurrentMethod); bool hasBody = loweredBody != null; hasErrors = hasErrors || (hasBody && loweredBody.HasErrors) || diagsForCurrentMethod.HasAnyErrors(); SetGlobalErrorIfTrue(hasErrors); // don't emit if the resulting method would contain initializers with errors if (!hasErrors && (hasBody || includeInitializersInBody)) { // Fields must be initialized before constructor initializer (which is the first statement of the analyzed body, if specified), // so that the initialization occurs before any method overridden by the declaring class can be invoked from the base constructor // and access the fields. ImmutableArray <BoundStatement> boundStatements; if (methodSymbol.IsScriptConstructor) { boundStatements = MethodBodySynthesizer.ConstructScriptConstructorBody(loweredBody, methodSymbol, previousSubmissionFields, compilation); } else { boundStatements = ImmutableArray <BoundStatement> .Empty; if (analyzedInitializers != null) { processedInitializers.LoweredInitializers = (BoundStatementList)Compiler.LowerStatement( this.generateDebugInfo, methodSymbol, analyzedInitializers, previousSubmissionFields, compilationState, diagsForCurrentMethod); Debug.Assert(!hasErrors); hasErrors = processedInitializers.LoweredInitializers.HasAnyErrors || diagsForCurrentMethod.HasAnyErrors(); SetGlobalErrorIfTrue(hasErrors); if (hasErrors) { this.diagnostics.AddRange(diagsForCurrentMethod); return; } } // initializers for global code have already been included in the body if (includeInitializersInBody) { //TODO: rewrite any BoundThis and BoundBase nodes in the initializers to have the correct ThisParameter symbol if (compilation.Options.Optimize) { // TODO: this part may conflict with InitializerRewriter.Rewrite in how it handles // the first field initializer (see 'if (i == 0)'...) which seems suspicious ArrayBuilder <BoundStatement> statements = ArrayBuilder <BoundStatement> .GetInstance(); statements.AddRange(boundStatements); bool anyNonDefault = false; foreach (var initializer in processedInitializers.LoweredInitializers.Statements) { if (ShouldOptimizeOutInitializer(initializer)) { if (methodSymbol.IsStatic) { // NOTE: Dev11 removes static initializers if ONLY all of them are optimized out statements.Add(initializer); } } else { statements.Add(initializer); anyNonDefault = true; } } if (anyNonDefault) { boundStatements = statements.ToImmutableAndFree(); } else { statements.Free(); } } else { boundStatements = boundStatements.Concat(processedInitializers.LoweredInitializers.Statements); } } if (hasBody) { boundStatements = boundStatements.Concat(ImmutableArray.Create(loweredBody)); } } CSharpSyntaxNode syntax = methodSymbol.GetNonNullSyntaxNode(); var boundBody = BoundStatementList.Synthesized(syntax, boundStatements); var emittedBody = Compiler.GenerateMethodBody( compilationState, methodSymbol, boundBody, diagsForCurrentMethod, optimize, debugDocumentProvider, GetNamespaceScopes(methodSymbol, debugImports)); moduleBeingBuilt.SetMethodBody(methodSymbol, emittedBody); } this.diagnostics.AddRange(diagsForCurrentMethod); } finally { diagsForCurrentMethod.Free(); compilationState.CurrentDebugImports = oldDebugImports; } }