diff --git a/docs/features/ExpressionVariables.md b/docs/features/ExpressionVariables.md
new file mode 100644
index 0000000000000..9564f0eb6ee23
--- /dev/null
+++ b/docs/features/ExpressionVariables.md
@@ -0,0 +1,17 @@
+Expression Variables
+=========================
+
+The *expression variables* feature extends the features introduced in C# 7 to permit expressions
+containing expression variables (out variable declarations and declaration patterns) in field
+initializers, property initializers, ctor-initializers, and query clauses.
+
+See https://github.com/dotnet/csharplang/issues/32 and
+https://github.com/dotnet/csharplang/blob/master/proposals/expression-variables-in-initializers.md
+for more information.
+
+Current state of the feature:
+
+[X] Permit in field initializers
+[X] Permit in property initializers
+[ ] Permit in ctor-initializers
+[X] Permit in query clauses
\ No newline at end of file
diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs
index 3aa3764cd61d0..d5d233efdab75 100644
--- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs
+++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs
@@ -209,6 +209,9 @@ public override Binder VisitDestructorDeclaration(DestructorDeclarationSyntax pa
// Destructors have neither parameters nor type parameters, so there's nothing special to do here.
resultBinder = VisitCore(parent.Parent);
+ SourceMemberMethodSymbol method = GetMethodSymbol(parent, resultBinder);
+ resultBinder = new InMethodBinder(method, resultBinder);
+
resultBinder = resultBinder.WithUnsafeRegionIfNecessary(parent.Modifiers);
binderCache.TryAdd(key, resultBinder);
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
index 740023e034672..9a06e3eef2d40 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
@@ -236,35 +236,43 @@ internal BoundExpression BindValueAllowArgList(ExpressionSyntax node, Diagnostic
return CheckValue(result, valueKind, diagnostics);
}
- internal BoundExpression BindVariableOrAutoPropInitializer(
+ internal BoundFieldEqualsValue BindFieldInitializer(
+ FieldSymbol field,
EqualsValueClauseSyntax initializerOpt,
- RefKind refKind,
- TypeSymbol varType,
DiagnosticBag diagnostics)
{
+ Debug.Assert((object)this.ContainingMemberOrLambda == field);
+
if (initializerOpt == null)
{
return null;
}
- SymbolKind containingMemberOrLambdaKind = this.ContainingMemberOrLambda.Kind;
- Debug.Assert(containingMemberOrLambdaKind == SymbolKind.Field || containingMemberOrLambdaKind == SymbolKind.Method);
- bool isMemberInitializer = containingMemberOrLambdaKind == SymbolKind.Field;
- Binder initializerBinder = isMemberInitializer ? this.GetBinder(initializerOpt) : this;
-
+ Binder initializerBinder = this.GetBinder(initializerOpt);
Debug.Assert(initializerBinder != null);
- BindValueKind valueKind;
- ExpressionSyntax value;
- IsInitializerRefKindValid(initializerOpt, initializerOpt, refKind, diagnostics, out valueKind, out value);
- var initializer = initializerBinder.BindPossibleArrayInitializer(value, varType, valueKind, diagnostics);
- initializer = initializerBinder.GenerateConversionForAssignment(varType, initializer, diagnostics);
+ BoundExpression result = initializerBinder.BindVariableOrAutoPropInitializerValue(initializerOpt, RefKind.None,
+ field.GetFieldType(initializerBinder.FieldsBeingBound), diagnostics);
- if (isMemberInitializer)
+ return new BoundFieldEqualsValue(initializerOpt, field, initializerBinder.GetDeclaredLocalsForScope(initializerOpt), result);
+ }
+
+ internal BoundExpression BindVariableOrAutoPropInitializerValue(
+ EqualsValueClauseSyntax initializerOpt,
+ RefKind refKind,
+ TypeSymbol varType,
+ DiagnosticBag diagnostics)
+ {
+ if (initializerOpt == null)
{
- initializer = initializerBinder.WrapWithVariablesIfAny(initializerOpt, initializer);
+ return null;
}
+ BindValueKind valueKind;
+ ExpressionSyntax value;
+ IsInitializerRefKindValid(initializerOpt, initializerOpt, refKind, diagnostics, out valueKind, out value);
+ BoundExpression initializer = BindPossibleArrayInitializer(value, varType, valueKind, diagnostics);
+ initializer = GenerateConversionForAssignment(varType, initializer, diagnostics);
return initializer;
}
@@ -278,9 +286,9 @@ internal Binder CreateBinderForParameterDefaultValue(
binder);
}
- internal BoundExpression BindParameterDefaultValue(
+ internal BoundParameterEqualsValue BindParameterDefaultValue(
EqualsValueClauseSyntax defaultValueSyntax,
- TypeSymbol parameterType,
+ ParameterSymbol parameter,
DiagnosticBag diagnostics,
out BoundExpression valueBeforeConversion)
{
@@ -295,10 +303,11 @@ internal BoundExpression BindParameterDefaultValue(
// Always generate the conversion, even if the expression is not convertible to the given type.
// We want the erroneous conversion in the tree.
- return defaultValueBinder.WrapWithVariablesIfAny(defaultValueSyntax, defaultValueBinder.GenerateConversionForAssignment(parameterType, valueBeforeConversion, diagnostics, isDefaultParameter: true));
+ return new BoundParameterEqualsValue(defaultValueSyntax, parameter, defaultValueBinder.GetDeclaredLocalsForScope(defaultValueSyntax),
+ defaultValueBinder.GenerateConversionForAssignment(parameter.Type, valueBeforeConversion, diagnostics, isDefaultParameter: true));
}
- internal BoundExpression BindEnumConstantInitializer(
+ internal BoundFieldEqualsValue BindEnumConstantInitializer(
SourceEnumConstantSymbol symbol,
EqualsValueClauseSyntax equalsValueSyntax,
DiagnosticBag diagnostics)
@@ -308,7 +317,7 @@ internal BoundExpression BindEnumConstantInitializer(
var initializer = initializerBinder.BindValue(equalsValueSyntax.Value, diagnostics, BindValueKind.RValue);
initializer = initializerBinder.GenerateConversionForAssignment(symbol.ContainingType.EnumUnderlyingType, initializer, diagnostics);
- return initializerBinder.WrapWithVariablesIfAny(equalsValueSyntax, initializer);
+ return new BoundFieldEqualsValue(equalsValueSyntax, symbol, initializerBinder.GetDeclaredLocalsForScope(equalsValueSyntax), initializer);
}
public BoundExpression BindExpression(ExpressionSyntax node, DiagnosticBag diagnostics)
@@ -2311,7 +2320,7 @@ private BoundExpression BindOutVariableDeclarationArgument(
Debug.Assert(localSymbol.DeclarationKind == LocalDeclarationKind.OutVariable);
if ((InConstructorInitializer || InFieldInitializer) && ContainingMemberOrLambda.ContainingSymbol.Kind == SymbolKind.NamedType)
{
- Error(diagnostics, ErrorCode.ERR_ExpressionVariableInConstructorOrFieldInitializer, declarationExpression);
+ CheckFeatureAvailability(declarationExpression, MessageID.IDS_FeatureExpressionVariablesInQueriesAndInitializers, diagnostics);
}
bool isConst = false;
@@ -3318,14 +3327,20 @@ internal BoundExpression BindConstructorInitializer(
MethodSymbol constructor,
DiagnosticBag diagnostics)
{
- Binder initializerBinder = initializerArgumentListOpt == null ? this : this.GetBinder(initializerArgumentListOpt);
- Debug.Assert(initializerBinder != null);
-
- var result = initializerBinder.BindConstructorInitializerCore(initializerArgumentListOpt, constructor, diagnostics);
+ Binder argumentListBinder = null;
if (initializerArgumentListOpt != null)
{
- result = initializerBinder.WrapWithVariablesIfAny(initializerArgumentListOpt, result);
+ argumentListBinder = this.GetBinder(initializerArgumentListOpt);
+ }
+
+ var result = (argumentListBinder ?? this).BindConstructorInitializerCore(initializerArgumentListOpt, constructor, diagnostics);
+
+ if (argumentListBinder != null)
+ {
+ // This code is reachable only for speculative SemanticModel.
+ Debug.Assert(argumentListBinder.IsSemanticModelBinder);
+ result = argumentListBinder.WrapWithVariablesIfAny(initializerArgumentListOpt, result);
}
return result;
@@ -3336,7 +3351,26 @@ private BoundExpression BindConstructorInitializerCore(
MethodSymbol constructor,
DiagnosticBag diagnostics)
{
- Debug.Assert(initializerArgumentListOpt == null || this.SkipSemanticModelBinder() == this.GetBinder(initializerArgumentListOpt).SkipSemanticModelBinder());
+ // Either our base type is not object, or we have an initializer syntax, or both. We're going to
+ // need to do overload resolution on the set of constructors of the base type, either on
+ // the provided initializer syntax, or on an implicit ": base()" syntax.
+
+ // SPEC ERROR: The specification states that if you have the situation
+ // SPEC ERROR: class B { ... } class D1 : B {} then the default constructor
+ // SPEC ERROR: generated for D1 must call an accessible *parameterless* constructor
+ // SPEC ERROR: in B. However, it also states that if you have
+ // SPEC ERROR: class B { ... } class D2 : B { D2() {} } or
+ // SPEC ERROR: class B { ... } class D3 : B { D3() : base() {} } then
+ // SPEC ERROR: the compiler performs *overload resolution* to determine
+ // SPEC ERROR: which accessible constructor of B is called. Since B might have
+ // SPEC ERROR: a ctor with all optional parameters, overload resolution might
+ // SPEC ERROR: succeed even if there is no parameterless constructor. This
+ // SPEC ERROR: is unintentionally inconsistent, and the native compiler does not
+ // SPEC ERROR: implement this behavior. Rather, we should say in the spec that
+ // SPEC ERROR: if there is no ctor in D1, then a ctor is created for you exactly
+ // SPEC ERROR: as though you'd said "D1() : base() {}".
+ // SPEC ERROR: This is what we now do in Roslyn.
+
Debug.Assert((object)constructor != null);
Debug.Assert(constructor.MethodKind == MethodKind.Constructor ||
constructor.MethodKind == MethodKind.StaticConstructor); // error scenario: constructor initializer on static constructor
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs
index 73b583d0d7954..6919314e1a887 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs
@@ -110,7 +110,7 @@ internal static void BindRegularCSharpFieldInitializers(
parentBinder = new LocalScopeBinder(parentBinder).WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.FieldInitializer, fieldSymbol);
- BoundFieldInitializer boundInitializer = BindFieldInitializer(parentBinder, fieldSymbol, initializerNode, diagnostics);
+ BoundFieldEqualsValue boundInitializer = BindFieldInitializer(parentBinder, fieldSymbol, initializerNode, diagnostics);
boundInitializers.Add(boundInitializer);
}
}
@@ -246,7 +246,7 @@ private static BoundInitializer BindGlobalStatement(
return new BoundGlobalStatementInitializer(statementNode, statement);
}
- private static BoundFieldInitializer BindFieldInitializer(Binder binder, FieldSymbol fieldSymbol, EqualsValueClauseSyntax equalsValueClauseNode,
+ private static BoundFieldEqualsValue BindFieldInitializer(Binder binder, FieldSymbol fieldSymbol, EqualsValueClauseSyntax equalsValueClauseNode,
DiagnosticBag diagnostics)
{
Debug.Assert(!fieldSymbol.IsMetadataConstant);
@@ -269,17 +269,14 @@ private static BoundFieldInitializer BindFieldInitializer(Binder binder, FieldSy
}
binder = new ExecutableCodeBinder(equalsValueClauseNode, fieldSymbol, new LocalScopeBinder(binder));
- var boundInitValue = binder.BindVariableOrAutoPropInitializer(equalsValueClauseNode, RefKind.None, fieldSymbol.GetFieldType(fieldsBeingBound), initializerDiagnostics);
+ BoundFieldEqualsValue boundInitValue = binder.BindFieldInitializer(fieldSymbol, equalsValueClauseNode, initializerDiagnostics);
if (isImplicitlyTypedField)
{
initializerDiagnostics.Free();
}
- return new BoundFieldInitializer(
- equalsValueClauseNode.Value, //we want the attached sequence point to indicate the value node
- fieldSymbol,
- boundInitValue);
+ return boundInitValue;
}
}
}
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
index 886f0c0518c71..e313d726aba3b 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
@@ -286,7 +286,7 @@ private BoundPattern BindDeclarationPattern(
{
if ((InConstructorInitializer || InFieldInitializer) && ContainingMemberOrLambda.ContainingSymbol.Kind == SymbolKind.NamedType)
{
- Error(diagnostics, ErrorCode.ERR_ExpressionVariableInConstructorOrFieldInitializer, node);
+ CheckFeatureAvailability(node, MessageID.IDS_FeatureExpressionVariablesInQueriesAndInitializers, diagnostics);
}
localSymbol.SetType(declType);
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs
index 569cea3042685..cae6965c02af8 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs
@@ -600,7 +600,7 @@ private BoundBlock CreateLambdaBlockForQueryClause(ExpressionSyntax expression,
var locals = this.GetDeclaredLocalsForScope(expression);
if (locals.Any())
{
- diagnostics.Add(ErrorCode.ERR_ExpressionVariableInQueryClause, locals[0].Locations[0]);
+ CheckFeatureAvailability(expression, MessageID.IDS_FeatureExpressionVariablesInQueriesAndInitializers, diagnostics, locals[0].Locations[0]);
}
return this.CreateBlockFromExpression(expression, locals, RefKind.None, result, expression, diagnostics);
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
index 90a3934143175..9c542b69859b4 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
@@ -1,5 +1,6 @@
// 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 System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
@@ -520,7 +521,7 @@ private BoundStatement BindLocalFunctionStatement(LocalFunctionStatementSyntax n
{
if (ImplicitReturnIsOkay(localSymbol))
{
- block = FlowAnalysisPass.AppendImplicitReturn(block, localSymbol, node.Body);
+ block = FlowAnalysisPass.AppendImplicitReturn(block, localSymbol);
}
else
{
@@ -2855,7 +2856,7 @@ private static bool IsValidExpressionBody(SyntaxNode expressionSyntax, BoundExpr
///
/// Binds an expression-bodied member with expression e as either { return e;} or { e; }.
///
- internal BoundBlock BindExpressionBodyAsBlock(ArrowExpressionClauseSyntax expressionBody,
+ internal virtual BoundBlock BindExpressionBodyAsBlock(ArrowExpressionClauseSyntax expressionBody,
DiagnosticBag diagnostics)
{
Binder bodyBinder = this.GetBinder(expressionBody);
@@ -2901,6 +2902,75 @@ private BindValueKind GetRequiredReturnValueKind(RefKind refKind)
return requiredValueKind;
}
+ public virtual BoundNode BindMethodBody(CSharpSyntaxNode syntax, DiagnosticBag diagnostics)
+ {
+ switch (syntax)
+ {
+ case BaseMethodDeclarationSyntax method:
+ if (method.Kind() == SyntaxKind.ConstructorDeclaration)
+ {
+ return BindConstructorBody((ConstructorDeclarationSyntax)method, diagnostics);
+ }
+
+ return BindMethodBody(method, method.Body, method.ExpressionBody, diagnostics);
+
+ case AccessorDeclarationSyntax accessor:
+ return BindMethodBody(accessor, accessor.Body, accessor.ExpressionBody, diagnostics);
+
+ case ArrowExpressionClauseSyntax arrowExpression:
+ return BindExpressionBodyAsBlock(arrowExpression, diagnostics);
+
+ default:
+ throw ExceptionUtilities.UnexpectedValue(syntax.Kind());
+ }
+ }
+
+ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor, DiagnosticBag diagnostics)
+ {
+ if (constructor.Initializer == null && constructor.Body == null && constructor.ExpressionBody == null)
+ {
+ return null;
+ }
+
+ Binder bodyBinder = this.GetBinder(constructor);
+ Debug.Assert(bodyBinder != null);
+
+ // Using BindStatement to bind block to make sure we are reusing results of partial binding in SemanticModel
+ return new BoundConstructorMethodBody(constructor,
+ bodyBinder.GetDeclaredLocalsForScope(constructor),
+ constructor.Initializer == null ? null : bodyBinder.BindConstructorInitializer(constructor.Initializer, diagnostics),
+ constructor.Body == null ? null : (BoundBlock)bodyBinder.BindStatement(constructor.Body, diagnostics),
+ constructor.ExpressionBody == null ?
+ null :
+ bodyBinder.BindExpressionBodyAsBlock(constructor.ExpressionBody,
+ constructor.Body == null ? diagnostics : new DiagnosticBag()));
+ }
+
+ internal virtual BoundExpressionStatement BindConstructorInitializer(ConstructorInitializerSyntax initializer, DiagnosticBag diagnostics)
+ {
+ BoundExpression initializerInvocation = GetBinder(initializer).BindConstructorInitializer(initializer.ArgumentList, (MethodSymbol)this.ContainingMember(), diagnostics);
+ // Base WasCompilerGenerated state off of whether constructor is implicitly declared, this will ensure proper instrumentation.
+ Debug.Assert(!this.ContainingMember().IsImplicitlyDeclared);
+ var constructorInitializer = new BoundExpressionStatement(initializer, initializerInvocation);
+ Debug.Assert(initializerInvocation.HasAnyErrors || constructorInitializer.IsConstructorInitializer(), "Please keep this bound node in sync with BoundNodeExtensions.IsConstructorInitializer.");
+ return constructorInitializer;
+ }
+
+ private BoundNode BindMethodBody(CSharpSyntaxNode declaration, BlockSyntax blockBody, ArrowExpressionClauseSyntax expressionBody, DiagnosticBag diagnostics)
+ {
+ if (blockBody == null && expressionBody == null)
+ {
+ return null;
+ }
+
+ // Using BindStatement to bind block to make sure we are reusing results of partial binding in SemanticModel
+ return new BoundNonConstructorMethodBody(declaration,
+ blockBody == null ? null : (BoundBlock)BindStatement(blockBody, diagnostics),
+ expressionBody == null ?
+ null :
+ BindExpressionBodyAsBlock(expressionBody,
+ blockBody == null ? diagnostics : new DiagnosticBag()));
+ }
internal virtual ImmutableArray Locals
{
diff --git a/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs b/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs
index d3f227eb64a93..d38596fe670c8 100644
--- a/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs
+++ b/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs
@@ -67,8 +67,9 @@ private void ComputeBinderMap()
map = LocalBinderFactory.BuildMap(_memberSymbol, _root, this, methodsWithYield, _binderUpdatedHandler);
foreach (var methodWithYield in methodsWithYield)
{
- Binder binder;
- if (map.TryGetValue(methodWithYield, out binder))
+ Binder binder = this;
+ if (methodWithYield.Kind() != SyntaxKind.GlobalStatement &&
+ (methodWithYield == _root || map.TryGetValue(methodWithYield, out binder)))
{
Symbol containing = binder.ContainingMemberOrLambda;
diff --git a/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs b/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs
index f888828b62369..95178715a02fe 100644
--- a/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs
+++ b/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs
@@ -42,6 +42,7 @@ protected void FindExpressionVariables(
case SyntaxKind.IfStatement:
case SyntaxKind.SwitchStatement:
case SyntaxKind.VariableDeclarator:
+ case SyntaxKind.ConstructorDeclaration:
break;
case SyntaxKind.ArgumentList:
Debug.Assert(node.Parent is ConstructorInitializerSyntax);
@@ -304,6 +305,14 @@ public override void VisitAssignmentExpression(AssignmentExpressionSyntax node)
Visit(node.Right);
}
+ public override void VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
+ {
+ if (node.Initializer != null)
+ {
+ VisitNodeToBind(node.Initializer);
+ }
+ }
+
private void CollectVariablesFromDeconstruction(
ExpressionSyntax possibleTupleDeclaration,
AssignmentExpressionSyntax deconstruction)
diff --git a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs
index 35fd73042146f..db708e8ded058 100644
--- a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs
+++ b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs
@@ -134,32 +134,42 @@ private LocalBinderFactory(Symbol containingMemberOrLambda, SyntaxNode root, Bin
public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
{
- VisitBlock(node.Body);
+ Visit(node.Body);
+ Visit(node.ExpressionBody);
}
public override void VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
{
- VisitBlock(node.Body);
+ Binder enclosing = new ExpressionVariableBinder(node, _enclosing);
+ AddToMap(node, enclosing);
+
+ Visit(node.Initializer, enclosing);
+ Visit(node.Body, enclosing);
+ Visit(node.ExpressionBody, enclosing);
}
public override void VisitDestructorDeclaration(DestructorDeclarationSyntax node)
{
- VisitBlock(node.Body);
+ Visit(node.Body);
+ Visit(node.ExpressionBody);
}
public override void VisitAccessorDeclaration(AccessorDeclarationSyntax node)
{
- VisitBlock(node.Body);
+ Visit(node.Body);
+ Visit(node.ExpressionBody);
}
public override void VisitConversionOperatorDeclaration(ConversionOperatorDeclarationSyntax node)
{
- VisitBlock(node.Body);
+ Visit(node.Body);
+ Visit(node.ExpressionBody);
}
public override void VisitOperatorDeclaration(OperatorDeclarationSyntax node)
{
- VisitBlock(node.Body);
+ Visit(node.Body);
+ Visit(node.ExpressionBody);
}
public override void VisitSimpleLambdaExpression(SimpleLambdaExpressionSyntax node)
@@ -290,23 +300,20 @@ public override void VisitAttribute(AttributeSyntax node)
}
}
- public override void VisitArgumentList(ArgumentListSyntax node)
+ public override void VisitConstructorInitializer(ConstructorInitializerSyntax node)
{
- if (_root == node)
- {
- // We are supposed to get here only for constructor initializers
- Debug.Assert(node.Parent is ConstructorInitializerSyntax);
- var argBinder = new ExpressionVariableBinder(node, _enclosing);
- AddToMap(node, argBinder);
+ var binder = _enclosing.WithAdditionalFlags(BinderFlags.ConstructorInitializer);
+ AddToMap(node, binder);
- foreach (var arg in node.Arguments)
+ if (node.ArgumentList != null)
+ {
+ if (_root == node)
{
- Visit(arg.Expression, argBinder);
+ binder = new ExpressionVariableBinder(node.ArgumentList, binder);
+ AddToMap(node.ArgumentList, binder);
}
- }
- else
- {
- base.VisitArgumentList(node);
+
+ Visit(node.ArgumentList, binder);
}
}
diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
index 42f645b9d0753..a6ebac2ee1e69 100644
--- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
+++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
@@ -33,12 +33,8 @@
-
-
-
-
-
-
+
+
@@ -1673,4 +1669,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
index d50e17632e602..d1f39b32994aa 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
+++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
@@ -4489,24 +4489,6 @@ internal static string ERR_ExpressionTreeMustHaveDelegate {
}
}
- ///
- /// Looks up a localized string similar to Out variable and pattern variable declarations are not allowed within constructor initializers, field initializers, or property initializers..
- ///
- internal static string ERR_ExpressionVariableInConstructorOrFieldInitializer {
- get {
- return ResourceManager.GetString("ERR_ExpressionVariableInConstructorOrFieldInitializer", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Out variable and pattern variable declarations are not allowed within a query clause..
- ///
- internal static string ERR_ExpressionVariableInQueryClause {
- get {
- return ResourceManager.GetString("ERR_ExpressionVariableInQueryClause", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll?.
///
@@ -10574,6 +10556,15 @@ internal static string IDS_FeatureExpressionBodiedProperty {
}
}
+ ///
+ /// Looks up a localized string similar to declaration of expression variables in member initializers and queries.
+ ///
+ internal static string IDS_FeatureExpressionVariablesInQueriesAndInitializers {
+ get {
+ return ResourceManager.GetString("IDS_FeatureExpressionVariablesInQueriesAndInitializers", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to extension method.
///
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index 5c0df8816e711..f56fca4799862 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -5096,12 +5096,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
The syntax 'var (...)' as an lvalue is reserved.
-
- Out variable and pattern variable declarations are not allowed within constructor initializers, field initializers, or property initializers.
-
-
- Out variable and pattern variable declarations are not allowed within a query clause.
-
{ or ; or => expected
@@ -5318,4 +5312,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
"Invalid rank specifier: expected ']'
+
+ declaration of expression variables in member initializers and queries
+
diff --git a/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs
index b41c27c372933..80ecabaf4f9ac 100644
--- a/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs
+++ b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs
@@ -129,7 +129,9 @@ public CodeGenerator(
_boundBody = boundBody;
}
- _methodBodySyntaxOpt = (method as SourceMemberMethodSymbol)?.BodySyntax;
+ var sourceMethod = method as SourceMemberMethodSymbol;
+ (BlockSyntax blockBody, ArrowExpressionClauseSyntax expressionBody) = sourceMethod?.Bodies ?? default;
+ _methodBodySyntaxOpt = (SyntaxNode)blockBody ?? expressionBody ?? sourceMethod?.SyntaxNode;
}
private bool IsDebugPlus()
diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
index 24f3baba16a7c..0785b20b573cc 100644
--- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
+++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
@@ -774,18 +774,14 @@ public SymbolInfo GetSpeculativeSymbolInfo(int position, ConstructorInitializerS
return SymbolInfo.None;
}
- var binder = this.GetEnclosingBinder(position);
+ var binder = memberModel.GetEnclosingBinder(position);
if (binder != null)
{
var diagnostics = DiagnosticBag.GetInstance();
+ binder = new ExecutableCodeBinder(constructorInitializer, binder.ContainingMemberOrLambda, binder);
- if (constructorInitializer.ArgumentList != null)
- {
- binder = new ExecutableCodeBinder(constructorInitializer.ArgumentList, binder.ContainingMemberOrLambda, binder);
- }
-
- var bnode = memberModel.Bind(binder, constructorInitializer, diagnostics);
- var binfo = memberModel.GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, bnode, bnode, boundNodeForSyntacticParent: null, binderOpt: binder);
+ BoundExpressionStatement bnode = binder.BindConstructorInitializer(constructorInitializer, diagnostics);
+ var binfo = memberModel.GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, bnode.Expression, bnode.Expression, boundNodeForSyntacticParent: null, binderOpt: binder);
diagnostics.Free();
return binfo;
}
diff --git a/src/Compilers/CSharp/Portable/Compilation/InitializerSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/InitializerSemanticModel.cs
index 0047859ce91ca..e19182e794531 100644
--- a/src/Compilers/CSharp/Portable/Compilation/InitializerSemanticModel.cs
+++ b/src/Compilers/CSharp/Portable/Compilation/InitializerSemanticModel.cs
@@ -18,8 +18,7 @@ internal sealed class InitializerSemanticModel : MemberSemanticModel
{
// create a SemanticModel for:
// (a) A true field initializer (field = value) of a named type (incl. Enums) OR
- // (b) A constructor initializer (": this(...)" or ": base(...)") OR
- // (c) A parameter default value
+ // (b) A parameter default value
private InitializerSemanticModel(CSharpCompilation compilation,
CSharpSyntaxNode syntax,
Symbol symbol,
@@ -28,6 +27,7 @@ private InitializerSemanticModel(CSharpCompilation compilation,
int speculatedPosition = 0) :
base(compilation, syntax, symbol, rootBinder, parentSemanticModelOpt, speculatedPosition)
{
+ Debug.Assert(!(syntax is ConstructorInitializerSyntax));
}
///
@@ -48,22 +48,6 @@ internal static InitializerSemanticModel Create(CSharpCompilation compilation, C
return new InitializerSemanticModel(compilation, syntax, propertySymbol, rootBinder);
}
- ///
- /// Creates a SemanticModel for a constructor initializer (": this(...)" or ": base(...)").
- ///
- internal static InitializerSemanticModel Create(CSharpCompilation compilation, ConstructorInitializerSyntax syntax, MethodSymbol methodSymbol, Binder rootBinder)
- {
- return new InitializerSemanticModel(compilation, syntax, methodSymbol, rootBinder);
- }
-
- ///
- /// Creates a SemanticModel for a constructor initializer (": base-class(...)").
- ///
- internal static InitializerSemanticModel Create(CSharpCompilation compilation, ArgumentListSyntax syntax, MethodSymbol methodSymbol, Binder rootBinder)
- {
- return new InitializerSemanticModel(compilation, syntax, methodSymbol, rootBinder);
- }
-
///
/// Creates a SemanticModel for a parameter default value.
///
@@ -80,10 +64,7 @@ internal static InitializerSemanticModel CreateSpeculative(SyntaxTreeSemanticMod
{
Debug.Assert(parentSemanticModel != null);
Debug.Assert(syntax != null);
- Debug.Assert(syntax.IsKind(SyntaxKind.EqualsValueClause) ||
- syntax.IsKind(SyntaxKind.ThisConstructorInitializer) ||
- syntax.IsKind(SyntaxKind.BaseConstructorInitializer) ||
- syntax.IsKind(SyntaxKind.ArgumentList));
+ Debug.Assert(syntax.IsKind(SyntaxKind.EqualsValueClause));
Debug.Assert(rootBinder != null);
Debug.Assert(rootBinder.IsSemanticModelBinder);
@@ -158,13 +139,6 @@ internal override BoundNode Bind(Binder binder, CSharpSyntaxNode node, Diagnosti
case SyntaxKind.EnumMemberDeclaration:
equalsValue = ((EnumMemberDeclarationSyntax)node).EqualsValue;
break;
-
- case SyntaxKind.BaseConstructorInitializer:
- case SyntaxKind.ThisConstructorInitializer:
- return binder.BindConstructorInitializer(((ConstructorInitializerSyntax)node).ArgumentList, (MethodSymbol)MemberSymbol, diagnostics);
-
- case SyntaxKind.ArgumentList:
- return binder.BindConstructorInitializer((ArgumentListSyntax)node, (MethodSymbol)MemberSymbol, diagnostics);
}
if (equalsValue != null)
@@ -183,54 +157,36 @@ private BoundEqualsValue BindEqualsValue(Binder binder, EqualsValueClauseSyntax
{
var field = (FieldSymbol)this.MemberSymbol;
var enumField = field as SourceEnumConstantSymbol;
- BoundExpression result;
if ((object)enumField != null)
{
- result = binder.BindEnumConstantInitializer(enumField, equalsValue, diagnostics);
+ return binder.BindEnumConstantInitializer(enumField, equalsValue, diagnostics);
}
else
{
- result = binder.BindVariableOrAutoPropInitializer(equalsValue, RefKind.None, field.GetFieldType(binder.FieldsBeingBound), diagnostics);
+ return binder.BindFieldInitializer(field, equalsValue, diagnostics);
}
- if (result != null)
- {
- return new BoundFieldEqualsValue(equalsValue, field, result);
- }
- break;
}
case SymbolKind.Property:
{
- var property = (PropertySymbol)this.MemberSymbol;
- BoundExpression result = binder.BindVariableOrAutoPropInitializer(equalsValue, RefKind.None, property.Type, diagnostics);
- if (result != null)
- {
- return new BoundPropertyEqualsValue(equalsValue, property, result);
- }
- break;
+ var property = (SourcePropertySymbol)this.MemberSymbol;
+ BoundFieldEqualsValue result = binder.BindFieldInitializer(property.BackingField, equalsValue, diagnostics);
+ return new BoundPropertyEqualsValue(result.Syntax, property, result.Locals, result.Value);
}
case SymbolKind.Parameter:
{
- BoundExpression unusedValueBeforeConversion; // not needed.
var parameter = (ParameterSymbol)this.MemberSymbol;
- BoundExpression result = binder.BindParameterDefaultValue(
+ return binder.BindParameterDefaultValue(
equalsValue,
- parameter.Type,
+ parameter,
diagnostics,
- out unusedValueBeforeConversion);
- if (result != null)
- {
- return new BoundParameterEqualsValue(equalsValue, parameter, result);
- }
- break;
+ out _);
}
default:
throw ExceptionUtilities.UnexpectedValue(this.MemberSymbol.Kind);
}
-
- return null;
}
private bool IsBindableInitializer(CSharpSyntaxNode node)
@@ -257,18 +213,6 @@ private bool IsBindableInitializer(CSharpSyntaxNode node)
internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out SemanticModel speculativeModel)
{
- return TryGetSpeculativeSemanticModelCore(parentModel, position, initializer, out speculativeModel);
- }
-
- internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel)
- {
- return TryGetSpeculativeSemanticModelCore(parentModel, position, constructorInitializer, out speculativeModel);
- }
-
- private bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, CSharpSyntaxNode initializer, out SemanticModel speculativeModel)
- {
- Debug.Assert(initializer is EqualsValueClauseSyntax || initializer is ConstructorInitializerSyntax);
-
var binder = this.GetEnclosingBinder(position);
if (binder == null)
{
@@ -276,26 +220,17 @@ private bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentMo
return false;
}
- switch (initializer.Kind())
- {
- case SyntaxKind.EqualsValueClause:
- binder = new ExecutableCodeBinder(initializer, binder.ContainingMemberOrLambda, binder);
- break;
-
- case SyntaxKind.ThisConstructorInitializer:
- case SyntaxKind.BaseConstructorInitializer:
- ArgumentListSyntax argList = ((ConstructorInitializerSyntax)initializer).ArgumentList;
- if (argList != null)
- {
- binder = new ExecutableCodeBinder(argList, binder.ContainingMemberOrLambda, binder);
- }
- break;
- }
-
+ binder = new ExecutableCodeBinder(initializer, binder.ContainingMemberOrLambda, binder);
speculativeModel = CreateSpeculative(parentModel, this.MemberSymbol, initializer, binder, position);
return true;
}
+ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel)
+ {
+ speculativeModel = null;
+ return false;
+ }
+
internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out SemanticModel speculativeModel)
{
speculativeModel = null;
diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs
index aa8c49b2da1d6..5e8aaf56d35fe 100644
--- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs
+++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs
@@ -242,6 +242,14 @@ private static Binder GetEnclosingBinderInternalWithinRoot(SyntaxNode node, int
{
binder = rootBinder.GetBinder(current);
}
+ else if (kind == SyntaxKind.ThisConstructorInitializer || kind == SyntaxKind.BaseConstructorInitializer)
+ {
+ binder = rootBinder.GetBinder(current);
+ }
+ else if (kind == SyntaxKind.ConstructorDeclaration)
+ {
+ binder = rootBinder.GetBinder(current);
+ }
else if ((current as ExpressionSyntax).IsValidScopeDesignator())
{
binder = rootBinder.GetBinder(current);
@@ -1291,6 +1299,15 @@ private ImmutableArray GuardedGetBoundNodesFromMap(CSharpSyntaxNode n
return _guardedNodeMap.TryGetValue(node, out result) ? result : default(ImmutableArray);
}
+ ///
+ /// Internal for test purposes only
+ ///
+ internal ImmutableArray TestOnlyTryGetBoundNodesFromMap(CSharpSyntaxNode node)
+ {
+ ImmutableArray result;
+ return _guardedNodeMap.TryGetValue(node, out result) ? result : default(ImmutableArray);
+ }
+
// Adds every syntax/bound pair in a tree rooted at the given bound node to the map, and the
// performs a lookup of the given syntax node in the map.
private ImmutableArray GuardedAddBoundTreeAndGetBoundNodeFromMap(CSharpSyntaxNode syntax, BoundNode bound)
@@ -1315,7 +1332,7 @@ private ImmutableArray GuardedAddBoundTreeAndGetBoundNodeFromMap(CSha
return _guardedNodeMap.TryGetValue(syntax, out result) ? result : default(ImmutableArray);
}
- internal void UnguardedAddBoundTreeForStandaloneSyntax(SyntaxNode syntax, BoundNode bound)
+ protected void UnguardedAddBoundTreeForStandaloneSyntax(SyntaxNode syntax, BoundNode bound)
{
using (_nodeMapLock.DisposableWrite())
{
@@ -1374,6 +1391,14 @@ private CSharpSyntaxNode GetBindingRoot(CSharpSyntaxNode node)
{
return current;
}
+
+ switch (current.Kind())
+ {
+ case SyntaxKind.ThisConstructorInitializer:
+ case SyntaxKind.BaseConstructorInitializer:
+ case SyntaxKind.ArrowExpressionClause:
+ return current;
+ }
}
return this.Root;
@@ -1807,13 +1832,11 @@ internal protected virtual CSharpSyntaxNode GetBindableSyntaxNode(CSharpSyntaxNo
case SyntaxKind.SetAccessorDeclaration:
case SyntaxKind.AddAccessorDeclaration:
case SyntaxKind.RemoveAccessorDeclaration:
- return ((AccessorDeclarationSyntax)node).Body;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.ConstructorDeclaration:
case SyntaxKind.DestructorDeclaration:
case SyntaxKind.OperatorDeclaration:
case SyntaxKind.ConversionOperatorDeclaration:
- return ((BaseMethodDeclarationSyntax)node).Body;
case SyntaxKind.GlobalStatement:
return node;
}
@@ -1877,6 +1900,7 @@ internal protected virtual CSharpSyntaxNode GetBindableSyntaxNode(CSharpSyntaxNo
!(node is OrderingSyntax) &&
!(node is JoinIntoClauseSyntax) &&
!(node is QueryContinuationSyntax) &&
+ !(node is ConstructorInitializerSyntax) &&
!(node is ArrowExpressionClauseSyntax))
{
return GetBindableSyntaxNode(parent);
@@ -2007,9 +2031,9 @@ public override BoundStatement BindStatement(StatementSyntax node, DiagnosticBag
return synthesizedStatement;
}
- ImmutableArray boundNodes = _semanticModel.GuardedGetBoundNodesFromMap(node);
+ BoundNode boundNode = TryGetBoundNodeFromMap(node);
- if (boundNodes.IsDefaultOrEmpty)
+ if (boundNode == null)
{
// Not bound already. Bind it. It will get added to the cache later by a MemberSemanticModel.NodeMapBuilder.
var statement = base.BindStatement(node, diagnostics);
@@ -2023,11 +2047,36 @@ public override BoundStatement BindStatement(StatementSyntax node, DiagnosticBag
return statement;
}
- else
+
+ return (BoundStatement)boundNode;
+ }
+
+ private BoundNode TryGetBoundNodeFromMap(CSharpSyntaxNode node)
+ {
+ ImmutableArray boundNodes = _semanticModel.GuardedGetBoundNodesFromMap(node);
+
+ if (!boundNodes.IsDefaultOrEmpty)
{
// Already bound. Return the top-most bound node associated with the statement.
- return (BoundStatement)boundNodes[0];
+ return boundNodes[0];
}
+
+ return null;
+ }
+
+ public override BoundNode BindMethodBody(CSharpSyntaxNode node, DiagnosticBag diagnostics)
+ {
+ return TryGetBoundNodeFromMap(node) ?? base.BindMethodBody(node, diagnostics);
+ }
+
+ internal override BoundExpressionStatement BindConstructorInitializer(ConstructorInitializerSyntax node, DiagnosticBag diagnostics)
+ {
+ return (BoundExpressionStatement)TryGetBoundNodeFromMap(node) ?? base.BindConstructorInitializer(node, diagnostics);
+ }
+
+ internal override BoundBlock BindExpressionBodyAsBlock(ArrowExpressionClauseSyntax node, DiagnosticBag diagnostics)
+ {
+ return (BoundBlock)TryGetBoundNodeFromMap(node) ?? base.BindExpressionBodyAsBlock(node, diagnostics);
}
}
}
diff --git a/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs
index 4c839ecbbe228..60e5ee6770a62 100644
--- a/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs
+++ b/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs
@@ -19,32 +19,45 @@ private MethodBodySemanticModel(CSharpCompilation compilation, Symbol owner, Bin
}
///
- /// Creates a SemanticModel that creates and owns the ExecutableCodeBinder for the method of which it is a model.
+ /// Creates a SemanticModel for the method.
///
- internal static MethodBodySemanticModel Create(CSharpCompilation compilation, MethodSymbol owner, Binder rootBinder, CSharpSyntaxNode syntax)
+ internal static MethodBodySemanticModel Create(CSharpCompilation compilation, MethodSymbol owner, ExecutableCodeBinder executableCodeBinder,
+ CSharpSyntaxNode syntax, BoundNode boundNode = null)
{
- var executableCodeBinder = new ExecutableCodeBinder(syntax, owner, rootBinder);
- return new MethodBodySemanticModel(compilation, owner, executableCodeBinder, syntax);
- }
+ var result = new MethodBodySemanticModel(compilation, owner, executableCodeBinder, syntax);
- internal override BoundNode Bind(Binder binder, CSharpSyntaxNode node, DiagnosticBag diagnostics)
- {
- if (node.Kind() == SyntaxKind.ArrowExpressionClause)
+ if (boundNode != null)
{
- return binder.BindExpressionBodyAsBlock((ArrowExpressionClauseSyntax)node, diagnostics);
+ result.UnguardedAddBoundTreeForStandaloneSyntax(syntax, boundNode);
}
- return base.Bind(binder, node, diagnostics);
+
+ return result;
}
- internal override BoundNode GetBoundRoot()
+ internal override BoundNode Bind(Binder binder, CSharpSyntaxNode node, DiagnosticBag diagnostics)
{
- CSharpSyntaxNode root = this.Root;
- if (root.Kind() == SyntaxKind.ArrowExpressionClause)
+ switch (node.Kind())
{
- root = ((ArrowExpressionClauseSyntax)root).Expression;
- return GetUpperBoundNode(GetBindableSyntaxNode(root));
+ case SyntaxKind.ArrowExpressionClause:
+ return binder.BindExpressionBodyAsBlock((ArrowExpressionClauseSyntax)node, diagnostics);
+
+ case SyntaxKind.BaseConstructorInitializer:
+ case SyntaxKind.ThisConstructorInitializer:
+ return binder.BindConstructorInitializer((ConstructorInitializerSyntax)node, diagnostics);
+
+ case SyntaxKind.MethodDeclaration:
+ case SyntaxKind.ConversionOperatorDeclaration:
+ case SyntaxKind.OperatorDeclaration:
+ case SyntaxKind.ConstructorDeclaration:
+ case SyntaxKind.DestructorDeclaration:
+ case SyntaxKind.GetAccessorDeclaration:
+ case SyntaxKind.SetAccessorDeclaration:
+ case SyntaxKind.AddAccessorDeclaration:
+ case SyntaxKind.RemoveAccessorDeclaration:
+ return binder.BindMethodBody(node, diagnostics);
}
- return base.GetBoundRoot();
+
+ return base.Bind(binder, node, diagnostics);
}
///
@@ -73,6 +86,19 @@ internal static MethodBodySemanticModel CreateSpeculative(SyntaxTreeSemanticMode
return new MethodBodySemanticModel(parentSemanticModel.Compilation, owner, rootBinder, syntax, parentSemanticModel, position);
}
+ ///
+ /// Creates a speculative SemanticModel for a constructor initializer that did not appear in the original source code.
+ ///
+ internal static MethodBodySemanticModel CreateSpeculative(SyntaxTreeSemanticModel parentSemanticModel, MethodSymbol owner, ConstructorInitializerSyntax syntax, Binder rootBinder, int position)
+ {
+ Debug.Assert(parentSemanticModel != null);
+ Debug.Assert(syntax != null);
+ Debug.Assert(rootBinder != null);
+ Debug.Assert(rootBinder.IsSemanticModelBinder);
+
+ return new MethodBodySemanticModel(parentSemanticModel.Compilation, owner, rootBinder, syntax, parentSemanticModel, position);
+ }
+
internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out SemanticModel speculativeModel)
{
// CONSIDER: Do we want to ensure that speculated method and the original method have identical signatures?
@@ -150,6 +176,18 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode
internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel)
{
+ if ((MemberSymbol as MethodSymbol)?.MethodKind == MethodKind.Constructor)
+ {
+ var binder = this.GetEnclosingBinder(position);
+ if (binder != null)
+ {
+ var methodSymbol = (MethodSymbol)this.MemberSymbol;
+ binder = new ExecutableCodeBinder(constructorInitializer, methodSymbol, binder);
+ speculativeModel = CreateSpeculative(parentModel, methodSymbol, constructorInitializer, binder, position);
+ return true;
+ }
+ }
+
speculativeModel = null;
return false;
}
diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs
index dc8d4cbb16aa5..0b32947a42722 100644
--- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs
+++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs
@@ -163,7 +163,24 @@ internal override Binder GetEnclosingBinderInternal(int position)
internal override IOperation GetOperationWorker(CSharpSyntaxNode node, CancellationToken cancellationToken)
{
- var model = this.GetMemberModel(node);
+ MemberSemanticModel model;
+
+ switch (node)
+ {
+ case ConstructorDeclarationSyntax constructor:
+ model = (constructor.HasAnyBody() || constructor.Initializer != null) ? GetOrAddModel(node) : null;
+ break;
+ case BaseMethodDeclarationSyntax method:
+ model = method.HasAnyBody() ? GetOrAddModel(node) : null;
+ break;
+ case AccessorDeclarationSyntax accessor:
+ model = (accessor.Body != null || accessor.ExpressionBody != null) ? GetOrAddModel(node) : null;
+ break;
+ default:
+ model = this.GetMemberModel(node);
+ break;
+ }
+
if (model != null)
{
return model.GetOperationWorker(node, cancellationToken);
@@ -760,25 +777,37 @@ internal override MemberSemanticModel GetMemberModel(SyntaxNode node)
{
var methodDecl = (BaseMethodDeclarationSyntax)memberDecl;
var expressionBody = methodDecl.GetExpressionBodySyntax();
- return GetOrAddModelIfContains(expressionBody, span) ??
- GetOrAddModelIfContains(methodDecl.Body, span);
+ return (expressionBody?.FullSpan.Contains(span) == true || methodDecl.Body?.FullSpan.Contains(span) == true) ?
+ GetOrAddModel(methodDecl) : null;
}
case SyntaxKind.ConstructorDeclaration:
{
ConstructorDeclarationSyntax constructorDecl = (ConstructorDeclarationSyntax)memberDecl;
var expressionBody = constructorDecl.GetExpressionBodySyntax();
- return GetOrAddModelIfContains(constructorDecl.Initializer, span) ??
- GetOrAddModelIfContains(expressionBody, span) ??
- GetOrAddModelIfContains(constructorDecl.Body, span);
+ return (constructorDecl.Initializer?.FullSpan.Contains(span) == true ||
+ expressionBody?.FullSpan.Contains(span) == true ||
+ constructorDecl.Body?.FullSpan.Contains(span) == true) ?
+ GetOrAddModel(constructorDecl) : null;
}
case SyntaxKind.DestructorDeclaration:
{
DestructorDeclarationSyntax destructorDecl = (DestructorDeclarationSyntax)memberDecl;
var expressionBody = destructorDecl.GetExpressionBodySyntax();
- return GetOrAddModelIfContains(expressionBody, span) ??
- GetOrAddModelIfContains(destructorDecl.Body, span);
+ return (expressionBody?.FullSpan.Contains(span) == true || destructorDecl.Body?.FullSpan.Contains(span) == true) ?
+ GetOrAddModel(destructorDecl) : null;
+ }
+
+ case SyntaxKind.GetAccessorDeclaration:
+ case SyntaxKind.SetAccessorDeclaration:
+ case SyntaxKind.AddAccessorDeclaration:
+ case SyntaxKind.RemoveAccessorDeclaration:
+ // NOTE: not UnknownAccessorDeclaration since there's no corresponding method symbol from which to build a member model.
+ {
+ var accessorDecl = (AccessorDeclarationSyntax)memberDecl;
+ return (accessorDecl.ExpressionBody?.FullSpan.Contains(span) == true || accessorDecl.Body?.FullSpan.Contains(span) == true) ?
+ GetOrAddModel(accessorDecl) : null;
}
case SyntaxKind.IndexerDeclaration:
@@ -817,14 +846,6 @@ internal override MemberSemanticModel GetMemberModel(SyntaxNode node)
GetOrAddModelIfContains(propertyDecl.ExpressionBody, span);
}
- case SyntaxKind.GetAccessorDeclaration:
- case SyntaxKind.SetAccessorDeclaration:
- case SyntaxKind.AddAccessorDeclaration:
- case SyntaxKind.RemoveAccessorDeclaration:
- // NOTE: not UnknownAccessorDeclaration since there's no corresponding method symbol from which to build a member model.
- return GetOrAddModelIfContains(((AccessorDeclarationSyntax)memberDecl).ExpressionBody, span) ??
- GetOrAddModelIfContains(((AccessorDeclarationSyntax)memberDecl).Body, span);
-
case SyntaxKind.GlobalStatement:
return GetOrAddModel(memberDecl);
@@ -839,6 +860,11 @@ internal override MemberSemanticModel GetMemberModel(SyntaxNode node)
return null;
}
+ ///
+ /// Internal for test purposes only
+ ///
+ internal ImmutableDictionary TestOnlyMemberModels => _memberModels;
+
private MemberSemanticModel GetOrAddModelForAttribute(AttributeSyntax attribute)
{
MemberSemanticModel containing = attribute.Parent != null ? GetMemberModel(attribute.Parent) : null;
@@ -923,6 +949,11 @@ private MemberSemanticModel GetOrAddModel(CSharpSyntaxNode node)
var createMemberModelFunction = _createMemberModelFunction ??
(_createMemberModelFunction = this.CreateMemberModel);
+ return GetOrAddModel(node, createMemberModelFunction);
+ }
+
+ internal MemberSemanticModel GetOrAddModel(CSharpSyntaxNode node, Func createMemberModelFunction)
+ {
return ImmutableInterlocked.GetOrAdd(ref _memberModels, node, createMemberModelFunction);
}
@@ -931,41 +962,55 @@ private MemberSemanticModel GetOrAddModel(CSharpSyntaxNode node)
// (although we try to minimize such cases). In such cases, null is returned.
private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node)
{
- var outer = _binderFactory.GetBinder(node);
-
+ BinderFlags additionalFlags = BinderFlags.None;
if (this.IgnoresAccessibility)
{
- outer = outer.WithAdditionalFlags(BinderFlags.IgnoreAccessibility);
+ additionalFlags = BinderFlags.IgnoreAccessibility;
}
+
+ Binder defaultOuter() => _binderFactory.GetBinder(node).WithAdditionalFlags(additionalFlags);
switch (node.Kind())
{
- case SyntaxKind.Block:
-
- MemberDeclarationSyntax memberDecl;
- AccessorDeclarationSyntax accessorDecl;
- if ((memberDecl = node.Parent as MemberDeclarationSyntax) != null)
+ case SyntaxKind.MethodDeclaration:
+ case SyntaxKind.ConversionOperatorDeclaration:
+ case SyntaxKind.OperatorDeclaration:
+ case SyntaxKind.ConstructorDeclaration:
+ case SyntaxKind.DestructorDeclaration:
{
+ var memberDecl = (MemberDeclarationSyntax)node;
var symbol = (SourceMemberMethodSymbol)GetDeclaredSymbol(memberDecl);
- if ((object)symbol == null)
+ ExecutableCodeBinder binder = symbol?.TryGetBodyBinder(_binderFactory, additionalFlags);
+
+ if (binder == null)
+ {
return null;
+ }
- return MethodBodySemanticModel.Create(this.Compilation, symbol, outer, memberDecl);
+ return MethodBodySemanticModel.Create(this.Compilation, symbol, binder, memberDecl);
}
- else if ((accessorDecl = node.Parent as AccessorDeclarationSyntax) != null)
+ case SyntaxKind.GetAccessorDeclaration:
+ case SyntaxKind.SetAccessorDeclaration:
+ case SyntaxKind.AddAccessorDeclaration:
+ case SyntaxKind.RemoveAccessorDeclaration:
{
+ var accessorDecl = (AccessorDeclarationSyntax)node;
var symbol = (SourceMemberMethodSymbol)GetDeclaredSymbol(accessorDecl);
- if ((object)symbol == null)
+ ExecutableCodeBinder binder = symbol?.TryGetBodyBinder(_binderFactory, additionalFlags);
+
+ if (binder == null)
+ {
return null;
+ }
- return MethodBodySemanticModel.Create(this.Compilation, symbol, outer, accessorDecl);
- }
- else
- {
- Debug.Assert(false, "Unexpected node: " + node.Parent);
- return null;
+ return MethodBodySemanticModel.Create(this.Compilation, symbol, binder, accessorDecl);
}
+ case SyntaxKind.Block:
+ // Don't throw, just use for the assert
+ ExceptionUtilities.UnexpectedValue(node.Parent);
+ break;
+
case SyntaxKind.EqualsValueClause:
switch (node.Parent.Kind())
{
@@ -979,7 +1024,7 @@ private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node)
variableDecl, //pass in the entire field initializer to permit region analysis.
fieldSymbol,
//if we're in regular C#, then insert an extra binder to perform field initialization checks
- GetFieldOrPropertyInitializerBinder(fieldSymbol, outer, variableDecl.Initializer));
+ GetFieldOrPropertyInitializerBinder(fieldSymbol, defaultOuter(), variableDecl.Initializer));
}
case SyntaxKind.PropertyDeclaration:
@@ -990,7 +1035,7 @@ private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node)
this.Compilation,
propertyDecl,
propertySymbol,
- GetFieldOrPropertyInitializerBinder(propertySymbol.BackingField, outer, propertyDecl.Initializer));
+ GetFieldOrPropertyInitializerBinder(propertySymbol.BackingField, defaultOuter(), propertyDecl.Initializer));
}
case SyntaxKind.Parameter:
@@ -1007,7 +1052,7 @@ private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node)
this.Compilation,
parameterDecl,
parameterSymbol,
- outer.CreateBinderForParameterDefaultValue(parameterSymbol, (EqualsValueClauseSyntax)node));
+ defaultOuter().CreateBinderForParameterDefaultValue(parameterSymbol, (EqualsValueClauseSyntax)node));
}
case SyntaxKind.EnumMemberDeclaration:
@@ -1021,7 +1066,7 @@ private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node)
this.Compilation,
enumDecl,
enumSymbol,
- GetFieldOrPropertyInitializerBinder(enumSymbol, outer, enumDecl.EqualsValue));
+ GetFieldOrPropertyInitializerBinder(enumSymbol, defaultOuter(), enumDecl.EqualsValue));
}
default:
throw ExceptionUtilities.UnexpectedValue(node.Parent.Kind());
@@ -1030,8 +1075,6 @@ private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node)
case SyntaxKind.ArrowExpressionClause:
{
SourceMemberMethodSymbol symbol = null;
- MemberDeclarationSyntax memberSyntax;
- AccessorDeclarationSyntax accessorSyntax;
var exprDecl = (ArrowExpressionClauseSyntax)node;
@@ -1039,24 +1082,20 @@ private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node)
{
symbol = (SourceMemberMethodSymbol)GetDeclaredSymbol(exprDecl);
}
- else if ((memberSyntax = node.Parent as MemberDeclarationSyntax) != null)
- {
- symbol = (SourceMemberMethodSymbol)GetDeclaredSymbol(memberSyntax);
- }
- else if ((accessorSyntax = node.Parent as AccessorDeclarationSyntax) != null)
- {
- symbol = (SourceMemberMethodSymbol)GetDeclaredSymbol(accessorSyntax);
- }
else
{
// Don't throw, just use for the assert
ExceptionUtilities.UnexpectedValue(node.Parent);
}
- if ((object)symbol == null)
+ ExecutableCodeBinder binder = symbol?.TryGetBodyBinder(_binderFactory, additionalFlags);
+
+ if (binder == null)
+ {
return null;
- return MethodBodySemanticModel.Create(
- _compilation, symbol, outer.WithContainingMemberOrLambda(symbol), exprDecl);
+ }
+
+ return MethodBodySemanticModel.Create(_compilation, symbol, binder, exprDecl);
}
case SyntaxKind.GlobalStatement:
@@ -1083,39 +1122,14 @@ private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node)
return MethodBodySemanticModel.Create(
this.Compilation,
scriptInitializer,
- new ScriptLocalScopeBinder(_globalStatementLabels, outer),
+ new ExecutableCodeBinder(node, scriptInitializer, new ScriptLocalScopeBinder(_globalStatementLabels, defaultOuter())),
node);
}
}
break;
- case SyntaxKind.BaseConstructorInitializer:
- case SyntaxKind.ThisConstructorInitializer:
- {
- var constructorDecl = (ConstructorDeclarationSyntax)node.Parent;
- var constructorSymbol = (SourceMemberMethodSymbol)GetDeclaredSymbol(constructorDecl);
- if ((object)constructorSymbol == null)
- return null;
-
- // insert an extra binder to perform constructor initialization checks
- // Handle scoping for possible pattern variables declared in the initializer
- Binder initializerBinder = outer.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.ConstructorInitializer, constructorSymbol);
- ArgumentListSyntax argumentList = ((ConstructorInitializerSyntax)node).ArgumentList;
-
- if (argumentList != null)
- {
- initializerBinder = new ExecutableCodeBinder(argumentList, constructorSymbol, initializerBinder);
- }
-
- return InitializerSemanticModel.Create(
- this.Compilation,
- (ConstructorInitializerSyntax)node,
- constructorSymbol,
- initializerBinder);
- }
-
case SyntaxKind.Attribute:
- return CreateModelForAttribute(outer, (AttributeSyntax)node);
+ return CreateModelForAttribute(defaultOuter(), (AttributeSyntax)node);
}
return null;
diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
index 898d0d7e41b9b..bc35982a12cb6 100644
--- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
+++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
@@ -565,7 +565,7 @@ private void CompileNamedType(NamedTypeSymbol containingType)
if (_moduleBeingBuiltOpt != null && !hasStaticConstructor && !processedStaticInitializers.BoundInitializers.IsDefaultOrEmpty)
{
Debug.Assert(processedStaticInitializers.BoundInitializers.All((init) =>
- (init.Kind == BoundKind.FieldInitializer) && !((BoundFieldInitializer)init).Field.IsMetadataConstant));
+ (init.Kind == BoundKind.FieldEqualsValue) && !((BoundFieldEqualsValue)init).Field.IsMetadataConstant));
MethodSymbol method = new SynthesizedStaticConstructor(sourceTypeSymbol);
if (PassesFilter(_filterOpt, method))
@@ -910,16 +910,19 @@ private void CompileMethod(
// initializers that have been analyzed but not yet lowered.
BoundStatementList analyzedInitializers = null;
-
+ (SyntaxNode Syntax, BoundNode Body, ExecutableCodeBinder Binder) forSemanticModel = default;
ImportChain importChain = null;
var hasTrailingExpression = false;
if (methodSymbol.IsScriptConstructor)
{
+ Debug.Assert(methodSymbol.IsImplicitlyDeclared);
body = new BoundBlock(methodSymbol.GetNonNullSyntaxNode(), ImmutableArray.Empty, ImmutableArray.Empty) { WasCompilerGenerated = true };
}
else if (methodSymbol.IsScriptInitializer)
{
+ Debug.Assert(methodSymbol.IsImplicitlyDeclared);
+
// rewrite top-level statements and script variable declarations to a list of statements and assignments, respectively:
var initializerStatements = InitializerRewriter.RewriteScriptInitializer(processedInitializers.BoundInitializers, (SynthesizedInteractiveInitializerMethod)methodSymbol, out hasTrailingExpression);
@@ -937,7 +940,7 @@ private void CompileMethod(
includeInitializersInBody = !processedInitializers.BoundInitializers.IsDefaultOrEmpty &&
!HasThisConstructorInitializer(methodSymbol);
- body = BindMethodBody(methodSymbol, compilationState, diagsForCurrentMethod, out importChain, out originalBodyNested);
+ body = BindMethodBody(methodSymbol, compilationState, diagsForCurrentMethod, out importChain, out originalBodyNested, out forSemanticModel);
// 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
@@ -1018,17 +1021,32 @@ private void CompileMethod(
if (diagsWritten && !methodSymbol.IsImplicitlyDeclared && _compilation.EventQueue != null)
{
- var lazySemanticModel = body == null ? null : new Lazy(() =>
+ Lazy lazySemanticModel = null;
+
+ if (body != null)
{
- var syntax = body.Syntax;
- var semanticModel = (CSharpSemanticModel)_compilation.GetSemanticModel(syntax.SyntaxTree);
- var memberModel = semanticModel.GetMemberModel(syntax);
- if (memberModel != null)
+ (SyntaxNode Syntax, BoundNode Body, ExecutableCodeBinder Binder) forSemanticModelToUseInLambda = forSemanticModel;
+
+ lazySemanticModel = new Lazy(() =>
{
- memberModel.UnguardedAddBoundTreeForStandaloneSyntax(syntax, body);
- }
- return semanticModel;
- });
+ var syntax = body.Syntax;
+ var semanticModel = (SyntaxTreeSemanticModel)_compilation.GetSemanticModel(syntax.SyntaxTree);
+
+ if (forSemanticModelToUseInLambda.Syntax != null)
+ {
+ semanticModel.GetOrAddModel((CSharpSyntaxNode)forSemanticModelToUseInLambda.Syntax,
+ (rootSyntax) =>
+ {
+ Debug.Assert(rootSyntax == forSemanticModelToUseInLambda.Syntax);
+ return MethodBodySemanticModel.Create(_compilation, methodSymbol,
+ forSemanticModelToUseInLambda.Binder, rootSyntax,
+ forSemanticModelToUseInLambda.Body);
+ });
+ }
+
+ return semanticModel;
+ });
+ }
_compilation.EventQueue.TryEnqueue(new SymbolDeclaredCompilationEvent(_compilation, methodSymbol, lazySemanticModel));
}
@@ -1547,23 +1565,25 @@ private static void GetStateMachineSlotDebugInfo(
// NOTE: can return null if the method has no body.
internal static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationState compilationState, DiagnosticBag diagnostics)
{
- ImportChain importChain;
- bool originalBodyNested;
- return BindMethodBody(method, compilationState, diagnostics, out importChain, out originalBodyNested);
+ return BindMethodBody(method, compilationState, diagnostics, out _, out _, out _);
}
// NOTE: can return null if the method has no body.
- private static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationState compilationState, DiagnosticBag diagnostics, out ImportChain importChain, out bool originalBodyNested)
+ private static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationState compilationState, DiagnosticBag diagnostics,
+ out ImportChain importChain, out bool originalBodyNested,
+ out (SyntaxNode Syntax, BoundNode Body, ExecutableCodeBinder Binder) forSemanticModel)
{
originalBodyNested = false;
importChain = null;
+ forSemanticModel = default;
BoundBlock body;
var sourceMethod = method as SourceMemberMethodSymbol;
if ((object)sourceMethod != null)
{
- var constructorSyntax = sourceMethod.SyntaxNode as ConstructorDeclarationSyntax;
+ CSharpSyntaxNode syntaxNode = sourceMethod.SyntaxNode;
+ var constructorSyntax = syntaxNode as ConstructorDeclarationSyntax;
// Static constructor can't have any this/base call
if (method.MethodKind == MethodKind.StaticConstructor &&
@@ -1575,9 +1595,11 @@ private static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSta
constructorSyntax.Identifier.ValueText);
}
+ ExecutableCodeBinder bodyBinder = sourceMethod.TryGetBodyBinder();
+
if (sourceMethod.IsExtern)
{
- if (sourceMethod.BodySyntax == null && constructorSyntax?.Initializer == null)
+ if (bodyBinder == null)
{
// Generate warnings only if we are not generating ERR_ExternHasBody or ERR_ExternHasConstructorInitializer errors
GenerateExternalMethodWarnings(sourceMethod, diagnostics);
@@ -1592,23 +1614,53 @@ private static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSta
return null;
}
- var compilation = method.DeclaringCompilation;
- var factory = compilation.GetBinderFactory(sourceMethod.SyntaxTree);
+ if (bodyBinder != null)
+ {
+ importChain = bodyBinder.ImportChain;
+ bodyBinder.ValidateIteratorMethods(diagnostics);
- var bodySyntax = sourceMethod.BodySyntax;
+ BoundNode methodBody = bodyBinder.BindMethodBody(syntaxNode, diagnostics);
+ forSemanticModel = (syntaxNode, methodBody, bodyBinder);
- if (bodySyntax != null)
- {
- var inMethodBinder = factory.GetBinder(bodySyntax);
- var binder = new ExecutableCodeBinder(bodySyntax, sourceMethod, inMethodBinder);
- importChain = binder.ImportChain;
+ switch (methodBody.Kind)
+ {
+ case BoundKind.ConstructorMethodBody:
+ var constructor = (BoundConstructorMethodBody)methodBody;
+ body = constructor.BlockBody ?? constructor.ExpressionBody;
+
+ if (constructor.Initializer != null)
+ {
+ ReportCtorInitializerCycles(method, constructor.Initializer.Expression, compilationState, diagnostics);
+
+ if (body == null)
+ {
+ body = new BoundBlock(constructor.Syntax, constructor.Locals, ImmutableArray.Create(constructor.Initializer));
+ }
+ else
+ {
+ body = new BoundBlock(constructor.Syntax, constructor.Locals, ImmutableArray.Create(constructor.Initializer, body));
+ originalBodyNested = true;
+ }
+
+ return body;
+ }
+ else
+ {
+ Debug.Assert(constructor.Locals.IsEmpty);
+ }
+ break;
- binder.ValidateIteratorMethods(diagnostics);
+ case BoundKind.NonConstructorMethodBody:
+ var nonConstructor = (BoundNonConstructorMethodBody)methodBody;
+ body = nonConstructor.BlockBody ?? nonConstructor.ExpressionBody;
+ break;
- body = bodySyntax.Kind() == SyntaxKind.Block
- ? binder.BindEmbeddedBlock((BlockSyntax)bodySyntax, diagnostics)
- : binder.BindExpressionBodyAsBlock(
- (ArrowExpressionClauseSyntax)bodySyntax, diagnostics);
+ case BoundKind.Block:
+ body = (BoundBlock)methodBody;
+ break;
+ default:
+ throw ExceptionUtilities.UnexpectedValue(methodBody.Kind);
+ }
}
else
{
@@ -1632,7 +1684,7 @@ private static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSta
return MethodBodySynthesizer.ConstructDestructorBody(method, body);
}
- var constructorInitializer = BindConstructorInitializerIfAny(method, compilationState, diagnostics);
+ var constructorInitializer = BindImplicitConstructorInitializerIfAny(method, compilationState, diagnostics);
ImmutableArray statements;
if (constructorInitializer == null)
@@ -1657,22 +1709,17 @@ private static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSta
return BoundBlock.SynthesizedNoLocals(method.GetNonNullSyntaxNode(), statements);
}
- private static BoundStatement BindConstructorInitializerIfAny(MethodSymbol method, TypeCompilationState compilationState, DiagnosticBag diagnostics)
+ private static BoundStatement BindImplicitConstructorInitializerIfAny(MethodSymbol method, TypeCompilationState compilationState, DiagnosticBag diagnostics)
{
// delegates have constructors but not constructor initializers
if (method.MethodKind == MethodKind.Constructor && !method.ContainingType.IsDelegateType() && !method.IsExtern)
{
var compilation = method.DeclaringCompilation;
- var initializerInvocation = BindConstructorInitializer(method, diagnostics, compilation);
+ var initializerInvocation = BindImplicitConstructorInitializer(method, diagnostics, compilation);
if (initializerInvocation != null)
{
- var ctorCall = initializerInvocation as BoundCall;
- if (ctorCall != null && !ctorCall.HasAnyErrors && ctorCall.Method != method && ctorCall.Method.ContainingType == method.ContainingType)
- {
- // Detect and report indirect cycles in the ctor-initializer call graph.
- compilationState.ReportCtorInitializerCycles(method, ctorCall.Method, ctorCall.Syntax, diagnostics);
- }
+ ReportCtorInitializerCycles(method, initializerInvocation, compilationState, diagnostics);
// Base WasCompilerGenerated state off of whether constructor is implicitly declared, this will ensure proper instrumentation.
var constructorInitializer = new BoundExpressionStatement(initializerInvocation.Syntax, initializerInvocation) { WasCompilerGenerated = method.IsImplicitlyDeclared };
@@ -1684,38 +1731,39 @@ private static BoundStatement BindConstructorInitializerIfAny(MethodSymbol metho
return null;
}
+ private static void ReportCtorInitializerCycles(MethodSymbol method, BoundExpression initializerInvocation, TypeCompilationState compilationState, DiagnosticBag diagnostics)
+ {
+ var ctorCall = initializerInvocation as BoundCall;
+ if (ctorCall != null && !ctorCall.HasAnyErrors && ctorCall.Method != method && ctorCall.Method.ContainingType == method.ContainingType)
+ {
+ // Detect and report indirect cycles in the ctor-initializer call graph.
+ compilationState.ReportCtorInitializerCycles(method, ctorCall.Method, ctorCall.Syntax, diagnostics);
+ }
+ }
+
///
- /// Bind the (implicit or explicit) constructor initializer of a constructor symbol.
+ /// Bind the implicit constructor initializer of a constructor symbol.
///
/// Constructor method.
/// Accumulates errors (e.g. access "this" in constructor initializer).
/// Used to retrieve binder.
/// A bound expression for the constructor initializer call.
- internal static BoundExpression BindConstructorInitializer(
+ internal static BoundExpression BindImplicitConstructorInitializer(
MethodSymbol constructor, DiagnosticBag diagnostics, CSharpCompilation compilation)
{
// Note that the base type can be null if we're compiling System.Object in source.
NamedTypeSymbol baseType = constructor.ContainingType.BaseTypeNoUseSiteDiagnostics;
SourceMemberMethodSymbol sourceConstructor = constructor as SourceMemberMethodSymbol;
- ConstructorDeclarationSyntax constructorSyntax = null;
- ArgumentListSyntax initializerArgumentListOpt = null;
- if ((object)sourceConstructor != null)
- {
- constructorSyntax = (ConstructorDeclarationSyntax)sourceConstructor.SyntaxNode;
- if (constructorSyntax.Initializer != null)
- {
- initializerArgumentListOpt = constructorSyntax.Initializer.ArgumentList;
- }
- }
+ Debug.Assert(((ConstructorDeclarationSyntax)sourceConstructor?.SyntaxNode)?.Initializer == null);
- // The common case is that we have no constructor initializer and the type inherits directly from object.
+ // The common case is that the type inherits directly from object.
// Also, we might be trying to generate a constructor for an entirely compiler-generated class such
// as a closure class; in that case it is vexing to try to find a suitable binder for the non-existing
// constructor syntax so that we can do unnecessary overload resolution on the non-existing initializer!
// Simply take the early out: bind directly to the parameterless object ctor rather than attempting
// overload resolution.
- if (initializerArgumentListOpt == null && (object)baseType != null)
+ if ((object)baseType != null)
{
if (baseType.SpecialType == SpecialType.System_Object)
{
@@ -1729,32 +1777,11 @@ internal static BoundExpression BindConstructorInitializer(
}
}
- // Either our base type is not object, or we have an initializer syntax, or both. We're going to
- // need to do overload resolution on the set of constructors of the base type, either on
- // the provided initializer syntax, or on an implicit ": base()" syntax.
-
- // SPEC ERROR: The specification states that if you have the situation
- // SPEC ERROR: class B { ... } class D1 : B {} then the default constructor
- // SPEC ERROR: generated for D1 must call an accessible *parameterless* constructor
- // SPEC ERROR: in B. However, it also states that if you have
- // SPEC ERROR: class B { ... } class D2 : B { D2() {} } or
- // SPEC ERROR: class B { ... } class D3 : B { D3() : base() {} } then
- // SPEC ERROR: the compiler performs *overload resolution* to determine
- // SPEC ERROR: which accessible constructor of B is called. Since B might have
- // SPEC ERROR: a ctor with all optional parameters, overload resolution might
- // SPEC ERROR: succeed even if there is no parameterless constructor. This
- // SPEC ERROR: is unintentionally inconsistent, and the native compiler does not
- // SPEC ERROR: implement this behavior. Rather, we should say in the spec that
- // SPEC ERROR: if there is no ctor in D1, then a ctor is created for you exactly
- // SPEC ERROR: as though you'd said "D1() : base() {}".
- // SPEC ERROR: This is what we now do in Roslyn.
-
// Now, in order to do overload resolution, we're going to need a binder. There are
- // three possible situations:
+ // two possible situations:
//
// class D1 : B { }
// class D2 : B { D2(int x) { } }
- // class D3 : B { D3(int x) : base(x) { } }
//
// In the first case the binder needs to be the binder associated with
// the *body* of D1 because if the base class ctor is protected, we need
@@ -1764,10 +1791,6 @@ internal static BoundExpression BindConstructorInitializer(
// In the second case the binder could be the binder associated with
// the body of D2; since the implicit call to base() will have no arguments
// there is no need to look up "x".
- //
- // In the third case the binder must be the binder that knows about "x"
- // because x is in scope.
-
Binder outerBinder;
if ((object)sourceConstructor == null)
@@ -1778,29 +1801,20 @@ internal static BoundExpression BindConstructorInitializer(
SyntaxToken bodyToken = GetImplicitConstructorBodyToken(containerNode);
outerBinder = compilation.GetBinderFactory(containerNode.SyntaxTree).GetBinder(containerNode, bodyToken.Position);
}
- else if (initializerArgumentListOpt == null)
+ else
{
// We have a ctor in source but no explicit constructor initializer. We can't just use the binder for the
// type containing the ctor because the ctor might be marked unsafe. Use the binder for the parameter list
// as an approximation - the extra symbols won't matter because there are no identifiers to bind.
- outerBinder = compilation.GetBinderFactory(sourceConstructor.SyntaxTree).GetBinder(constructorSyntax.ParameterList);
- }
- else
- {
- outerBinder = compilation.GetBinderFactory(sourceConstructor.SyntaxTree).GetBinder(initializerArgumentListOpt);
+ outerBinder = compilation.GetBinderFactory(sourceConstructor.SyntaxTree).GetBinder(((ConstructorDeclarationSyntax)sourceConstructor.SyntaxNode).ParameterList);
}
// wrap in ConstructorInitializerBinder for appropriate errors
// Handle scoping for possible pattern variables declared in the initializer
Binder initializerBinder = outerBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.ConstructorInitializer, constructor);
- if (initializerArgumentListOpt != null)
- {
- initializerBinder = new ExecutableCodeBinder(initializerArgumentListOpt, constructor, initializerBinder);
- }
-
- return initializerBinder.BindConstructorInitializer(initializerArgumentListOpt, constructor, diagnostics);
+ return initializerBinder.BindConstructorInitializer(null, constructor, diagnostics);
}
private static SyntaxToken GetImplicitConstructorBodyToken(CSharpSyntaxNode containerNode)
diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
index 68f60af8e7f82..bd64a27b43aca 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
@@ -1460,8 +1460,8 @@ internal enum ErrorCode
#region more stragglers for C# 7
ERR_VarInvocationLvalueReserved = 8199,
- ERR_ExpressionVariableInConstructorOrFieldInitializer = 8200,
- ERR_ExpressionVariableInQueryClause = 8201,
+ //ERR_ExpressionVariableInConstructorOrFieldInitializer = 8200,
+ //ERR_ExpressionVariableInQueryClause = 8201,
ERR_PublicSignNetModule = 8202,
ERR_BadAssemblyName = 8203,
ERR_BadAsyncMethodBuilderTaskProperty = 8204,
diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs
index f015dc27af70a..4557a277ac43f 100644
--- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs
+++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs
@@ -157,6 +157,7 @@ internal enum MessageID
IDS_FeatureUnmanagedGenericTypeConstraint = MessageBase + 12739,
IDS_FeatureStackAllocInitializer = MessageBase + 12740,
IDS_FeatureTupleEquality = MessageBase + 12741,
+ IDS_FeatureExpressionVariablesInQueriesAndInitializers = MessageBase + 12742,
}
// Message IDs may refer to strings that need to be localized.
@@ -207,6 +208,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
case MessageID.IDS_FeatureDelegateGenericTypeConstraint: // semantic check
case MessageID.IDS_FeatureUnmanagedGenericTypeConstraint: // semantic check
case MessageID.IDS_FeatureStackAllocInitializer:
+ case MessageID.IDS_FeatureExpressionVariablesInQueriesAndInitializers: // semantic check
return LanguageVersion.CSharp7_3;
// C# 7.2 features.
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs
index f17bf96c1bee3..51db64717fcec 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs
@@ -41,7 +41,7 @@ public static BoundBlock Rewrite(
// we don't analyze synthesized void methods.
if ((method.IsImplicitlyDeclared && !method.IsScriptInitializer) || Analyze(compilation, method, block, diagnostics))
{
- block = AppendImplicitReturn(block, method, (CSharpSyntaxNode)(method as SourceMemberMethodSymbol)?.BodySyntax, originalBodyNested);
+ block = AppendImplicitReturn(block, method, originalBodyNested);
}
}
else if (Analyze(compilation, method, block, diagnostics))
@@ -80,7 +80,7 @@ public static BoundBlock Rewrite(
return block;
}
- private static BoundBlock AppendImplicitReturn(BoundBlock body, MethodSymbol method, CSharpSyntaxNode syntax, bool originalBodyNested)
+ private static BoundBlock AppendImplicitReturn(BoundBlock body, MethodSymbol method, bool originalBodyNested)
{
if (originalBodyNested)
{
@@ -89,30 +89,30 @@ private static BoundBlock AppendImplicitReturn(BoundBlock body, MethodSymbol met
var builder = ArrayBuilder.GetInstance(n);
builder.AddRange(statements, n - 1);
- builder.Add(AppendImplicitReturn((BoundBlock)statements[n - 1], method, syntax));
+ builder.Add(AppendImplicitReturn((BoundBlock)statements[n - 1], method));
return body.Update(body.Locals, ImmutableArray.Empty, builder.ToImmutableAndFree());
}
else
{
- return AppendImplicitReturn(body, method, syntax);
+ return AppendImplicitReturn(body, method);
}
}
// insert the implicit "return" statement at the end of the method body
// Normally, we wouldn't bother attaching syntax trees to compiler-generated nodes, but these
// ones are going to have sequence points.
- internal static BoundBlock AppendImplicitReturn(BoundBlock body, MethodSymbol method, SyntaxNode syntax = null)
+ internal static BoundBlock AppendImplicitReturn(BoundBlock body, MethodSymbol method)
{
Debug.Assert(body != null);
Debug.Assert(method != null);
- if (syntax == null)
- {
- syntax = body.Syntax;
- }
+ SyntaxNode syntax = body.Syntax;
- Debug.Assert(body.WasCompilerGenerated || syntax.IsKind(SyntaxKind.Block) || syntax.IsKind(SyntaxKind.ArrowExpressionClause));
+ Debug.Assert(body.WasCompilerGenerated ||
+ syntax.IsKind(SyntaxKind.Block) ||
+ syntax.IsKind(SyntaxKind.ArrowExpressionClause) ||
+ syntax.IsKind(SyntaxKind.ConstructorDeclaration));
BoundStatement ret = method.IsIterator
? (BoundStatement)BoundYieldBreakStatement.Synthesized(syntax)
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs
index 1eb8c0aaef567..8bc45d838a733 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs
@@ -1182,12 +1182,6 @@ public override BoundNode VisitScope(BoundScope node)
return null;
}
- public override BoundNode VisitFieldInitializer(BoundFieldInitializer node)
- {
- Visit(node.InitialValue);
- return null;
- }
-
public override BoundNode VisitExpressionStatement(BoundExpressionStatement node)
{
VisitRvalue(node.Expression);
@@ -2761,6 +2755,49 @@ public override BoundNode VisitDiscardExpression(BoundDiscardExpression node)
{
return null;
}
+
+ public override BoundNode VisitConstructorMethodBody(BoundConstructorMethodBody node)
+ {
+ Visit(node.Initializer);
+ VisitMethodBodies(node.BlockBody, node.ExpressionBody);
+ return null;
+ }
+
+ public override BoundNode VisitNonConstructorMethodBody(BoundNonConstructorMethodBody node)
+ {
+ VisitMethodBodies(node.BlockBody, node.ExpressionBody);
+ return null;
+ }
+
+ private void VisitMethodBodies(BoundBlock blockBody, BoundBlock expressionBody)
+ {
+ if (blockBody == null)
+ {
+ Visit(expressionBody);
+ return;
+ }
+ else if (expressionBody == null)
+ {
+ Visit(blockBody);
+ return;
+ }
+
+ // In error cases we have two bodies. These are two unrelated pieces of code,
+ // they are not executed one after another. As we don't really know which one the developer
+ // intended to use, we need to visit both. We are going to pretend that there is
+ // an unconditional fork in execution and then we are converging after each body is executed.
+ // For example, if only one body assigns an out parameter, then after visiting both bodies
+ // we should consider that parameter is not definitely assigned.
+ // Note, that today this code is not executed for regular definite assignment analysis. It is
+ // only executed for region analysis.
+ LocalState initialState = this.State.Clone();
+ Visit(blockBody);
+ LocalState afterBlock = this.State;
+ SetState(initialState);
+ Visit(expressionBody);
+
+ IntersectWith(ref this.State, ref afterBlock);
+ }
#endregion visitors
}
}
diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs
index ccaf2220ef328..43980939241f8 100644
--- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs
+++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs
@@ -20,7 +20,6 @@ namespace Microsoft.CodeAnalysis.CSharp
{
internal enum BoundKind: byte
{
- FieldInitializer,
FieldEqualsValue,
PropertyEqualsValue,
ParameterEqualsValue,
@@ -173,6 +172,8 @@ internal enum BoundKind: byte
OutVariablePendingInference,
DeconstructionVariablePendingInference,
OutDeconstructVarPendingInference,
+ NonConstructorMethodBody,
+ ConstructorMethodBody,
}
@@ -198,63 +199,33 @@ protected BoundInitializer(BoundKind kind, SyntaxNode syntax)
}
- internal sealed partial class BoundFieldInitializer : BoundInitializer
+ internal abstract partial class BoundEqualsValue : BoundInitializer
{
- public BoundFieldInitializer(SyntaxNode syntax, FieldSymbol field, BoundExpression initialValue, bool hasErrors = false)
- : base(BoundKind.FieldInitializer, syntax, hasErrors || initialValue.HasErrors())
- {
-
- Debug.Assert(field != null, "Field 'field' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
- Debug.Assert(initialValue != null, "Field 'initialValue' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
-
- this.Field = field;
- this.InitialValue = initialValue;
- }
-
-
- public FieldSymbol Field { get; }
-
- public BoundExpression InitialValue { get; }
-
- public override BoundNode Accept(BoundTreeVisitor visitor)
- {
- return visitor.VisitFieldInitializer(this);
- }
-
- public BoundFieldInitializer Update(FieldSymbol field, BoundExpression initialValue)
- {
- if (field != this.Field || initialValue != this.InitialValue)
- {
- var result = new BoundFieldInitializer(this.Syntax, field, initialValue, this.HasErrors);
- result.WasCompilerGenerated = this.WasCompilerGenerated;
- return result;
- }
- return this;
- }
- }
-
- internal abstract partial class BoundEqualsValue : BoundNode
- {
- protected BoundEqualsValue(BoundKind kind, SyntaxNode syntax, BoundExpression value, bool hasErrors = false)
+ protected BoundEqualsValue(BoundKind kind, SyntaxNode syntax, ImmutableArray locals, BoundExpression value, bool hasErrors = false)
: base(kind, syntax, hasErrors)
{
+ Debug.Assert(!locals.IsDefault, "Field 'locals' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(value != null, "Field 'value' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
+ this.Locals = locals;
this.Value = value;
}
+ public ImmutableArray Locals { get; }
+
public BoundExpression Value { get; }
}
internal sealed partial class BoundFieldEqualsValue : BoundEqualsValue
{
- public BoundFieldEqualsValue(SyntaxNode syntax, FieldSymbol field, BoundExpression value, bool hasErrors = false)
- : base(BoundKind.FieldEqualsValue, syntax, value, hasErrors || value.HasErrors())
+ public BoundFieldEqualsValue(SyntaxNode syntax, FieldSymbol field, ImmutableArray locals, BoundExpression value, bool hasErrors = false)
+ : base(BoundKind.FieldEqualsValue, syntax, locals, value, hasErrors || value.HasErrors())
{
Debug.Assert(field != null, "Field 'field' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
+ Debug.Assert(!locals.IsDefault, "Field 'locals' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(value != null, "Field 'value' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
this.Field = field;
@@ -268,11 +239,11 @@ public override BoundNode Accept(BoundTreeVisitor visitor)
return visitor.VisitFieldEqualsValue(this);
}
- public BoundFieldEqualsValue Update(FieldSymbol field, BoundExpression value)
+ public BoundFieldEqualsValue Update(FieldSymbol field, ImmutableArray locals, BoundExpression value)
{
- if (field != this.Field || value != this.Value)
+ if (field != this.Field || locals != this.Locals || value != this.Value)
{
- var result = new BoundFieldEqualsValue(this.Syntax, field, value, this.HasErrors);
+ var result = new BoundFieldEqualsValue(this.Syntax, field, locals, value, this.HasErrors);
result.WasCompilerGenerated = this.WasCompilerGenerated;
return result;
}
@@ -282,11 +253,12 @@ public BoundFieldEqualsValue Update(FieldSymbol field, BoundExpression value)
internal sealed partial class BoundPropertyEqualsValue : BoundEqualsValue
{
- public BoundPropertyEqualsValue(SyntaxNode syntax, PropertySymbol property, BoundExpression value, bool hasErrors = false)
- : base(BoundKind.PropertyEqualsValue, syntax, value, hasErrors || value.HasErrors())
+ public BoundPropertyEqualsValue(SyntaxNode syntax, PropertySymbol property, ImmutableArray locals, BoundExpression value, bool hasErrors = false)
+ : base(BoundKind.PropertyEqualsValue, syntax, locals, value, hasErrors || value.HasErrors())
{
Debug.Assert(property != null, "Field 'property' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
+ Debug.Assert(!locals.IsDefault, "Field 'locals' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(value != null, "Field 'value' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
this.Property = property;
@@ -300,11 +272,11 @@ public override BoundNode Accept(BoundTreeVisitor visitor)
return visitor.VisitPropertyEqualsValue(this);
}
- public BoundPropertyEqualsValue Update(PropertySymbol property, BoundExpression value)
+ public BoundPropertyEqualsValue Update(PropertySymbol property, ImmutableArray locals, BoundExpression value)
{
- if (property != this.Property || value != this.Value)
+ if (property != this.Property || locals != this.Locals || value != this.Value)
{
- var result = new BoundPropertyEqualsValue(this.Syntax, property, value, this.HasErrors);
+ var result = new BoundPropertyEqualsValue(this.Syntax, property, locals, value, this.HasErrors);
result.WasCompilerGenerated = this.WasCompilerGenerated;
return result;
}
@@ -314,11 +286,12 @@ public BoundPropertyEqualsValue Update(PropertySymbol property, BoundExpression
internal sealed partial class BoundParameterEqualsValue : BoundEqualsValue
{
- public BoundParameterEqualsValue(SyntaxNode syntax, ParameterSymbol parameter, BoundExpression value, bool hasErrors = false)
- : base(BoundKind.ParameterEqualsValue, syntax, value, hasErrors || value.HasErrors())
+ public BoundParameterEqualsValue(SyntaxNode syntax, ParameterSymbol parameter, ImmutableArray locals, BoundExpression value, bool hasErrors = false)
+ : base(BoundKind.ParameterEqualsValue, syntax, locals, value, hasErrors || value.HasErrors())
{
Debug.Assert(parameter != null, "Field 'parameter' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
+ Debug.Assert(!locals.IsDefault, "Field 'locals' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(value != null, "Field 'value' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
this.Parameter = parameter;
@@ -332,11 +305,11 @@ public override BoundNode Accept(BoundTreeVisitor visitor)
return visitor.VisitParameterEqualsValue(this);
}
- public BoundParameterEqualsValue Update(ParameterSymbol parameter, BoundExpression value)
+ public BoundParameterEqualsValue Update(ParameterSymbol parameter, ImmutableArray locals, BoundExpression value)
{
- if (parameter != this.Parameter || value != this.Value)
+ if (parameter != this.Parameter || locals != this.Locals || value != this.Value)
{
- var result = new BoundParameterEqualsValue(this.Syntax, parameter, value, this.HasErrors);
+ var result = new BoundParameterEqualsValue(this.Syntax, parameter, locals, value, this.HasErrors);
result.WasCompilerGenerated = this.WasCompilerGenerated;
return result;
}
@@ -6273,6 +6246,80 @@ public OutDeconstructVarPendingInference Update()
}
}
+ internal abstract partial class BoundMethodBodyBase : BoundNode
+ {
+ protected BoundMethodBodyBase(BoundKind kind, SyntaxNode syntax, BoundBlock blockBody, BoundBlock expressionBody, bool hasErrors = false)
+ : base(kind, syntax, hasErrors)
+ {
+ this.BlockBody = blockBody;
+ this.ExpressionBody = expressionBody;
+ }
+
+
+ public BoundBlock BlockBody { get; }
+
+ public BoundBlock ExpressionBody { get; }
+ }
+
+ internal sealed partial class BoundNonConstructorMethodBody : BoundMethodBodyBase
+ {
+ public BoundNonConstructorMethodBody(SyntaxNode syntax, BoundBlock blockBody, BoundBlock expressionBody, bool hasErrors = false)
+ : base(BoundKind.NonConstructorMethodBody, syntax, blockBody, expressionBody, hasErrors || blockBody.HasErrors() || expressionBody.HasErrors())
+ {
+ }
+
+
+ public override BoundNode Accept(BoundTreeVisitor visitor)
+ {
+ return visitor.VisitNonConstructorMethodBody(this);
+ }
+
+ public BoundNonConstructorMethodBody Update(BoundBlock blockBody, BoundBlock expressionBody)
+ {
+ if (blockBody != this.BlockBody || expressionBody != this.ExpressionBody)
+ {
+ var result = new BoundNonConstructorMethodBody(this.Syntax, blockBody, expressionBody, this.HasErrors);
+ result.WasCompilerGenerated = this.WasCompilerGenerated;
+ return result;
+ }
+ return this;
+ }
+ }
+
+ internal sealed partial class BoundConstructorMethodBody : BoundMethodBodyBase
+ {
+ public BoundConstructorMethodBody(SyntaxNode syntax, ImmutableArray locals, BoundExpressionStatement initializer, BoundBlock blockBody, BoundBlock expressionBody, bool hasErrors = false)
+ : base(BoundKind.ConstructorMethodBody, syntax, blockBody, expressionBody, hasErrors || initializer.HasErrors() || blockBody.HasErrors() || expressionBody.HasErrors())
+ {
+
+ Debug.Assert(!locals.IsDefault, "Field 'locals' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
+
+ this.Locals = locals;
+ this.Initializer = initializer;
+ }
+
+
+ public ImmutableArray Locals { get; }
+
+ public BoundExpressionStatement Initializer { get; }
+
+ public override BoundNode Accept(BoundTreeVisitor visitor)
+ {
+ return visitor.VisitConstructorMethodBody(this);
+ }
+
+ public BoundConstructorMethodBody Update(ImmutableArray locals, BoundExpressionStatement initializer, BoundBlock blockBody, BoundBlock expressionBody)
+ {
+ if (locals != this.Locals || initializer != this.Initializer || blockBody != this.BlockBody || expressionBody != this.ExpressionBody)
+ {
+ var result = new BoundConstructorMethodBody(this.Syntax, locals, initializer, blockBody, expressionBody, this.HasErrors);
+ result.WasCompilerGenerated = this.WasCompilerGenerated;
+ return result;
+ }
+ return this;
+ }
+ }
+
internal abstract partial class BoundTreeVisitor
{
@@ -6281,8 +6328,6 @@ internal R VisitInternal(BoundNode node, A arg)
{
switch (node.Kind)
{
- case BoundKind.FieldInitializer:
- return VisitFieldInitializer(node as BoundFieldInitializer, arg);
case BoundKind.FieldEqualsValue:
return VisitFieldEqualsValue(node as BoundFieldEqualsValue, arg);
case BoundKind.PropertyEqualsValue:
@@ -6587,6 +6632,10 @@ internal R VisitInternal(BoundNode node, A arg)
return VisitDeconstructionVariablePendingInference(node as DeconstructionVariablePendingInference, arg);
case BoundKind.OutDeconstructVarPendingInference:
return VisitOutDeconstructVarPendingInference(node as OutDeconstructVarPendingInference, arg);
+ case BoundKind.NonConstructorMethodBody:
+ return VisitNonConstructorMethodBody(node as BoundNonConstructorMethodBody, arg);
+ case BoundKind.ConstructorMethodBody:
+ return VisitConstructorMethodBody(node as BoundConstructorMethodBody, arg);
}
return default(R);
@@ -6595,10 +6644,6 @@ internal R VisitInternal(BoundNode node, A arg)
internal abstract partial class BoundTreeVisitor
{
- public virtual R VisitFieldInitializer(BoundFieldInitializer node, A arg)
- {
- return this.DefaultVisit(node, arg);
- }
public virtual R VisitFieldEqualsValue(BoundFieldEqualsValue node, A arg)
{
return this.DefaultVisit(node, arg);
@@ -7207,14 +7252,18 @@ public virtual R VisitOutDeconstructVarPendingInference(OutDeconstructVarPending
{
return this.DefaultVisit(node, arg);
}
+ public virtual R VisitNonConstructorMethodBody(BoundNonConstructorMethodBody node, A arg)
+ {
+ return this.DefaultVisit(node, arg);
+ }
+ public virtual R VisitConstructorMethodBody(BoundConstructorMethodBody node, A arg)
+ {
+ return this.DefaultVisit(node, arg);
+ }
}
internal abstract partial class BoundTreeVisitor
{
- public virtual BoundNode VisitFieldInitializer(BoundFieldInitializer node)
- {
- return this.DefaultVisit(node);
- }
public virtual BoundNode VisitFieldEqualsValue(BoundFieldEqualsValue node)
{
return this.DefaultVisit(node);
@@ -7823,15 +7872,18 @@ public virtual BoundNode VisitOutDeconstructVarPendingInference(OutDeconstructVa
{
return this.DefaultVisit(node);
}
+ public virtual BoundNode VisitNonConstructorMethodBody(BoundNonConstructorMethodBody node)
+ {
+ return this.DefaultVisit(node);
+ }
+ public virtual BoundNode VisitConstructorMethodBody(BoundConstructorMethodBody node)
+ {
+ return this.DefaultVisit(node);
+ }
}
internal abstract partial class BoundTreeWalker: BoundTreeVisitor
{
- public override BoundNode VisitFieldInitializer(BoundFieldInitializer node)
- {
- this.Visit(node.InitialValue);
- return null;
- }
public override BoundNode VisitFieldEqualsValue(BoundFieldEqualsValue node)
{
this.Visit(node.Value);
@@ -8617,29 +8669,37 @@ public override BoundNode VisitOutDeconstructVarPendingInference(OutDeconstructV
{
return null;
}
+ public override BoundNode VisitNonConstructorMethodBody(BoundNonConstructorMethodBody node)
+ {
+ this.Visit(node.BlockBody);
+ this.Visit(node.ExpressionBody);
+ return null;
+ }
+ public override BoundNode VisitConstructorMethodBody(BoundConstructorMethodBody node)
+ {
+ this.Visit(node.Initializer);
+ this.Visit(node.BlockBody);
+ this.Visit(node.ExpressionBody);
+ return null;
+ }
}
internal abstract partial class BoundTreeRewriter : BoundTreeVisitor
{
- public override BoundNode VisitFieldInitializer(BoundFieldInitializer node)
- {
- BoundExpression initialValue = (BoundExpression)this.Visit(node.InitialValue);
- return node.Update(node.Field, initialValue);
- }
public override BoundNode VisitFieldEqualsValue(BoundFieldEqualsValue node)
{
BoundExpression value = (BoundExpression)this.Visit(node.Value);
- return node.Update(node.Field, value);
+ return node.Update(node.Field, node.Locals, value);
}
public override BoundNode VisitPropertyEqualsValue(BoundPropertyEqualsValue node)
{
BoundExpression value = (BoundExpression)this.Visit(node.Value);
- return node.Update(node.Property, value);
+ return node.Update(node.Property, node.Locals, value);
}
public override BoundNode VisitParameterEqualsValue(BoundParameterEqualsValue node)
{
BoundExpression value = (BoundExpression)this.Visit(node.Value);
- return node.Update(node.Parameter, value);
+ return node.Update(node.Parameter, node.Locals, value);
}
public override BoundNode VisitGlobalStatementInitializer(BoundGlobalStatementInitializer node)
{
@@ -9524,6 +9584,19 @@ public override BoundNode VisitOutDeconstructVarPendingInference(OutDeconstructV
TypeSymbol type = this.VisitType(node.Type);
return node.Update();
}
+ public override BoundNode VisitNonConstructorMethodBody(BoundNonConstructorMethodBody node)
+ {
+ BoundBlock blockBody = (BoundBlock)this.Visit(node.BlockBody);
+ BoundBlock expressionBody = (BoundBlock)this.Visit(node.ExpressionBody);
+ return node.Update(blockBody, expressionBody);
+ }
+ public override BoundNode VisitConstructorMethodBody(BoundConstructorMethodBody node)
+ {
+ BoundExpressionStatement initializer = (BoundExpressionStatement)this.Visit(node.Initializer);
+ BoundBlock blockBody = (BoundBlock)this.Visit(node.BlockBody);
+ BoundBlock expressionBody = (BoundBlock)this.Visit(node.ExpressionBody);
+ return node.Update(node.Locals, initializer, blockBody, expressionBody);
+ }
}
internal sealed class BoundTreeDumperNodeProducer : BoundTreeVisitor