diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index b057ab748fd7a5ae3922400e5cd5918b54368157..49e212b61544d9212c0e5368f4754c6e4e42136a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -416,25 +416,43 @@ private BoundStatement BindLocalFunctionStatement(LocalFunctionStatementSyntax n var hasErrors = false; - hasErrors |= this.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics); - - var inMethodBinder = new InMethodBinder(localSymbol, this); - var blockBinder = new ExecutableCodeBinder(node, localSymbol, inMethodBinder); - - BoundBlock block; - if (node.Body != null) + // In error scenarios with misplaced code, it is possible we can't bind the local declaration. + // This occurs through the semantic model. In that case concoct a plausible result. + if (localSymbol == null) { - block = blockBinder.BindBlock(node.Body, diagnostics); + localSymbol = new LocalFunctionMethodSymbol(this, this.ContainingType, node, node.Identifier.GetLocation()); } - else if (node.ExpressionBody != null) + else + { + hasErrors |= this.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics); + } + + var binder = this.GetBinder(node); + + // Binder could be null in error scenarios (as above) + BoundBlock block; + if (binder != null) { - block = blockBinder.BindExpressionBodyAsBlock(node.ExpressionBody, diagnostics); + if (node.Body != null) + { + block = binder.BindBlock(node.Body, diagnostics); + } + else if (node.ExpressionBody != null) + { + block = binder.BindExpressionBodyAsBlock(node.ExpressionBody, diagnostics); + } + else + { + block = null; + hasErrors = true; + // TODO: add a message for this? + diagnostics.Add(ErrorCode.ERR_ConcreteMissingBody, node.Location, localSymbol); + } } else { block = null; - // TODO: add a message for this? - diagnostics.Add(ErrorCode.ERR_ConcreteMissingBody, node.Location); + hasErrors = true; } return new BoundLocalFunctionStatement(node, localSymbol, block, hasErrors); diff --git a/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs b/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs index 54aa7540b74d87f89f7561002c0568159961c4e5..d8e1d112a37587e5ee42a2d34541cf12b02f8559 100644 --- a/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs @@ -2,6 +2,8 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; +using System.Diagnostics; +using System.Collections.Immutable; namespace Microsoft.CodeAnalysis.CSharp { @@ -18,6 +20,7 @@ internal sealed class ExecutableCodeBinder : Binder private readonly CSharpSyntaxNode _root; private readonly MethodSymbol _owner; private SmallDictionary _lazyBinderMap; + private ImmutableArray _methodSymbolsWithYield; internal ExecutableCodeBinder(CSharpSyntaxNode root, Symbol memberSymbol, Binder next) : this(root, memberSymbol, next, next.Flags) @@ -45,43 +48,69 @@ internal override Binder GetBinder(CSharpSyntaxNode node) return this.BinderMap.TryGetValue(node, out binder) ? binder : Next.GetBinder(node); } - private SmallDictionary BinderMap + private void ComputeBinderMap() { - get + SmallDictionary map; + ImmutableArray methodSymbolsWithYield; + var methodSymbol = _owner; + + // Ensure that the member symbol is a method symbol. + if ((object)methodSymbol != null && _root != null) { - if (_lazyBinderMap == null) + var methodsWithYield = ArrayBuilder.GetInstance(); + var symbolsWithYield = ArrayBuilder.GetInstance(); + map = LocalBinderFactory.BuildMap(methodSymbol, _root, this, methodsWithYield); + foreach (var methodWithYield in methodsWithYield) { - SmallDictionary map; - var methodSymbol = _owner; - - // Ensure that the member symbol is a method symbol. - if ((object)methodSymbol != null && _root != null) + Binder binder; + InMethodBinder inMethod; + if (map.TryGetValue(methodWithYield, out binder) && (inMethod = binder as InMethodBinder) != null) { - bool sawYield; - map = LocalBinderFactory.BuildMap(methodSymbol, _root, this, out sawYield); - if (sawYield && ((MethodSymbol)this.ContainingMemberOrLambda).MethodKind != MethodKind.AnonymousFunction) - { - for (Binder b = this; b != null; b = b.Next) - { - var inMethod = b as InMethodBinder; - if (inMethod != null) - { - inMethod.MakeIterator(); - break; - } - } - } + inMethod.MakeIterator(); + symbolsWithYield.Add((MethodSymbol)inMethod.ContainingMemberOrLambda); } else { - map = SmallDictionary.Empty; + Debug.Assert(false); } + } + methodsWithYield.Free(); + methodSymbolsWithYield = symbolsWithYield.ToImmutableAndFree(); + } + else + { + map = SmallDictionary.Empty; + methodSymbolsWithYield = ImmutableArray.Empty; + } + + Interlocked.CompareExchange(ref _lazyBinderMap, map, null); + _methodSymbolsWithYield = methodSymbolsWithYield; + } - Interlocked.CompareExchange(ref _lazyBinderMap, map, null); + private SmallDictionary BinderMap + { + get + { + if (_lazyBinderMap == null) + { + ComputeBinderMap(); } return _lazyBinderMap; } } + + public ImmutableArray MethodSymbolsWithYield + { + get + { + if (_methodSymbolsWithYield.IsDefault) + { + ComputeBinderMap(); + } + + return _methodSymbolsWithYield; + } + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs index 9f9cc81bc26784007d69b87b342c4e990847b03a..1a9b3d56cecfed42fe2c32e7f5eeddc17cd636e0 100644 --- a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs +++ b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs @@ -25,7 +25,8 @@ internal sealed class LocalBinderFactory : CSharpSyntaxVisitor { private readonly SmallDictionary _map; private bool _sawYield; - private readonly MethodSymbol _method; + private readonly ArrayBuilder _methodsWithYields; + private MethodSymbol _method; private Binder _enclosing; private void Visit(CSharpSyntaxNode syntax, Binder enclosing) @@ -43,11 +44,17 @@ private void Visit(CSharpSyntaxNode syntax, Binder enclosing) } } - public static SmallDictionary BuildMap(MethodSymbol method, CSharpSyntaxNode syntax, Binder enclosing, out bool sawYield) + // methodsWithYields will contain all function-declaration-like CSharpSyntaxNodes with yield statements contained within them. + // Currently the types of these are restricted to only be whatever the syntax parameter is, plus any LocalFunctionStatementSyntax contained within it. + // This may change if the language is extended to allow iterator lambdas, in which case the lambda would also be returned. + // (lambdas currently throw a diagnostic in WithLambdaParametersBinder.GetIteratorElementType when a yield is used within them) + public static SmallDictionary BuildMap(MethodSymbol method, CSharpSyntaxNode syntax, Binder enclosing, ArrayBuilder methodsWithYields) { - var builder = new LocalBinderFactory(method, enclosing); + var builder = new LocalBinderFactory(method, enclosing, methodsWithYields); builder.Visit(syntax); - sawYield = builder._sawYield; + // the other place this is possible is in a local function + if (builder._sawYield) + methodsWithYields.Add(syntax); return builder._map; } @@ -62,12 +69,13 @@ public override void VisitCompilationUnit(CompilationUnitSyntax node) } } - private LocalBinderFactory(MethodSymbol method, Binder enclosing) + private LocalBinderFactory(MethodSymbol method, Binder enclosing, ArrayBuilder methodsWithYields) { Debug.Assert((object)method != null); _map = new SmallDictionary(ReferenceEqualityComparer.Instance); _method = method; _enclosing = enclosing; + _methodsWithYields = methodsWithYields; } #region Starting points - these nodes contain statements @@ -122,10 +130,39 @@ public override void VisitParenthesizedLambdaExpression(ParenthesizedLambdaExpre public override void VisitLocalFunctionStatement(LocalFunctionStatementSyntax node) { - BlockSyntax body = node.Body; - if (body != null) + var body = (CSharpSyntaxNode)node.Body ?? node.ExpressionBody; + MethodSymbol match = null; + // Don't use LookupLocalFunction because it recurses up the tree, as it + // should be defined in the directly enclosing block (see note below) + foreach (var candidate in _enclosing.LocalFunctions) { - VisitBlock(body); + if (candidate.Name == node.Identifier.Text) + { + match = candidate; + } + } + if (match != null) + { + var oldMethod = _method; + _method = match; + var inMethod = new InMethodBinder(match, _enclosing); + AddToMap(node, inMethod); + if (body != null) + { + Visit(body, inMethod); + } + _method = oldMethod; + } + else + { + // The enclosing block should have found this node and created a LocalFunctionMethodSymbol + // The code that does so is in LocalScopeBinder.BuildLocalFunctions + + if (body != null) + { + // do our best to attempt to bind + Visit(body); + } } } @@ -160,6 +197,10 @@ public override void VisitBlock(BlockSyntax node) Visit(statement, blockBinder); if (statement is LocalFunctionStatementSyntax) { + if (_sawYield) + { + _methodsWithYields.Add(statement); + } // do not make the parent method an iterator if a local function has a yield _sawYield = oldSawYield; } diff --git a/src/Compilers/CSharp/Portable/Binder/LocalScopeBinder.cs b/src/Compilers/CSharp/Portable/Binder/LocalScopeBinder.cs index dcf722a6d249f6c68b03eece3c12e8ed43645625..2a1340579d5000e76c5a7329d5055e2304fea283 100644 --- a/src/Compilers/CSharp/Portable/Binder/LocalScopeBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/LocalScopeBinder.cs @@ -354,13 +354,26 @@ protected override void AddLookupSymbolsInfoInSingleBinder(LookupSymbolsInfo res } } } - else if (this.LocalsMap != null && options.CanConsiderLocals()) + if (options.CanConsiderLocals()) { - foreach (var local in this.LocalsMap) + if (this.LocalsMap != null) { - if (originalBinder.CanAddLookupSymbolInfo(local.Value, options, null)) + foreach (var local in this.LocalsMap) { - result.AddSymbol(local.Value, local.Key, 0); + if (originalBinder.CanAddLookupSymbolInfo(local.Value, options, null)) + { + result.AddSymbol(local.Value, local.Key, 0); + } + } + } + if (this.LocalFunctionsMap != null) + { + foreach (var local in this.LocalFunctionsMap) + { + if (originalBinder.CanAddLookupSymbolInfo(local.Value, options, null)) + { + result.AddSymbol(local.Value, local.Key, 0); + } } } } @@ -381,6 +394,7 @@ private bool ReportConflictWithLocal(Symbol local, Symbol newSymbol, string name diagnostics.Add(ErrorCode.ERR_LocalDuplicate, newLocation, name); return true; } + return false; } if (newSymbolKind == SymbolKind.Method) @@ -391,6 +405,7 @@ private bool ReportConflictWithLocal(Symbol local, Symbol newSymbol, string name diagnostics.Add(ErrorCode.ERR_LocalDuplicate, newLocation, name); return true; } + return false; } if (newSymbolKind == SymbolKind.Local || newSymbolKind == SymbolKind.Parameter) diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 0084f764fc0cf6daa0c5e9c7c78d7beaa2545d0f..c2034f3956b47e8527354ce0269e605714c55569 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -596,7 +596,7 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState) var variableSlotAllocatorOpt = ((object)lambda != null) ? _moduleBeingBuiltOpt.TryCreateVariableSlotAllocator(lambda, lambda.TopLevelMethod) : _moduleBeingBuiltOpt.TryCreateVariableSlotAllocator(method, method); - + // We make sure that an asynchronous mutation to the diagnostic bag does not // confuse the method body generator by making a fresh bag and then loading // any diagnostics emitted into it back into the main diagnostic bag. @@ -606,13 +606,21 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState) // In case of async lambdas, which synthesize a state machine type during the following rewrite, the containing method has already been uniquely named, // so there is no need to produce a unique method ordinal for the corresponding state machine type, whose name includes the (unique) containing method name. const int methodOrdinal = -1; - ArrayBuilder lambdaDebugInfo = ArrayBuilder.GetInstance(); - ArrayBuilder closureDebugInfo = ArrayBuilder.GetInstance(); - StateMachineTypeSymbol stateMachineType; - // Use full lowering on synthesized methods. Previously this was just an async lowering, - // but with local functions, more lowering types need to be allowed (e.g. iterators) - var loweredBody = LowerBodyOrInitializer(method, methodOrdinal, methodWithBody.Body, null, compilationState, diagnosticsThisMethod, ref variableSlotAllocatorOpt, lambdaDebugInfo, closureDebugInfo, out stateMachineType); - + + // Previously this was just an async lowering, but local functions can also be iterators, so we need to lower those + IteratorStateMachine iteratorStateMachine; + BoundStatement loweredBody = IteratorRewriter.Rewrite(methodWithBody.Body, method, methodOrdinal, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, out iteratorStateMachine); + StateMachineTypeSymbol stateMachine = iteratorStateMachine; + + if (!loweredBody.HasErrors) + { + AsyncStateMachine asyncStateMachine; + loweredBody = AsyncRewriter.Rewrite(loweredBody, method, methodOrdinal, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, out asyncStateMachine); + + Debug.Assert(iteratorStateMachine == null || asyncStateMachine == null); + stateMachine = stateMachine ?? asyncStateMachine; + } + MethodBody emittedBody = null; if (!diagnosticsThisMethod.HasAnyErrors() && !_globalHasErrors) { @@ -621,9 +629,9 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState) method, methodOrdinal, loweredBody, - lambdaDebugInfo.ToImmutableAndFree(), - closureDebugInfo.ToImmutableAndFree(), - stateMachineType, + ImmutableArray.Empty, + ImmutableArray.Empty, + stateMachine, variableSlotAllocatorOpt, diagnosticsThisMethod, _debugDocumentProvider, @@ -1479,13 +1487,13 @@ private static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSta { var inMethodBinder = factory.GetBinder(blockSyntax); - Binder binder = new ExecutableCodeBinder(blockSyntax, sourceMethod, inMethodBinder); + var binder = new ExecutableCodeBinder(blockSyntax, sourceMethod, inMethodBinder); body = binder.BindBlock(blockSyntax, diagnostics); importChain = binder.ImportChain; - if (inMethodBinder.IsDirectlyInIterator) + foreach (var iterator in binder.MethodSymbolsWithYield) { - foreach (var parameter in method.Parameters) + foreach (var parameter in iterator.Parameters) { if (parameter.RefKind != RefKind.None) { @@ -1497,15 +1505,19 @@ private static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSta } } - if (sourceMethod.IsUnsafe && compilation.Options.AllowUnsafe) // Don't cascade + var sourceIterator = iterator as SourceMethodSymbol; + if (sourceIterator != null) { - diagnostics.Add(ErrorCode.ERR_IllegalInnerUnsafe, sourceMethod.Locations[0]); - } + if (sourceIterator.IsUnsafe && compilation.Options.AllowUnsafe) // Don't cascade + { + diagnostics.Add(ErrorCode.ERR_IllegalInnerUnsafe, sourceIterator.Locations[0]); + } - if (sourceMethod.IsVararg) - { - // error CS1636: __arglist is not allowed in the parameter list of iterators - diagnostics.Add(ErrorCode.ERR_VarargsIterator, sourceMethod.Locations[0]); + if (sourceIterator.IsVararg) + { + // error CS1636: __arglist is not allowed in the parameter list of iterators + diagnostics.Add(ErrorCode.ERR_VarargsIterator, sourceIterator.Locations[0]); + } } } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs index 78826ab2466bc941df9711aaf26db7d7d368bd61..e10fb9825b4743ce069adb5b823ed6ff044ee18b 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs @@ -2043,7 +2043,8 @@ public override BoundNode VisitSequence(BoundSequence node) public override BoundNode VisitSequencePoint(BoundSequencePoint node) { - VisitStatement(node.StatementOpt); + if (node.StatementOpt != null) + VisitStatement(node.StatementOpt); return null; } @@ -2055,7 +2056,8 @@ public override BoundNode VisitSequencePointExpression(BoundSequencePointExpress public override BoundNode VisitSequencePointWithSpan(BoundSequencePointWithSpan node) { - VisitStatement(node.StatementOpt); + if (node.StatementOpt != null) + VisitStatement(node.StatementOpt); return null; } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalFunctionRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalFunctionRewriter.cs index e309700af76bd31eeb068f64bc98df1a0f11e507..2c6ed085898ca91bc567951bdb9e78202f490d90 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalFunctionRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalFunctionRewriter.cs @@ -46,12 +46,19 @@ private bool ImplicitReturnIsOkay(MethodSymbol method) public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node) { + var topLevelDebugId = new DebugId(_methodOrdinal, _compilationState.ModuleBuilderOpt.CurrentGenerationOrdinal); + var localFunctionDebugId = new DebugId(-1, _compilationState.ModuleBuilderOpt.CurrentGenerationOrdinal); + var synth = new SynthesizedLocalFunction(_method.ContainingType, _method, topLevelDebugId, node, localFunctionDebugId); + + _loweringTranslation.Add(node.LocalSymbol, synth); + var translatedBody = (BoundStatement)base.Visit(node.Body); + // Have to do ControlFlowPass here because in MethodCompiler, we don't call this for synthed methods // rather we go directly to LowerBodyOrInitializer, which skips over flow analysis (which is in CompileMethod) // (the same thing - calling ControlFlowPass.Analyze in the lowering - is done for lambdas) - var endIsReachable = ControlFlowPass.Analyze(node.LocalSymbol.DeclaringCompilation, node.LocalSymbol, node, _diagnostics); + var endIsReachable = ControlFlowPass.Analyze(node.LocalSymbol.DeclaringCompilation, node.LocalSymbol, translatedBody, _diagnostics); - var flowAnalyzed = node.Body; + var flowAnalyzed = translatedBody; if (endIsReachable) { if (ImplicitReturnIsOkay(node.LocalSymbol)) @@ -64,14 +71,9 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen } } - var topLevelDebugId = new DebugId(_methodOrdinal, _compilationState.ModuleBuilderOpt.CurrentGenerationOrdinal); - var localFunctionDebugId = new DebugId(-1, _compilationState.ModuleBuilderOpt.CurrentGenerationOrdinal); - var synth = new SynthesizedLocalFunction(_method.ContainingType, _method, topLevelDebugId, node, localFunctionDebugId); _compilationState.ModuleBuilderOpt.AddSynthesizedDefinition(_method.ContainingType, synth); _compilationState.AddSynthesizedMethod(synth, flowAnalyzed); - _loweringTranslation.Add(node.LocalSymbol, synth); - return new BoundNoOpStatement(node.Syntax, NoOpStatementFlavor.Default); } @@ -104,7 +106,8 @@ internal sealed class SynthesizedLocalFunction : SynthesizedMethodBaseSymbol, IS localFunction.SyntaxTree.GetReference(localFunction.Body.Syntax), localFunction.Syntax.GetLocation(), MakeName(topLevelMethod.Name, topLevelMethodId, localFunction.LocalSymbol.Name, localFunctionId), - DeclarationModifiers.Private | DeclarationModifiers.Static) + DeclarationModifiers.Private | DeclarationModifiers.Static | + (localFunction.LocalSymbol.IsAsync ? DeclarationModifiers.Async : 0)) { _topLevelMethod = topLevelMethod; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 6388d2de2537c8574ac25dda95a74a6a6a2a8504..52a7b802dba021af6fe57f00a36a1a60a9716bde 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -127,6 +127,11 @@ private PEModuleBuilder EmitModule private BoundStatement AddSequencePoint(BoundStatement node) { + if (node == null) + { + return node; + } + if (this.GenerateDebugInfo && !node.WasCompilerGenerated) { node = new BoundSequencePoint(node.Syntax, node); diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 21810de0869361bc4a2863cc7a3ec626dfdcdad3..2ff69c06c83088e6a992e6e2d4791f9a4fb74685 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -7986,6 +7986,8 @@ private static bool IsDeclarationModifier(SyntaxKind kind) private bool ParseLocalFunctionStatement(out LocalFunctionStatementSyntax decl) { ResetPoint resetPoint = this.GetResetPoint(); + var modifiers = _pool.Allocate(); + this.ParseModifiers(modifiers); TypeSyntax type = this.ParseReturnType(); SyntaxToken identifier = this.EatToken(SyntaxKind.IdentifierToken); if (this.CurrentToken.Kind != SyntaxKind.OpenParenToken) @@ -7996,6 +7998,22 @@ private bool ParseLocalFunctionStatement(out LocalFunctionStatementSyntax decl) return false; } this.Release(ref resetPoint); + + bool parentScopeIsInAsync = IsInAsync; + IsInAsync = false; + for (int i = 0; i < modifiers.Count; i++) + { + switch (modifiers[i].Kind) + { + case SyntaxKind.AsyncKeyword: + IsInAsync = true; + break; + default: + modifiers[i] = this.AddError(modifiers[i], ErrorCode.ERR_BadMemberFlag, ((SyntaxToken)modifiers[i]).Text); + break; + } + } + ParameterListSyntax paramList = this.ParseParenthesizedParameterList(allowThisKeyword: true, allowDefaults: true, allowAttributes: true); BlockSyntax blockBody; @@ -8004,14 +8022,16 @@ private bool ParseLocalFunctionStatement(out LocalFunctionStatementSyntax decl) this.ParseBlockAndExpressionBodiesWithSemicolon(out blockBody, out expressionBody, out semicolon); decl = _syntaxFactory.LocalFunctionStatement( - type, - identifier, - paramList, - blockBody, - expressionBody, - semicolon); + modifiers.ToTokenList(), + type, + identifier, + paramList, + blockBody, + expressionBody, + semicolon); decl = CheckForBlockAndExpressionBody(blockBody, expressionBody, decl); + IsInAsync = parentScopeIsInAsync; return true; } diff --git a/src/Compilers/CSharp/Portable/PublicAPI.txt b/src/Compilers/CSharp/Portable/PublicAPI.txt index 941986565aa94c790f6ad7e00f32827fdd18cf50..52ce0e51faaef2ef064ce952f955324aa75cd93f 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.txt @@ -1173,17 +1173,20 @@ Microsoft.CodeAnalysis.CSharp.Syntax.LocalDeclarationStatementSyntax.WithModifie Microsoft.CodeAnalysis.CSharp.Syntax.LocalDeclarationStatementSyntax.WithSemicolonToken(Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalDeclarationStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.AddBodyStatements(params Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.AddModifiers(params Microsoft.CodeAnalysis.SyntaxToken[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.AddParameterListParameters(params Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Body.get -> Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.ExpressionBody.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Identifier.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Modifiers.get -> Microsoft.CodeAnalysis.SyntaxTokenList Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.ParameterList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.ReturnType.get -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.SemicolonToken.get -> Microsoft.CodeAnalysis.SyntaxToken -Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Update(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithBody(Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithExpressionBody(Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithIdentifier(Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithModifiers(Microsoft.CodeAnalysis.SyntaxTokenList modifiers) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithParameterList(Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithReturnType(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithSemicolonToken(Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax @@ -3743,9 +3746,9 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalDeclarationStatement(Mic static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalDeclarationStatement(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax declaration) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalDeclarationStatementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalDeclarationStatement(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax declaration, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalDeclarationStatementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax -static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax -static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, string identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LockStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax statement) -> Microsoft.CodeAnalysis.CSharp.Syntax.LockStatementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LockStatement(Microsoft.CodeAnalysis.SyntaxToken lockKeyword, Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.SyntaxToken closeParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax statement) -> Microsoft.CodeAnalysis.CSharp.Syntax.LockStatementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.MakeRefExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.MakeRefExpressionSyntax diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionMethodSymbol.cs index ad117cd5d1bcb74bf127d9186706a15f356a7e2f..a4cdcdf63a96a3e8676809271f99a3d856dc045f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionMethodSymbol.cs @@ -35,7 +35,7 @@ internal class LocalFunctionMethodSymbol : SourceMethodSymbol this.MakeFlags( MethodKind.LocalFunction, - DeclarationModifiers.Static, // TODO: Will change when we allow local captures (also change in LocalFunctionRewriter) + DeclarationModifiers.Static | syntax.Modifiers.ToDeclarationModifiers(), // TODO: Will change when we allow local captures (also change in LocalFunctionRewriter) returnsVoid: false, // will be fixed in MethodChecks isExtensionMethod: isExtensionMethod); } diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index 0b15d48a985638f2477981f45437c1d1b27a6d75..a3af993b8ddb67b2f81f925a635ab5d98a5199f9 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -1635,6 +1635,7 @@ + diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 96a84ff29fd1c04825fca3cbd76cfc4795a53fd5..4d53ab4727c7910acc547230760fe7edc16c0860 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -1,21 +1,14 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; -using Microsoft.CodeAnalysis.Test.Utilities; -using Roslyn.Test.Utilities; using Xunit; -using System.Threading; -using System.Linq; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class LocalFunctionTests : CSharpTestBase { [Fact] - public void LocalFunctionEndToEnd() + public void EndToEnd() { var source = @" using System; @@ -38,7 +31,89 @@ void Local() } [Fact] - public void LocalFunctionEnumerator() + public void ExpressionBody() + { + var source = @" +using System; + +class Program +{ + static void Main(string[] args) + { + int Local() => 2; + Console.WriteLine(Local()); + } +} +"; + var comp = CompileAndVerify(source, expectedOutput: @" +2 +"); + } + + [Fact] + public void Recursion() + { + var source = @" +using System; +using System.Collections.Generic; + +class Program +{ + static void Main(string[] args) + { + void Foo(int depth) + { + if (depth > 10) + { + Console.WriteLine(2); + return; + } + Foo(depth + 1); + } + Foo(0); + } +} +"; + var comp = CompileAndVerify(source, expectedOutput: @" +2 +"); + } + + [Fact] + public void MutualRecursion() + { + var source = @" +using System; +using System.Collections.Generic; + +class Program +{ + static void Main(string[] args) + { + void Foo(int depth) + { + if (depth > 10) + { + Console.WriteLine(2); + return; + } + void Bar(int depth2) + { + Foo(depth2 + 1); + } + Bar(depth + 1); + } + Foo(0); + } +} +"; + var comp = CompileAndVerify(source, expectedOutput: @" +2 +"); + } + + [Fact] + public void Enumerator() { var source = @" using System; @@ -60,5 +135,146 @@ IEnumerable Local() 2 "); } + + [Fact] + public void Async() + { + var source = @" +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +class Program +{ + static void Main(string[] args) + { + async Task Local() + { + return await Task.FromResult(2); + } + Console.WriteLine(Local().Result); + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: new CSharpCompilationOptions(OutputKind.ConsoleApplication)); + var comp = CompileAndVerify(compilation, expectedOutput: @" +2 +"); + } + + [Fact] + public void NoBody() + { + var source = @" +using System; +using System.Collections.Generic; + +class Program +{ + static void Main(string[] args) + { + void Local1() + void Local2(); + } +} +"; + var option = TestOptions.ReleaseExe.WithWarningLevel(0); + CreateCompilationWithMscorlibAndSystemCore(source, options: option).VerifyDiagnostics( + // (9,22): error CS1002: ; expected + // void Local1() + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(9, 22), + // (9,9): error CS0501: 'Program.Local1()' must declare a body because it is not marked abstract, extern, or partial + // void Local1() + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, @"void Local1() +").WithArguments("Program.Local1()").WithLocation(9, 9), + // (10,9): error CS0501: 'Program.Local2()' must declare a body because it is not marked abstract, extern, or partial + // void Local2(); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "void Local2();").WithArguments("Program.Local2()").WithLocation(10, 9) + ); + } + + [Fact] + public void ByRefIterator() + { + var source = @" +using System; +using System.Collections.Generic; + +class Program +{ + static void Main(string[] args) + { + IEnumerable Local(ref int x) + { + yield return x; + } + int y = 2; + Console.WriteLine(string.Join("","", Local(ref y))); + } +} +"; + var option = TestOptions.ReleaseExe.WithWarningLevel(0); + CreateCompilationWithMscorlibAndSystemCore(source, options: option).VerifyDiagnostics( + // (9,40): error CS1623: Iterators cannot have ref or out parameters + // IEnumerable Local(ref int x) + Diagnostic(ErrorCode.ERR_BadIteratorArgType, "x").WithLocation(9, 40) + ); + } + + [Fact] + public void ArglistIterator() + { + var source = @" +using System; +using System.Collections.Generic; + +class Program +{ + static void Main(string[] args) + { + IEnumerable Local(__arglist) + { + yield return 2; + } + Console.WriteLine(string.Join("","", Local(__arglist()))); + } +} +"; + var option = TestOptions.ReleaseExe.WithWarningLevel(0); + CreateCompilationWithMscorlibAndSystemCore(source, options: option).VerifyDiagnostics( + // (9,26): error CS1636: __arglist is not allowed in the parameter list of iterators + // IEnumerable Local(__arglist) + Diagnostic(ErrorCode.ERR_VarargsIterator, "Local").WithLocation(9, 26) + ); + } + + [Fact] + public void PartialExpressionBody() + { + var source = @" +using System; +using System.Collections.Generic; + +class Program +{ + static void Main(string[] args) + { + void Local() => Console.Writ + } +} +"; + var option = TestOptions.ReleaseExe.WithWarningLevel(0); + CreateCompilationWithMscorlibAndSystemCore(source, options: option).VerifyDiagnostics( + // (9,37): error CS1002: ; expected + // void Local() => Console.Writ + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(9, 37), + // (9,33): error CS0117: 'Console' does not contain a definition for 'Writ' + // void Local() => Console.Writ + Diagnostic(ErrorCode.ERR_NoSuchMember, "Writ").WithArguments("System.Console", "Writ").WithLocation(9, 33), + // (9,25): error CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement + // void Local() => Console.Writ + Diagnostic(ErrorCode.ERR_IllegalStatement, "Console.Writ").WithLocation(9, 25) + ); + } } }