Skip to content

Commit

Permalink
Allow mixing auto- and implemented accessors
Browse files Browse the repository at this point in the history
  • Loading branch information
cston committed Aug 4, 2024
1 parent 1fce652 commit fcc6b42
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ private static SourcePropertySymbol Create(
diagnostics,
out _);

bool isAutoProperty = (modifiers & DeclarationModifiers.Partial) == 0 && !accessorsHaveImplementation; // PROTOTYPE: Remove if not needed.
bool hasAutoPropertyGet = (modifiers & DeclarationModifiers.Partial) == 0 && !getAccessorInfo.IsDefault && !getAccessorInfo.HasImplementation;
bool hasAutoPropertySet = (modifiers & DeclarationModifiers.Partial) == 0 && !setAccessorInfo.IsDefault && !setAccessorInfo.HasImplementation;

Expand All @@ -74,14 +73,13 @@ private static SourcePropertySymbol Create(
syntax,
hasGetAccessor: !getAccessorInfo.IsDefault || isExpressionBodied,
hasSetAccessor: !setAccessorInfo.IsDefault,
hasAutoPropertyGet: hasAutoPropertyGet,
hasAutoPropertySet: hasAutoPropertySet,
isExplicitInterfaceImplementation,
explicitInterfaceType,
aliasQualifierOpt,
modifiers,
hasExplicitAccessMod: hasExplicitAccessMod,
isAutoProperty: isAutoProperty,
hasAutoPropertyGet: hasAutoPropertyGet,
hasAutoPropertySet: hasAutoPropertySet,
isExpressionBodied: isExpressionBodied,
isInitOnly: isInitOnly,
accessorsHaveImplementation: accessorsHaveImplementation,
Expand All @@ -96,14 +94,13 @@ private SourcePropertySymbol(
BasePropertyDeclarationSyntax syntax,
bool hasGetAccessor,
bool hasSetAccessor,
bool hasAutoPropertyGet,
bool hasAutoPropertySet,
bool isExplicitInterfaceImplementation,
TypeSymbol? explicitInterfaceType,
string? aliasQualifierOpt,
DeclarationModifiers modifiers,
bool hasExplicitAccessMod,
bool isAutoProperty, // PROTOTYPE: Do we need this in addition to hasAutoPropertyGet, hasAutoPropertySet?
bool hasAutoPropertyGet,
bool hasAutoPropertySet,
bool isExpressionBodied,
bool isInitOnly,
bool accessorsHaveImplementation,
Expand All @@ -116,15 +113,14 @@ private SourcePropertySymbol(
syntax,
hasGetAccessor: hasGetAccessor,
hasSetAccessor: hasSetAccessor,
hasAutoPropertyGet: hasAutoPropertyGet,
hasAutoPropertySet: hasAutoPropertySet,
isExplicitInterfaceImplementation,
explicitInterfaceType,
aliasQualifierOpt,
modifiers,
hasInitializer: HasInitializer(syntax),
hasExplicitAccessMod: hasExplicitAccessMod,
isAutoProperty: isAutoProperty,
hasAutoPropertyGet: hasAutoPropertyGet,
hasAutoPropertySet: hasAutoPropertySet,
isExpressionBodied: isExpressionBodied,
isInitOnly: isInitOnly,
accessorsHaveImplementation: accessorsHaveImplementation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ internal abstract class SourcePropertySymbolBase : PropertySymbol, IAttributeTar
private enum Flags : byte
{
IsExpressionBodied = 1 << 0,
IsAutoProperty = 1 << 1,
IsExplicitInterfaceImplementation = 1 << 2,
HasInitializer = 1 << 3,
AccessorsHaveImplementation = 1 << 4,
HasExplicitAccessModifier = 1 << 5,
UsesFieldKeyword = 1 << 6,
HasAutoPropertyGet = 1 << 1,
HasAutoPropertySet = 1 << 2,
UsesFieldKeyword = 1 << 3,
IsExplicitInterfaceImplementation = 1 << 4,
HasInitializer = 1 << 5,
AccessorsHaveImplementation = 1 << 6,
HasExplicitAccessModifier = 1 << 7,
}

// TODO (tomat): consider splitting into multiple subclasses/rare data.
Expand Down Expand Up @@ -75,15 +76,14 @@ protected SourcePropertySymbolBase(
CSharpSyntaxNode syntax,
bool hasGetAccessor,
bool hasSetAccessor,
bool hasAutoPropertyGet,
bool hasAutoPropertySet,
bool isExplicitInterfaceImplementation,
TypeSymbol? explicitInterfaceType,
string? aliasQualifierOpt,
DeclarationModifiers modifiers,
bool hasInitializer,
bool hasExplicitAccessMod,
bool isAutoProperty,
bool hasAutoPropertyGet,
bool hasAutoPropertySet,
bool isExpressionBodied,
bool isInitOnly,
bool accessorsHaveImplementation,
Expand All @@ -94,7 +94,7 @@ protected SourcePropertySymbolBase(
Location location,
BindingDiagnosticBag diagnostics)
{
Debug.Assert(!isExpressionBodied || !isAutoProperty);
Debug.Assert(!isExpressionBodied || !(hasAutoPropertyGet || hasAutoPropertySet));
Debug.Assert(!isExpressionBodied || !hasInitializer);
Debug.Assert(!isExpressionBodied || accessorsHaveImplementation);
Debug.Assert((modifiers & DeclarationModifiers.Required) == 0 || this is SourcePropertySymbol);
Expand All @@ -116,11 +116,10 @@ protected SourcePropertySymbolBase(
}

bool isIndexer = IsIndexer;
if (isAutoProperty)
if ((!hasGetAccessor || hasAutoPropertyGet) && (!hasSetAccessor || hasAutoPropertySet))
{
if (!(!(containingType.IsInterface && !IsStatic) && !IsAbstract && !IsExtern && !isIndexer))
{
isAutoProperty = false;
hasAutoPropertyGet = false;
hasAutoPropertySet = false;
}
Expand All @@ -131,9 +130,14 @@ protected SourcePropertySymbolBase(
_propertyFlags |= Flags.HasExplicitAccessModifier;
}

if (isAutoProperty)
if (hasAutoPropertyGet)
{
_propertyFlags |= Flags.HasAutoPropertyGet;
}

if (hasAutoPropertySet)
{
_propertyFlags |= Flags.IsAutoProperty;
_propertyFlags |= Flags.HasAutoPropertySet;
}

if (usesFieldKeyword)
Expand Down Expand Up @@ -174,7 +178,7 @@ protected SourcePropertySymbolBase(
_name = _lazySourceName = memberName;
}

if (usesFieldKeyword || isAutoProperty || hasInitializer)
if (usesFieldKeyword || hasAutoPropertyGet || hasAutoPropertySet || hasInitializer)
{
Debug.Assert(!IsIndexer);
string fieldName = GeneratedNames.MakeBackingFieldName(_name);
Expand Down Expand Up @@ -666,7 +670,7 @@ protected bool HasExplicitAccessModifier
=> (_propertyFlags & Flags.HasExplicitAccessModifier) != 0;

internal bool IsAutoProperty
=> (_propertyFlags & Flags.IsAutoProperty) != 0;
=> (_propertyFlags & (Flags.HasAutoPropertyGet | Flags.HasAutoPropertySet)) != 0;

protected bool AccessorsHaveImplementation
=> (_propertyFlags & Flags.AccessorsHaveImplementation) != 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ public SynthesizedRecordEqualityContractProperty(SourceMemberContainerTypeSymbol
syntax: (CSharpSyntaxNode)containingType.SyntaxReferences[0].GetSyntax(),
hasGetAccessor: true,
hasSetAccessor: false,
hasAutoPropertyGet: false,
hasAutoPropertySet: false,
isExplicitInterfaceImplementation: false,
explicitInterfaceType: null,
aliasQualifierOpt: null,
Expand All @@ -34,7 +32,8 @@ public SynthesizedRecordEqualityContractProperty(SourceMemberContainerTypeSymbol
},
hasInitializer: false,
hasExplicitAccessMod: false,
isAutoProperty: false,
hasAutoPropertyGet: false,
hasAutoPropertySet: false,
isExpressionBodied: false,
isInitOnly: false,
accessorsHaveImplementation: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,14 @@ public SynthesizedRecordPropertySymbol(
syntax: syntax,
hasGetAccessor: true,
hasSetAccessor: true,
hasAutoPropertyGet: true,
hasAutoPropertySet: true,
isExplicitInterfaceImplementation: false,
explicitInterfaceType: null,
aliasQualifierOpt: null,
modifiers: DeclarationModifiers.Public | (isOverride ? DeclarationModifiers.Override : DeclarationModifiers.None),
hasInitializer: true, // Synthesized record properties always have a synthesized initializer
hasExplicitAccessMod: false,
isAutoProperty: true,
hasAutoPropertyGet: true,
hasAutoPropertySet: true,
isExpressionBodied: false,
isInitOnly: ShouldUseInit(containingType),
accessorsHaveImplementation: true,
Expand Down
54 changes: 50 additions & 4 deletions src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ .maxstack 2
"System.Object C.Q.get",
"C..ctor()"
};
Assert.Equal(expectedMembers, actualMembers);
AssertEx.Equal(expectedMembers, actualMembers);
}

[Fact]
Expand Down Expand Up @@ -147,7 +147,7 @@ .maxstack 2
"System.Object C.Initialize(out System.Object field, System.Object value)",
"C..ctor()"
};
Assert.Equal(expectedMembers, actualMembers);
AssertEx.Equal(expectedMembers, actualMembers);
}

[Fact]
Expand Down Expand Up @@ -230,7 +230,7 @@ C P
"void C.P.set",
"C..ctor()"
};
Assert.Equal(expectedMembers, actualMembers);
AssertEx.Equal(expectedMembers, actualMembers);
}

[Fact]
Expand Down Expand Up @@ -280,7 +280,7 @@ class C
"System.Object C.F(System.Object value)",
"C..ctor()"
};
Assert.Equal(expectedMembers, actualMembers);
AssertEx.Equal(expectedMembers, actualMembers);
}

[Fact]
Expand Down Expand Up @@ -337,9 +337,12 @@ public void ImplicitAccessorBody_01(string typeKind)
{
public static object P1 { get; set { _ = field; } }
public static object P2 { get { return field; } set; }
public static object P3 { get { return null; } set; }
public object Q1 { get; set { _ = field; } }
public object Q2 { get { return field; } set; }
public object Q3 { get { return field; } init; }
public object Q4 { get; set { } }
public object Q5 { get; init { } }
}
class Program
{
Expand Down Expand Up @@ -396,6 +399,49 @@ .maxstack 2
IL_0007: ret
}
""");
if (!typeKind.StartsWith("record"))
{
var comp = (CSharpCompilation)verifier.Compilation;
var actualMembers = comp.GetMember<NamedTypeSymbol>("A").GetMembers().ToTestDisplayStrings();
string readonlyQualifier = typeKind.EndsWith("struct") ? "readonly " : "";
var expectedMembers = new[]
{
"System.Object A.<P1>k__BackingField",
"System.Object A.P1 { get; set; }",
"System.Object A.P1.get",
"void A.P1.set",
"System.Object A.<P2>k__BackingField",
"System.Object A.P2 { get; set; }",
"System.Object A.P2.get",
"void A.P2.set",
"System.Object A.<P3>k__BackingField",
"System.Object A.P3 { get; set; }",
"System.Object A.P3.get",
"void A.P3.set",
"System.Object A.<Q1>k__BackingField",
"System.Object A.Q1 { get; set; }",
readonlyQualifier + "System.Object A.Q1.get",
"void A.Q1.set",
"System.Object A.<Q2>k__BackingField",
"System.Object A.Q2 { get; set; }",
"System.Object A.Q2.get",
"void A.Q2.set",
"System.Object A.<Q3>k__BackingField",
"System.Object A.Q3 { get; init; }",
"System.Object A.Q3.get",
"void modreq(System.Runtime.CompilerServices.IsExternalInit) A.Q3.init",
"System.Object A.<Q4>k__BackingField",
"System.Object A.Q4 { get; set; }",
readonlyQualifier + "System.Object A.Q4.get",
"void A.Q4.set",
"System.Object A.<Q5>k__BackingField",
"System.Object A.Q5 { get; init; }",
readonlyQualifier + "System.Object A.Q5.get",
"void modreq(System.Runtime.CompilerServices.IsExternalInit) A.Q5.init",
"A..ctor()"
};
AssertEx.Equal(expectedMembers, actualMembers);
}
}

[Fact]
Expand Down

0 comments on commit fcc6b42

Please sign in to comment.