From 46ab842a08745463c83a978c19336b789d5bf6a0 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Sat, 3 Aug 2024 08:18:48 -0700 Subject: [PATCH] Generate implicit accessor bodies --- .../Portable/Compiler/MethodCompiler.cs | 3 +- .../Source/SourcePropertyAccessorSymbol.cs | 26 ++-- .../Symbols/Source/SourcePropertySymbol.cs | 110 +++++++------ .../Source/SourcePropertySymbolBase.cs | 22 ++- ...nthesizedRecordEqualityContractProperty.cs | 8 +- .../SynthesizedRecordPropertySymbol.cs | 10 +- .../CSharp/Test/Emit3/FieldKeywordTests.cs | 146 ++++++++++++++++++ 7 files changed, 255 insertions(+), 70 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 7940f2df63ec4..a1d1307ee090b 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -1879,8 +1879,7 @@ syntaxNode is ConstructorDeclarationSyntax constructorSyntax && } else { - var property = sourceMethod.AssociatedSymbol as SourcePropertySymbolBase; - if (property is not null && property.IsAutoPropertyWithGetAccessor) + if (sourceMethod is SourcePropertyAccessorSymbol { GenerateAccessorBody: 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 dc1f91d9fe98d..c50eee6bb3196 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 _generateAccessorBody; private readonly bool _usesInit; public static SourcePropertyAccessorSymbol CreateAccessorSymbol( @@ -30,7 +30,7 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol( SourcePropertySymbol property, DeclarationModifiers propertyModifiers, AccessorDeclarationSyntax syntax, - bool isAutoPropertyAccessor, + bool generateAccessorBody, BindingDiagnosticBag diagnostics) { Debug.Assert(syntax.Kind() == SyntaxKind.GetAccessorDeclaration || @@ -57,7 +57,7 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol( syntax.Modifiers, methodKind, syntax.Keyword.IsKind(SyntaxKind.InitKeyword), - isAutoPropertyAccessor, + generateAccessorBody, isNullableAnalysisEnabled: isNullableAnalysisEnabled, diagnostics); } @@ -104,7 +104,7 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol( modifiers: default, methodKind, usesInit, - isAutoPropertyAccessor: true, + generateAccessorBody: true, isNullableAnalysisEnabled: false, diagnostics); } @@ -159,7 +159,7 @@ private SourcePropertyAccessorSymbol( out var modifierErrors)) { _property = property; - _isAutoPropertyAccessor = false; + _generateAccessorBody = false; CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: true, diagnostics: diagnostics); CheckModifiersForBody(location, diagnostics); @@ -182,7 +182,7 @@ protected SourcePropertyAccessorSymbol( SyntaxTokenList modifiers, MethodKind methodKind, bool usesInit, - bool isAutoPropertyAccessor, + bool generateAccessorBody, bool isNullableAnalysisEnabled, BindingDiagnosticBag diagnostics) : base(containingType, @@ -192,7 +192,7 @@ protected SourcePropertyAccessorSymbol( MakeModifiersAndFlags(containingType, property, propertyModifiers, location, hasBlockBody, hasExpressionBody, modifiers, methodKind, isNullableAnalysisEnabled, diagnostics, out bool modifierErrors)) { _property = property; - _isAutoPropertyAccessor = isAutoPropertyAccessor; + _generateAccessorBody = generateAccessorBody; Debug.Assert(!_property.IsExpressionBodied, "Cannot have accessors in expression bodied lightweight properties"); var hasAnyBody = hasBlockBody || hasExpressionBody; _usesInit = usesInit; @@ -201,7 +201,7 @@ protected SourcePropertyAccessorSymbol( Binder.CheckFeatureAvailability(syntax, MessageID.IDS_FeatureInitOnlySetters, diagnostics, location); } - CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: hasAnyBody || isAutoPropertyAccessor, diagnostics); + CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: hasAnyBody || generateAccessorBody, diagnostics); if (hasAnyBody) { @@ -212,7 +212,7 @@ protected SourcePropertyAccessorSymbol( if (!modifierErrors) { - this.CheckModifiers(location, hasAnyBody, isAutoPropertyAccessor, diagnostics); + this.CheckModifiers(location, hasAnyBody, generateAccessorBody, diagnostics); } if (modifiers.Count > 0) @@ -480,11 +480,13 @@ internal sealed override bool IsDeclaredReadOnly return ContainingType.IsStructType() && !_property.IsStatic && - _isAutoPropertyAccessor && + _generateAccessorBody && MethodKind == MethodKind.PropertyGet; } } + internal bool GenerateAccessorBody => _generateAccessorBody; + internal sealed override bool IsInitOnly => !IsStatic && _usesInit; private static DeclarationModifiers MakeModifiers(NamedTypeSymbol containingType, SyntaxTokenList modifiers, bool isExplicitInterfaceImplementation, @@ -557,7 +559,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 && _generateAccessorBody && MethodKind == MethodKind.PropertySet) { // Auto-implemented accessor '{0}' cannot be marked 'readonly'. diagnostics.Add(ErrorCode.ERR_AutoSetterCantBeReadOnly, location, this); @@ -803,7 +805,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - if (_isAutoPropertyAccessor) + if (_generateAccessorBody) { 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 e575d7840340a..04491466e94f7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -40,12 +40,12 @@ private static SourcePropertySymbol Create( GetAccessorDeclarations( syntax, diagnostics, - out bool hasAccessorList, - out bool accessorsHaveImplementation, - out bool usesFieldKeyword, + out bool isExpressionBodied, out bool isInitOnly, - out var getSyntax, - out var setSyntax); + out var getAccessorInfo, + out var setAccessorInfo); + + bool accessorsHaveImplementation = getAccessorInfo.HasImplementation || setAccessorInfo.HasImplementation; // PROTOTYPE: Remove if not needed. var explicitInterfaceSpecifier = GetExplicitInterfaceSpecifier(syntax); SyntaxTokenList modifiersTokenList = GetModifierTokensSyntax(syntax); @@ -61,18 +61,27 @@ private static SourcePropertySymbol Create( out _); bool isAutoProperty = (modifiers & DeclarationModifiers.Partial) == 0 && !accessorsHaveImplementation; - bool isExpressionBodied = !hasAccessorList && GetArrowExpression(syntax) != null; binder = binder.SetOrClearUnsafeRegionIfNecessary(modifiersTokenList); TypeSymbol? explicitInterfaceType; string? aliasQualifierOpt; string memberName = ExplicitInterfaceHelpers.GetMemberNameAndInterfaceSymbol(binder, explicitInterfaceSpecifier, name, diagnostics, out explicitInterfaceType, out aliasQualifierOpt); + // PROTOTYPE: Rename corresponding parameters to match these names. + bool isAutoGetAccessor = false; + bool isAutoSetAccessor = false; + if ((modifiers & DeclarationModifiers.Partial) == 0) + { + isAutoGetAccessor = !getAccessorInfo.IsDefault && !getAccessorInfo.HasImplementation; + isAutoSetAccessor = !setAccessorInfo.IsDefault && !setAccessorInfo.HasImplementation; + } return new SourcePropertySymbol( containingType, syntax, - hasGetAccessor: getSyntax != null || isExpressionBodied, - hasSetAccessor: setSyntax != null, + hasGetAccessor: !getAccessorInfo.IsDefault || isExpressionBodied, + hasSetAccessor: !setAccessorInfo.IsDefault, + generateGetAccessorBody: isAutoGetAccessor, + generateSetAccessorBody: isAutoSetAccessor, isExplicitInterfaceImplementation, explicitInterfaceType, aliasQualifierOpt, @@ -82,7 +91,7 @@ private static SourcePropertySymbol Create( isExpressionBodied: isExpressionBodied, isInitOnly: isInitOnly, accessorsHaveImplementation: accessorsHaveImplementation, - usesFieldKeyword: usesFieldKeyword, + usesFieldKeyword: getAccessorInfo.UsesFieldKeyword || setAccessorInfo.UsesFieldKeyword, memberName, location, diagnostics); @@ -93,6 +102,8 @@ private SourcePropertySymbol( BasePropertyDeclarationSyntax syntax, bool hasGetAccessor, bool hasSetAccessor, + bool generateGetAccessorBody, + bool generateSetAccessorBody, bool isExplicitInterfaceImplementation, TypeSymbol? explicitInterfaceType, string? aliasQualifierOpt, @@ -109,8 +120,10 @@ private SourcePropertySymbol( : base( containingType, syntax, - hasGetAccessor, - hasSetAccessor, + hasGetAccessor: hasGetAccessor, + hasSetAccessor: hasSetAccessor, + generateGetAccessorBody: generateGetAccessorBody, + generateSetAccessorBody: generateSetAccessorBody, isExplicitInterfaceImplementation, explicitInterfaceType, aliasQualifierOpt, @@ -199,34 +212,46 @@ public override OneOrMany> GetAttributeDeclarati public override IAttributeTargetSymbol AttributesOwner => this; + private readonly struct AccessorInfo + { + public AccessorInfo(CSharpSyntaxNode syntax, bool hasImplementation, bool usesFieldKeyword) + { + Syntax = syntax; + HasImplementation = hasImplementation; + UsesFieldKeyword = usesFieldKeyword; + } + + public readonly CSharpSyntaxNode? Syntax; + public readonly bool HasImplementation; + public readonly bool UsesFieldKeyword; + + public bool IsDefault => Syntax is null; + } + private static void GetAccessorDeclarations( CSharpSyntaxNode syntaxNode, BindingDiagnosticBag diagnostics, - out bool hasAccessorList, - out bool accessorsHaveImplementation, - out bool usesFieldKeyword, + out bool isExpressionBodied, out bool isInitOnly, - out CSharpSyntaxNode? getSyntax, - out CSharpSyntaxNode? setSyntax) + out AccessorInfo getAccessor, + out AccessorInfo setAccessor) { var syntax = (BasePropertyDeclarationSyntax)syntaxNode; - hasAccessorList = syntax.AccessorList != null; - getSyntax = null; - setSyntax = null; + isExpressionBodied = syntax.AccessorList is null; isInitOnly = false; + getAccessor = default; + setAccessor = default; - if (hasAccessorList) + if (!isExpressionBodied) { - usesFieldKeyword = false; - accessorsHaveImplementation = false; foreach (var accessor in syntax.AccessorList!.Accessors) { switch (accessor.Kind()) { case SyntaxKind.GetAccessorDeclaration: - if (getSyntax == null) + if (getAccessor.IsDefault) { - getSyntax = accessor; + getAccessor = getAccessorInfo(accessor); } else { @@ -235,9 +260,9 @@ private static void GetAccessorDeclarations( break; case SyntaxKind.SetAccessorDeclaration: case SyntaxKind.InitAccessorDeclaration: - if (setSyntax == null) + if (setAccessor.IsDefault) { - setSyntax = accessor; + setAccessor = getAccessorInfo(accessor); if (accessor.Keyword.IsKind(SyntaxKind.InitKeyword)) { isInitOnly = true; @@ -259,22 +284,19 @@ private static void GetAccessorDeclarations( default: throw ExceptionUtilities.UnexpectedValue(accessor.Kind()); } - - var body = (SyntaxNode?)accessor.Body ?? accessor.ExpressionBody; - if (body != null) - { - accessorsHaveImplementation = true; - } - - usesFieldKeyword = usesFieldKeyword || containsFieldKeyword(accessor); } } else { var body = GetArrowExpression(syntax); - accessorsHaveImplementation = body is object; - usesFieldKeyword = body is { } && containsFieldKeyword(body); - Debug.Assert(accessorsHaveImplementation); // it's not clear how this even parsed as a property if it has no accessor list and no arrow expression. + Debug.Assert(body is { }); // it's not clear how this even parsed as a property if it has no accessor list and no arrow expression. + getAccessor = new AccessorInfo(body, hasImplementation: true, usesFieldKeyword: body is { } && containsFieldKeyword(body)); + } + + static AccessorInfo getAccessorInfo(AccessorDeclarationSyntax accessor) + { + var body = (SyntaxNode?)accessor.Body ?? accessor.ExpressionBody; + return new AccessorInfo(accessor, hasImplementation: body is { }, usesFieldKeyword: containsFieldKeyword(accessor)); } static bool containsFieldKeyword(SyntaxNode syntax) @@ -324,7 +346,7 @@ private static (DeclarationModifiers modifiers, bool hasExplicitAccessMod) MakeM SyntaxTokenList modifiers, bool isExplicitInterfaceImplementation, bool isIndexer, - bool accessorsHaveImplementation, + bool accessorsHaveImplementation, // PROTOTYPE: How is this used? Location location, BindingDiagnosticBag diagnostics, out bool modifierErrors) @@ -451,7 +473,7 @@ private static (DeclarationModifiers modifiers, bool hasExplicitAccessMod) MakeM return (mods, hasExplicitAccessMod); } - protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) + protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool generateAccessorBody, BindingDiagnosticBag diagnostics) { var syntax = (BasePropertyDeclarationSyntax)CSharpSyntaxNode; ArrowExpressionClauseSyntax? arrowExpression = GetArrowExpression(syntax); @@ -464,21 +486,21 @@ protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isA } else { - return CreateAccessorSymbol(GetGetAccessorDeclaration(syntax), isAutoPropertyAccessor, diagnostics); + return CreateAccessorSymbol(GetGetAccessorDeclaration(syntax), generateAccessorBody, diagnostics); } } - protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) + protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool generateAccessorBody, 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), generateAccessorBody, diagnostics); } private SourcePropertyAccessorSymbol CreateAccessorSymbol( AccessorDeclarationSyntax syntax, - bool isAutoPropertyAccessor, + bool generateAccessorBody, BindingDiagnosticBag diagnostics) { return SourcePropertyAccessorSymbol.CreateAccessorSymbol( @@ -486,7 +508,7 @@ private SourcePropertyAccessorSymbol CreateAccessorSymbol( this, _modifiers, syntax, - isAutoPropertyAccessor, + generateAccessorBody, diagnostics); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs index 67c53805e2a50..7549ca1d3e2e1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -75,6 +75,8 @@ protected SourcePropertySymbolBase( CSharpSyntaxNode syntax, bool hasGetAccessor, bool hasSetAccessor, + bool generateGetAccessorBody, + bool generateSetAccessorBody, bool isExplicitInterfaceImplementation, TypeSymbol? explicitInterfaceType, string? aliasQualifierOpt, @@ -96,6 +98,8 @@ protected SourcePropertySymbolBase( Debug.Assert(!isExpressionBodied || !hasInitializer); Debug.Assert(!isExpressionBodied || accessorsHaveImplementation); Debug.Assert((modifiers & DeclarationModifiers.Required) == 0 || this is SourcePropertySymbol); + //Debug.Assert(isAutoProperty || usesFieldKeyword || !generateGetAccessorBody); + //Debug.Assert(isAutoProperty || usesFieldKeyword || !generateSetAccessorBody); _syntaxRef = syntax.GetReference(); Location = location; @@ -114,7 +118,15 @@ protected SourcePropertySymbolBase( } bool isIndexer = IsIndexer; - isAutoProperty = isAutoProperty && !(containingType.IsInterface && !IsStatic) && !IsAbstract && !IsExtern && !isIndexer; + if (isAutoProperty) + { + if (!(!(containingType.IsInterface && !IsStatic) && !IsAbstract && !IsExtern && !isIndexer)) + { + isAutoProperty = false; + generateGetAccessorBody = false; + generateSetAccessorBody = false; + } + } if (hasExplicitAccessMod) { @@ -181,12 +193,12 @@ protected SourcePropertySymbolBase( if (hasGetAccessor) { - _getMethod = CreateGetAccessorSymbol(isAutoPropertyAccessor: isAutoProperty, diagnostics); + _getMethod = CreateGetAccessorSymbol(generateGetAccessorBody, diagnostics); } if (hasSetAccessor) { - _setMethod = CreateSetAccessorSymbol(isAutoPropertyAccessor: isAutoProperty, diagnostics); + _setMethod = CreateSetAccessorSymbol(generateSetAccessorBody, diagnostics); } } @@ -557,7 +569,7 @@ internal bool IsNew /// The implementation may depend only on information available from the type. /// protected abstract SourcePropertyAccessorSymbol CreateGetAccessorSymbol( - bool isAutoPropertyAccessor, + bool generateAccessorBody, BindingDiagnosticBag diagnostics); /// @@ -565,7 +577,7 @@ protected abstract SourcePropertyAccessorSymbol CreateGetAccessorSymbol( /// The implementation may depend only on information available from the type. /// protected abstract SourcePropertyAccessorSymbol CreateSetAccessorSymbol( - bool isAutoPropertyAccessor, + bool generateAccessorBody, 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 6fab1931f2763..dcfc06fa59444 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs @@ -21,6 +21,8 @@ public SynthesizedRecordEqualityContractProperty(SourceMemberContainerTypeSymbol syntax: (CSharpSyntaxNode)containingType.SyntaxReferences[0].GetSyntax(), hasGetAccessor: true, hasSetAccessor: false, + generateGetAccessorBody: false, + generateSetAccessorBody: false, isExplicitInterfaceImplementation: false, explicitInterfaceType: null, aliasQualifierOpt: null, @@ -60,7 +62,7 @@ public override OneOrMany> GetAttributeDeclarati protected override Location TypeLocation => ContainingType.GetFirstLocation(); - protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) + protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool generateAccessorBody, BindingDiagnosticBag diagnostics) { return SourcePropertyAccessorSymbol.CreateAccessorSymbol( ContainingType, @@ -71,7 +73,7 @@ protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isA diagnostics); } - protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) + protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool generateAccessorBody, BindingDiagnosticBag diagnostics) { throw ExceptionUtilities.Unreachable(); } @@ -148,7 +150,7 @@ internal GetAccessorSymbol( modifiers: default, MethodKind.PropertyGet, usesInit: false, - isAutoPropertyAccessor: false, + generateAccessorBody: 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 798028c8b916a..ca9b842c6c869 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs @@ -24,6 +24,8 @@ public SynthesizedRecordPropertySymbol( syntax: syntax, hasGetAccessor: true, hasSetAccessor: true, + generateGetAccessorBody: true, + generateSetAccessorBody: true, isExplicitInterfaceImplementation: false, explicitInterfaceType: null, aliasQualifierOpt: null, @@ -54,15 +56,15 @@ protected override Location TypeLocation public override OneOrMany> GetAttributeDeclarations() => OneOrMany.Create(BackingParameter.AttributeDeclarationList); - protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) + protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool generateAccessorBody, BindingDiagnosticBag diagnostics) { - Debug.Assert(isAutoPropertyAccessor); + Debug.Assert(generateAccessorBody); return CreateAccessorSymbol(isGet: true, CSharpSyntaxNode, diagnostics); } - protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) + protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool generateAccessorBody, BindingDiagnosticBag diagnostics) { - Debug.Assert(isAutoPropertyAccessor); + Debug.Assert(generateAccessorBody); return CreateAccessorSymbol(isGet: false, CSharpSyntaxNode, diagnostics); } diff --git a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs index 462bce6734ddc..11298970e766c 100644 --- a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs @@ -324,6 +324,152 @@ class C Diagnostic(ErrorCode.ERR_NameNotInContext, "field").WithArguments("field").WithLocation(3, 28)); } + [Theory] + [InlineData("class")] + [InlineData("struct")] + [InlineData("ref struct")] + [InlineData("record")] + [InlineData("record struct")] + public void ImplicitAccessorBody_01(string typeKind) + { + string source = $$""" + {{typeKind}} A + { + public static object P1 { get; set { _ = field; } } + public static object P2 { get { return field; } set; } + public object Q1 { get; set { _ = field; } } + public object Q2 { get { return field; } set; } + public object Q3 { get { return field; } init; } + } + class Program + { + static void Main() + { + _ = new A(); + } + } + """; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, targetFramework: TargetFramework.Net80); + verifier.VerifyIL("A.P1.get", """ + { + // Code size 6 (0x6) + .maxstack 1 + IL_0000: ldsfld "object A.k__BackingField" + IL_0005: ret + } + """); + verifier.VerifyIL("A.P2.set", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: stsfld "object A.k__BackingField" + IL_0006: ret + } + """); + verifier.VerifyIL("A.Q1.get", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldfld "object A.k__BackingField" + IL_0006: ret + } + """); + verifier.VerifyIL("A.Q2.set", """ + { + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld "object A.k__BackingField" + IL_0007: ret + } + """); + verifier.VerifyIL("A.Q3.init", """ + { + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld "object A.k__BackingField" + IL_0007: ret + } + """); + } + + [Fact] + public void ImplicitAccessorBody_02() + { + string source = """ + interface I + { + static object P1 { get; set { _ = field; } } + static object P2 { get { return field; } set; } + object Q1 { get; set { _ = field; } } + object Q2 { get { return field; } set; } + object Q3 { get { return field; } init; } + } + class A : I + { + } + class Program + { + static void Main() + { + _ = new A(); + } + } + """; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, targetFramework: TargetFramework.Net80); + verifier.VerifyIL("I.P1.get", """ + { + // Code size 6 (0x6) + .maxstack 1 + IL_0000: ldsfld "object I.k__BackingField" + IL_0005: ret + } + """); + verifier.VerifyIL("I.P2.set", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: stsfld "object I.k__BackingField" + IL_0006: ret + } + """); + verifier.VerifyIL("I.Q1.get", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldfld "object I.k__BackingField" + IL_0006: ret + } + """); + verifier.VerifyIL("I.Q2.set", """ + { + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld "object I.k__BackingField" + IL_0007: ret + } + """); + verifier.VerifyIL("I.Q3.init", """ + { + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld "object I.k__BackingField" + IL_0007: ret + } + """); + } + [Fact] public void Attribute_01() {