diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs index cdd918a3aeb46..20f7118a66261 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs @@ -168,7 +168,7 @@ private static void MakeSubmissionInitialization( } /// - /// Construct a body for an auto-property accessor (updating or returning the backing field). + /// Construct a body for an auto-property accessor (updating or returning the backing field). This can return null on error scenarios. /// internal static BoundBlock ConstructAutoPropertyAccessorBody(SourceMemberMethodSymbol accessor) { @@ -184,6 +184,13 @@ internal static BoundBlock ConstructAutoPropertyAccessorBody(SourceMemberMethodS } var field = property.BackingField; + if (field is null) + { + // This happens for public int { set; } where we produce ERR_AutoPropertyMustHaveGetAccessor + Debug.Assert(property.GetMethod is null); + return null; + } + Debug.Assert(!field.IsCreatedForFieldKeyword); var fieldAccess = new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true }; BoundStatement statement; diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 7836c005a8f0b..9c1fdac50e138 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -1869,8 +1869,7 @@ syntaxNode is ConstructorDeclarationSyntax constructorSyntax && } else { - var property = sourceMethod.AssociatedSymbol as SourcePropertySymbolBase; - if ((object)property != null && property.IsAutoPropertyWithGetAccessor) // PROTOTYPE: Just IsAutoProperty, or _isAutoPropertyAccessor? + if (sourceMethod is SourcePropertyAccessorSymbol { BodyShouldBeSynthesized: true }) { return MethodBodySynthesizer.ConstructAutoPropertyAccessorBody(sourceMethod); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs index 5e277dc4e08c7..8f7cdbbe5e75a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs @@ -22,7 +22,7 @@ internal class SourcePropertyAccessorSymbol : SourceMemberMethodSymbol private ImmutableArray _lazyRefCustomModifiers; private ImmutableArray _lazyExplicitInterfaceImplementations; private string _lazyName; - private readonly bool _isAutoPropertyAccessor; + private readonly bool _bodyShouldBeSynthesized; private readonly bool _isExpressionBodied; private readonly bool _containsFieldKeyword; private readonly bool _usesInit; @@ -32,7 +32,7 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol( SourcePropertySymbol property, DeclarationModifiers propertyModifiers, AccessorDeclarationSyntax syntax, - bool isAutoPropertyAccessor, + bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics) { Debug.Assert(syntax.Kind() == SyntaxKind.GetAccessorDeclaration || @@ -42,7 +42,7 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol( bool isGetMethod = (syntax.Kind() == SyntaxKind.GetAccessorDeclaration); var methodKind = isGetMethod ? MethodKind.PropertyGet : MethodKind.PropertySet; - bool hasBody = syntax.Body is object; + bool hasBlockBody = syntax.Body is object; bool hasExpressionBody = syntax.ExpressionBody is object; bool isNullableAnalysisEnabled = containingType.DeclaringCompilation.IsNullableAnalysisEnabledIn(syntax); CheckForBlockAndExpressionBody(syntax.Body, syntax.ExpressionBody, syntax, diagnostics); @@ -52,13 +52,13 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol( propertyModifiers, syntax.Keyword.GetLocation(), syntax, - hasBody, + hasBlockBody, hasExpressionBody, isIterator: SyntaxFacts.HasYieldOperations(syntax.Body), syntax.Modifiers, methodKind, syntax.Keyword.IsKind(SyntaxKind.InitKeyword), - isAutoPropertyAccessor, + bodyShouldBeSynthesized: bodyShouldBeSynthesizedForSemicolonOnly && !hasBlockBody && !hasExpressionBody, isNullableAnalysisEnabled: isNullableAnalysisEnabled, diagnostics); } @@ -99,34 +99,17 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol( propertyModifiers, location, syntax, - hasBody: false, + hasBlockBody: false, hasExpressionBody: false, isIterator: false, modifiers: new SyntaxTokenList(), methodKind, usesInit, - isAutoPropertyAccessor: true, + bodyShouldBeSynthesized: true, isNullableAnalysisEnabled: false, diagnostics); } - public static SourcePropertyAccessorSymbol CreateAccessorSymbol( - NamedTypeSymbol containingType, - SynthesizedRecordEqualityContractProperty property, - DeclarationModifiers propertyModifiers, - Location location, - CSharpSyntaxNode syntax, - BindingDiagnosticBag diagnostics) - { - return new SynthesizedRecordEqualityContractProperty.GetAccessorSymbol( - containingType, - property, - propertyModifiers, - location, - syntax, - diagnostics); - } - // PROTOTYPE(semi-auto-props): Figure out what is going to be more efficient, to go after tokens and then // checking their parent, or to go after nodes (IdentifierNameSyntax) first and then checking the underlying token. // PROTOTYPE(semi-auto-props): Filter out identifiers that syntactically cannot be keywords. For example those that follow a ., a -> or a :: in names. Something else? @@ -166,7 +149,7 @@ private SourcePropertyAccessorSymbol( base(containingType, syntax.GetReference(), location, isIterator: false) { _property = property; - _isAutoPropertyAccessor = false; + _bodyShouldBeSynthesized = false; _isExpressionBodied = true; _containsFieldKeyword = property.IsIndexer ? false : NodeContainsFieldKeyword(syntax); @@ -188,7 +171,7 @@ private SourcePropertyAccessorSymbol( diagnostics.Add(info, location); } - this.CheckModifiers(location, hasBody: true, isAutoPropertyOrExpressionBodied: true, diagnostics: diagnostics); + this.CheckModifiers(location, isBodyMissing: false, diagnostics: diagnostics); } #nullable enable @@ -198,13 +181,13 @@ protected SourcePropertyAccessorSymbol( DeclarationModifiers propertyModifiers, Location location, CSharpSyntaxNode syntax, - bool hasBody, + bool hasBlockBody, bool hasExpressionBody, bool isIterator, SyntaxTokenList modifiers, MethodKind methodKind, bool usesInit, - bool isAutoPropertyAccessor, + bool bodyShouldBeSynthesized, bool isNullableAnalysisEnabled, BindingDiagnosticBag diagnostics) : base(containingType, @@ -213,10 +196,10 @@ protected SourcePropertyAccessorSymbol( isIterator) { _property = property; - _isAutoPropertyAccessor = isAutoPropertyAccessor; + _bodyShouldBeSynthesized = bodyShouldBeSynthesized; _containsFieldKeyword = property.IsIndexer ? false : NodeContainsFieldKeyword(getAccessorSyntax(syntax)); Debug.Assert(!_property.IsExpressionBodied, "Cannot have accessors in expression bodied lightweight properties"); - _isExpressionBodied = !hasBody && hasExpressionBody; + _isExpressionBodied = !hasBlockBody && hasExpressionBody; _usesInit = usesInit; if (_usesInit) { @@ -224,7 +207,7 @@ protected SourcePropertyAccessorSymbol( } bool modifierErrors; - var declarationModifiers = this.MakeModifiers(modifiers, property.IsExplicitInterfaceImplementation, hasBody || hasExpressionBody, location, diagnostics, out modifierErrors); + var declarationModifiers = this.MakeModifiers(modifiers, property.IsExplicitInterfaceImplementation, hasBlockBody || hasExpressionBody, location, diagnostics, out modifierErrors); // Include some modifiers from the containing property, but not the accessibility modifiers. declarationModifiers |= GetAccessorModifiers(propertyModifiers) & ~DeclarationModifiers.AccessibilityMask; @@ -239,9 +222,9 @@ protected SourcePropertyAccessorSymbol( this.MakeFlags(methodKind, declarationModifiers, returnsVoid: false, isExtensionMethod: false, isNullableAnalysisEnabled: isNullableAnalysisEnabled, isMetadataVirtualIgnoringModifiers: property.IsExplicitInterfaceImplementation && (declarationModifiers & DeclarationModifiers.Static) == 0); - CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: hasBody || hasExpressionBody || isAutoPropertyAccessor, diagnostics); + CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: hasBlockBody || hasExpressionBody || _bodyShouldBeSynthesized, diagnostics); - if (hasBody || hasExpressionBody) + if (hasBlockBody || hasExpressionBody) { CheckModifiersForBody(location, diagnostics); } @@ -254,7 +237,7 @@ protected SourcePropertyAccessorSymbol( if (!modifierErrors) { - this.CheckModifiers(location, hasBody || hasExpressionBody, isAutoPropertyAccessor, diagnostics); + this.CheckModifiers(location, isBodyMissing: !hasBlockBody && !hasExpressionBody && !_bodyShouldBeSynthesized, diagnostics); } static CSharpSyntaxNode? getAccessorSyntax(CSharpSyntaxNode node) @@ -478,6 +461,11 @@ internal Accessibility LocalAccessibility /// internal bool ContainsFieldKeyword => _containsFieldKeyword; + /// + /// Indicates whether this accessor has no body in source and a body will be synthesized by the compiler. + /// + internal bool BodyShouldBeSynthesized => _bodyShouldBeSynthesized; + /// /// Indicates whether this accessor is readonly due to reasons scoped to itself and its containing property. /// @@ -527,7 +515,7 @@ internal sealed override bool IsDeclaredReadOnly return ContainingType.IsStructType() && !_property.IsStatic && - _isAutoPropertyAccessor && + _bodyShouldBeSynthesized && MethodKind == MethodKind.PropertyGet; } } @@ -564,7 +552,7 @@ private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, bool isExp return mods; } - private void CheckModifiers(Location location, bool hasBody, bool isAutoPropertyOrExpressionBodied, BindingDiagnosticBag diagnostics) + private void CheckModifiers(Location location, bool isBodyMissing, BindingDiagnosticBag diagnostics) { // Check accessibility against the accessibility declared on the accessor not the property. var localAccessibility = this.LocalAccessibility; @@ -579,7 +567,7 @@ private void CheckModifiers(Location location, bool hasBody, bool isAutoProperty // '{0}' is a new virtual member in sealed type '{1}' diagnostics.Add(ErrorCode.ERR_NewVirtualInSealed, location, this, ContainingType); } - else if (!hasBody && !IsExtern && !IsAbstract && !isAutoPropertyOrExpressionBodied) + else if (isBodyMissing && !IsExtern && !IsAbstract) { diagnostics.Add(ErrorCode.ERR_ConcreteMissingBody, location, this); } @@ -602,7 +590,7 @@ private void CheckModifiers(Location location, bool hasBody, bool isAutoProperty // 'init' accessors cannot be marked 'readonly'. Mark '{0}' readonly instead. diagnostics.Add(ErrorCode.ERR_InitCannotBeReadonly, location, _property); } - else if (LocalDeclaredReadOnly && _isAutoPropertyAccessor && MethodKind == MethodKind.PropertySet) + else if (LocalDeclaredReadOnly && _bodyShouldBeSynthesized && MethodKind == MethodKind.PropertySet) { // Auto-implemented accessor '{0}' cannot be marked 'readonly'. diagnostics.Add(ErrorCode.ERR_AutoSetterCantBeReadOnly, location, this); @@ -835,7 +823,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - if (_isAutoPropertyAccessor) + if (_bodyShouldBeSynthesized) { var compilation = this.DeclaringCompilation; AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index f71c881683cec..96fed048c58ab 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -353,7 +353,7 @@ private static DeclarationModifiers MakeModifiers( return mods; } - protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) + protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics) { var syntax = (BasePropertyDeclarationSyntax)CSharpSyntaxNode; ArrowExpressionClauseSyntax? arrowExpression = GetArrowExpression(syntax); @@ -366,21 +366,21 @@ protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isA } else { - return CreateAccessorSymbol(GetGetAccessorDeclaration(syntax), isAutoPropertyAccessor, diagnostics); + return CreateAccessorSymbol(GetGetAccessorDeclaration(syntax), bodyShouldBeSynthesizedForSemicolonOnly, diagnostics); } } - protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) + protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics) { var syntax = (BasePropertyDeclarationSyntax)CSharpSyntaxNode; Debug.Assert(!(syntax.AccessorList is null && GetArrowExpression(syntax) != null)); - return CreateAccessorSymbol(GetSetAccessorDeclaration(syntax), isAutoPropertyAccessor, diagnostics); + return CreateAccessorSymbol(GetSetAccessorDeclaration(syntax), bodyShouldBeSynthesizedForSemicolonOnly, diagnostics); } private SourcePropertyAccessorSymbol CreateAccessorSymbol( AccessorDeclarationSyntax syntax, - bool isAutoPropertyAccessor, + bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics) { return SourcePropertyAccessorSymbol.CreateAccessorSymbol( @@ -388,7 +388,7 @@ private SourcePropertyAccessorSymbol CreateAccessorSymbol( this, _modifiers, syntax, - isAutoPropertyAccessor, + bodyShouldBeSynthesizedForSemicolonOnly, diagnostics); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs index e3ab466a3e38d..f5d88cb95da38 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -193,11 +193,11 @@ protected SourcePropertySymbolBase( if (hasGetAccessor) { - _getMethod = CreateGetAccessorSymbol(isAutoPropertyAccessor: isAutoProperty, diagnostics); + _getMethod = CreateGetAccessorSymbol(bodyShouldBeSynthesizedForSemicolonOnly: isAutoProperty, diagnostics); } if (hasSetAccessor) { - _setMethod = CreateSetAccessorSymbol(isAutoPropertyAccessor: isAutoProperty, diagnostics); + _setMethod = CreateSetAccessorSymbol(bodyShouldBeSynthesizedForSemicolonOnly: isAutoProperty, diagnostics); } } @@ -643,7 +643,7 @@ internal bool IsNew /// The implementation may depend only on information available from the type. /// protected abstract SourcePropertyAccessorSymbol CreateGetAccessorSymbol( - bool isAutoPropertyAccessor, + bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics); /// @@ -651,7 +651,7 @@ protected abstract SourcePropertyAccessorSymbol CreateGetAccessorSymbol( /// The implementation may depend only on information available from the type. /// protected abstract SourcePropertyAccessorSymbol CreateSetAccessorSymbol( - bool isAutoPropertyAccessor, + bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics); public sealed override MethodSymbol? GetMethod diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs index 9437e08dd6b5a..b862ee1b25dd0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs @@ -55,9 +55,9 @@ public override SyntaxList AttributeDeclarationSyntaxList protected override Location TypeLocation => ContainingType.Locations[0]; - protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) + protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics) { - return SourcePropertyAccessorSymbol.CreateAccessorSymbol( + return new GetAccessorSymbol( ContainingType, this, _modifiers, @@ -66,7 +66,7 @@ protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isA diagnostics); } - protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) + protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics) { throw ExceptionUtilities.Unreachable; } @@ -115,7 +115,7 @@ internal static void VerifyOverridesEqualityContractFromBase(PropertySymbol over } } - internal sealed class GetAccessorSymbol : SourcePropertyAccessorSymbol + private sealed class GetAccessorSymbol : SourcePropertyAccessorSymbol { internal GetAccessorSymbol( NamedTypeSymbol containingType, @@ -130,13 +130,13 @@ internal GetAccessorSymbol( propertyModifiers, location, syntax, - hasBody: true, + hasBlockBody: true, hasExpressionBody: false, isIterator: false, modifiers: new SyntaxTokenList(), MethodKind.PropertyGet, usesInit: false, - isAutoPropertyAccessor: false, + bodyShouldBeSynthesized: false, isNullableAnalysisEnabled: false, diagnostics) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs index 496f928ffbcfa..6c8a016d01ac6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs @@ -48,15 +48,15 @@ protected override Location TypeLocation public override SyntaxList AttributeDeclarationSyntaxList => BackingParameter.AttributeDeclarationList; - protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) + protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics) { - Debug.Assert(isAutoPropertyAccessor); + Debug.Assert(bodyShouldBeSynthesizedForSemicolonOnly); return CreateAccessorSymbol(isGet: true, CSharpSyntaxNode, diagnostics); } - protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) + protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics) { - Debug.Assert(isAutoPropertyAccessor); + Debug.Assert(bodyShouldBeSynthesizedForSemicolonOnly); return CreateAccessorSymbol(isGet: false, CSharpSyntaxNode, diagnostics); }