-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Records: override abstract inherited properties #45336
Changes from 1 commit
e22ea5e
e065ff0
d1f9199
942fae4
3af055e
8f43626
40c5f83
b9b5038
3028c82
d94b13e
03be993
c9af0db
7971a3b
63b0dae
7a0481b
697f85f
5d5316b
7f6fd4b
f852cf0
f29f2f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3061,26 +3061,30 @@ ImmutableArray<PropertySymbol> addProperties(ImmutableArray<ParameterSymbol> rec | |
int addedCount = 0; | ||
foreach (ParameterSymbol param in recordParameters) | ||
{ | ||
var property = new SynthesizedRecordPropertySymbol(this, param, diagnostics); | ||
_ = memberSignatures.TryGetValue(property, out var existingMember); | ||
existingMember ??= getInheritedMember(property, this); | ||
bool isInherited = false; | ||
var property = new SynthesizedRecordPropertySymbol(this, param, isOverride: false, diagnostics); | ||
if (!memberSignatures.TryGetValue(property, out var existingMember)) | ||
{ | ||
existingMember = getInheritedMember(property, this); | ||
isInherited = true; | ||
} | ||
if (existingMember is null) | ||
{ | ||
existingOrAddedMembers.Add(property); | ||
members.Add(property); | ||
members.Add(property.GetMethod); | ||
members.Add(property.SetMethod); | ||
members.Add(property.BackingField); | ||
|
||
builder.InstanceInitializersForRecordDeclarationWithParameters.Insert(addedCount, new FieldOrPropertyInitializer.Builder(property.BackingField, paramList.Parameters[param.Ordinal])); | ||
addedCount++; | ||
addProperty(property); | ||
} | ||
else if (existingMember is PropertySymbol { IsStatic: false, GetMethod: { } } prop | ||
&& prop.TypeWithAnnotations.Equals(param.TypeWithAnnotations, TypeCompareKind.AllIgnoreOptions)) | ||
{ | ||
// There already exists a member corresponding to the candidate synthesized property. | ||
// The deconstructor is specified to simply assign from this property to the corresponding out parameter. | ||
existingOrAddedMembers.Add(prop); | ||
if (isInherited && existingMember.IsAbstract) | ||
{ | ||
addProperty(new SynthesizedRecordPropertySymbol(this, param, isOverride: true, diagnostics)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It doesn't look like we are properly inheriting custom modifiers for the property and the accessors. We should do what is done for regular properties and their accessors. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
else | ||
{ | ||
// Deconstruct() is specified to simply assign from this property to the corresponding out parameter. | ||
existingOrAddedMembers.Add(prop); | ||
} | ||
} | ||
else | ||
{ | ||
|
@@ -3090,6 +3094,18 @@ ImmutableArray<PropertySymbol> addProperties(ImmutableArray<ParameterSymbol> rec | |
param.TypeWithAnnotations, | ||
param.Name); | ||
} | ||
|
||
void addProperty(SynthesizedRecordPropertySymbol property) | ||
{ | ||
existingOrAddedMembers.Add(property); | ||
members.Add(property); | ||
members.Add(property.GetMethod); | ||
members.Add(property.SetMethod); | ||
members.Add(property.BackingField); | ||
|
||
builder.InstanceInitializersForRecordDeclarationWithParameters.Insert(addedCount, new FieldOrPropertyInitializer.Builder(property.BackingField, paramList.Parameters[param.Ordinal])); | ||
addedCount++; | ||
} | ||
} | ||
|
||
#if DEBUG | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,15 +16,13 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols | |
internal sealed class SynthesizedRecordPropertySymbol : SourceOrRecordPropertySymbol | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It looks like changes made in this file are going to conflict with #44883 #Closed |
||
{ | ||
private readonly ParameterSymbol _backingParameter; | ||
|
||
internal override SynthesizedBackingFieldSymbol BackingField { get; } | ||
public override MethodSymbol GetMethod { get; } | ||
public override MethodSymbol SetMethod { get; } | ||
public override NamedTypeSymbol ContainingType { get; } | ||
|
||
public SynthesizedRecordPropertySymbol( | ||
NamedTypeSymbol containingType, | ||
ParameterSymbol backingParameter, | ||
DiagnosticBag diagnostics) | ||
public SynthesizedRecordPropertySymbol(NamedTypeSymbol containingType, ParameterSymbol backingParameter, bool isOverride, DiagnosticBag diagnostics) | ||
: base(backingParameter.Locations[0]) | ||
{ | ||
ContainingType = containingType; | ||
|
@@ -38,6 +36,7 @@ public SynthesizedRecordPropertySymbol( | |
hasInitializer: true); | ||
GetMethod = new GetAccessorSymbol(this, name); | ||
SetMethod = new InitAccessorSymbol(this, name, diagnostics); | ||
IsOverride = isOverride; | ||
} | ||
|
||
public ParameterSymbol BackingParameter => _backingParameter; | ||
|
@@ -68,7 +67,7 @@ public SynthesizedRecordPropertySymbol( | |
|
||
public override bool IsVirtual => false; | ||
|
||
public override bool IsOverride => false; | ||
public override bool IsOverride { get; } | ||
|
||
public override bool IsAbstract => false; | ||
|
||
|
@@ -98,46 +97,42 @@ public SynthesizedRecordPropertySymbol( | |
|
||
public override SyntaxList<AttributeListSyntax> AttributeDeclarationSyntaxList => new SyntaxList<AttributeListSyntax>(); | ||
|
||
private sealed class GetAccessorSymbol : SynthesizedInstanceMethodSymbol | ||
private abstract class AccessorSymbol : SynthesizedInstanceMethodSymbol | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
See also #44883 (comment), this might make it easier to deal with overriding. #Closed |
||
{ | ||
private readonly SynthesizedRecordPropertySymbol _property; | ||
protected readonly SynthesizedRecordPropertySymbol _property; | ||
|
||
public override string Name { get; } | ||
public abstract override string Name { get; } | ||
|
||
public GetAccessorSymbol(SynthesizedRecordPropertySymbol property, string paramName) | ||
protected AccessorSymbol(SynthesizedRecordPropertySymbol property) | ||
{ | ||
_property = property; | ||
Name = SourcePropertyAccessorSymbol.GetAccessorName( | ||
paramName, | ||
getNotSet: true, | ||
isWinMdOutput: false /* unused for getters */); | ||
} | ||
|
||
public override MethodKind MethodKind => MethodKind.PropertyGet; | ||
public abstract override MethodKind MethodKind { get; } | ||
|
||
public override int Arity => 0; | ||
|
||
public override bool IsExtensionMethod => false; | ||
|
||
public override bool HidesBaseMethodsByName => false; | ||
public override bool HidesBaseMethodsByName => false; // PROTOTYPE: Should this be true if it hides? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This PR is targeting master, no PROTOTYPE comments allowed. Also, I believe C# never hides by name. #Closed |
||
|
||
public override bool IsVararg => false; | ||
|
||
public override bool ReturnsVoid => false; | ||
public override bool ReturnsVoid => ReturnType.SpecialType == SpecialType.System_Void; | ||
|
||
public override bool IsAsync => false; | ||
|
||
public override RefKind RefKind => RefKind.None; | ||
|
||
public override TypeWithAnnotations ReturnTypeWithAnnotations => _property.TypeWithAnnotations; | ||
public abstract override TypeWithAnnotations ReturnTypeWithAnnotations { get; } | ||
|
||
public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None; | ||
|
||
public override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotations => ImmutableArray<TypeWithAnnotations>.Empty; | ||
|
||
public override ImmutableArray<TypeParameterSymbol> TypeParameters => ImmutableArray<TypeParameterSymbol>.Empty; | ||
|
||
public override ImmutableArray<ParameterSymbol> Parameters => _property.Parameters; | ||
public abstract override ImmutableArray<ParameterSymbol> Parameters { get; } | ||
|
||
public override ImmutableArray<MethodSymbol> ExplicitInterfaceImplementations => ImmutableArray<MethodSymbol>.Empty; | ||
|
||
|
@@ -187,12 +182,33 @@ internal override ImmutableArray<string> GetAppliedConditionalSymbols() | |
internal override IEnumerable<SecurityAttribute> GetSecurityInformation() | ||
=> Array.Empty<SecurityAttribute>(); | ||
|
||
internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => false; | ||
internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => !this.IsOverride; | ||
|
||
internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) => false; | ||
internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) => this.IsOverride; | ||
|
||
internal override bool SynthesizesLoweredBoundBody => true; | ||
|
||
internal abstract override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics); | ||
} | ||
|
||
private sealed class GetAccessorSymbol : AccessorSymbol | ||
{ | ||
public override string Name { get; } | ||
|
||
public GetAccessorSymbol(SynthesizedRecordPropertySymbol property, string paramName) : base(property) | ||
{ | ||
Name = SourcePropertyAccessorSymbol.GetAccessorName( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In case of an override, shouldn't we inherit the name of the accessor? #Closed |
||
paramName, | ||
getNotSet: true, | ||
isWinMdOutput: false /* unused for getters */); | ||
} | ||
|
||
public override MethodKind MethodKind => MethodKind.PropertyGet; | ||
|
||
public override TypeWithAnnotations ReturnTypeWithAnnotations => _property.TypeWithAnnotations; | ||
|
||
public override ImmutableArray<ParameterSymbol> Parameters => _property.Parameters; // PROTOTYPE: Shouldn't the parameters be owned by this symbol? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, they should be. I think #44883 addresses that. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics) | ||
{ | ||
// Method body: | ||
|
@@ -208,19 +224,17 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, | |
} | ||
} | ||
|
||
private sealed class InitAccessorSymbol : SynthesizedInstanceMethodSymbol | ||
private sealed class InitAccessorSymbol : AccessorSymbol | ||
{ | ||
private readonly SynthesizedRecordPropertySymbol _property; | ||
|
||
public override TypeWithAnnotations ReturnTypeWithAnnotations { get; } | ||
public override string Name { get; } | ||
|
||
public InitAccessorSymbol( | ||
SynthesizedRecordPropertySymbol property, | ||
string paramName, | ||
DiagnosticBag diagnostics) | ||
DiagnosticBag diagnostics) : | ||
base(property) | ||
{ | ||
_property = property; | ||
Name = SourcePropertyAccessorSymbol.GetAccessorName( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In case of an override, shouldn't we inherit the name of the accessor? #Closed |
||
paramName, | ||
getNotSet: false, | ||
|
@@ -243,87 +257,13 @@ public InitAccessorSymbol( | |
|
||
public override MethodKind MethodKind => MethodKind.PropertySet; | ||
|
||
public override int Arity => 0; | ||
|
||
public override bool IsExtensionMethod => false; | ||
|
||
public override bool HidesBaseMethodsByName => false; | ||
|
||
public override bool IsVararg => false; | ||
|
||
public override bool ReturnsVoid => true; | ||
|
||
public override bool IsAsync => false; | ||
|
||
public override RefKind RefKind => RefKind.None; | ||
|
||
public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None; | ||
|
||
public override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotations => ImmutableArray<TypeWithAnnotations>.Empty; | ||
|
||
public override ImmutableArray<TypeParameterSymbol> TypeParameters => ImmutableArray<TypeParameterSymbol>.Empty; | ||
|
||
public override ImmutableArray<ParameterSymbol> Parameters => ImmutableArray.Create(SynthesizedParameterSymbol.Create( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Every time allocating a new parameter. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
this, | ||
_property.TypeWithAnnotations, | ||
ordinal: 0, | ||
RefKind.None, | ||
name: ParameterSymbol.ValueParameterName)); | ||
|
||
public override ImmutableArray<MethodSymbol> ExplicitInterfaceImplementations => ImmutableArray<MethodSymbol>.Empty; | ||
|
||
public override ImmutableArray<CustomModifier> RefCustomModifiers => _property.RefCustomModifiers; | ||
|
||
public override Symbol AssociatedSymbol => _property; | ||
|
||
public override Symbol ContainingSymbol => _property.ContainingSymbol; | ||
|
||
public override ImmutableArray<Location> Locations => _property.Locations; | ||
|
||
public override Accessibility DeclaredAccessibility => _property.DeclaredAccessibility; | ||
|
||
public override bool IsStatic => _property.IsStatic; | ||
|
||
public override bool IsVirtual => _property.IsVirtual; | ||
|
||
public override bool IsOverride => _property.IsOverride; | ||
|
||
public override bool IsAbstract => _property.IsAbstract; | ||
|
||
public override bool IsSealed => _property.IsSealed; | ||
|
||
public override bool IsExtern => _property.IsExtern; | ||
|
||
public override ImmutableHashSet<string> ReturnNotNullIfParameterNotNull => ImmutableHashSet<string>.Empty; | ||
|
||
internal override bool HasSpecialName => _property.HasSpecialName; | ||
|
||
internal override MethodImplAttributes ImplementationAttributes => MethodImplAttributes.Managed; | ||
|
||
internal override bool HasDeclarativeSecurity => false; | ||
|
||
internal override MarshalPseudoCustomAttributeData? ReturnValueMarshallingInformation => null; | ||
|
||
internal override bool RequiresSecurityObject => false; | ||
|
||
internal override CallingConvention CallingConvention => CallingConvention.HasThis; | ||
|
||
internal override bool GenerateDebugInfo => false; | ||
|
||
public override DllImportData? GetDllImportData() => null; | ||
|
||
internal override ImmutableArray<string> GetAppliedConditionalSymbols() | ||
=> ImmutableArray<string>.Empty; | ||
|
||
internal override IEnumerable<SecurityAttribute> GetSecurityInformation() | ||
=> Array.Empty<SecurityAttribute>(); | ||
|
||
internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => false; | ||
|
||
internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) => false; | ||
|
||
internal override bool SynthesizesLoweredBoundBody => true; | ||
|
||
internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics) | ||
{ | ||
// Method body: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current specification, that I think should be up to date given the time when the #44618 was created, and the rules stated in the issue disagree.
The issue:
The specification:
Could you please reconcile the differences? This is not blocking
#Resolved