Skip to content

Commit

Permalink
Record structs: add ToString and PrintMembers (#52012)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv authored Mar 29, 2021
1 parent 157512d commit 7574293
Show file tree
Hide file tree
Showing 7 changed files with 1,040 additions and 173 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3509,7 +3509,6 @@ private void AddSynthesizedRecordMembersIfNecessary(MembersAndInitializersBuilde
}
}


if (isRecordClass)
{
addCopyCtor(primaryAndCopyCtorAmbiguity);
Expand All @@ -3534,12 +3533,8 @@ private void AddSynthesizedRecordMembersIfNecessary(MembersAndInitializersBuilde
diagnostics.Add(ErrorCode.WRN_RecordEqualsWithoutGetHashCode, thisEquals.Locations[0], declaration.Name);
}

// PROTOTYPE(record-structs): update for record structs
if (isRecordClass)
{
var printMembers = addPrintMembersMethod();
addToStringMethod(printMembers);
}
var printMembers = addPrintMembersMethod();
addToStringMethod(printMembers);

memberSignatures.Free();

Expand Down Expand Up @@ -3671,7 +3666,7 @@ MethodSymbol addPrintMembersMethod()
else
{
printMembersMethod = (MethodSymbol)existingPrintMembersMethod;
if (this.IsSealed && this.BaseTypeNoUseSiteDiagnostics.IsObjectType())
if (!isRecordClass || (this.IsSealed && this.BaseTypeNoUseSiteDiagnostics.IsObjectType()))
{
if (printMembersMethod.DeclaredAccessibility != Accessibility.Private)
{
Expand All @@ -3690,7 +3685,7 @@ MethodSymbol addPrintMembersMethod()
diagnostics.Add(ErrorCode.ERR_SignatureMismatchInRecord, printMembersMethod.Locations[0], printMembersMethod, targetMethod.ReturnType);
}
}
else
else if (isRecordClass)
{
SynthesizedRecordPrintMembers.VerifyOverridesPrintMembersFromBase(printMembersMethod, diagnostics);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,7 @@ protected override void CheckBase(BindingDiagnosticBag diagnostics)

if (declaration.Kind == DeclarationKind.Record)
{
if (SynthesizedRecordClone.FindValidCloneMethod(localBase, ref useSiteInfo) is null ||
SynthesizedRecordPrintMembers.FindValidPrintMembersMethod(localBase, DeclaringCompilation) is null)
if (SynthesizedRecordClone.FindValidCloneMethod(localBase, ref useSiteInfo) is null)
{
diagnostics.Add(ErrorCode.ERR_BadRecordBase, baseLocation);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ public SynthesizedRecordPrintMembers(

protected override DeclarationModifiers MakeDeclarationModifiers(DeclarationModifiers allowedModifiers, BindingDiagnosticBag diagnostics)
{
var result = (ContainingType.BaseTypeNoUseSiteDiagnostics.IsObjectType() && ContainingType.IsSealed) ?
var result = (ContainingType.IsRecordStruct || (ContainingType.BaseTypeNoUseSiteDiagnostics.IsObjectType() && ContainingType.IsSealed)) ?
DeclarationModifiers.Private :
DeclarationModifiers.Protected;

if (virtualPrintInBase() is object)
if (ContainingType.IsRecord && !ContainingType.BaseTypeNoUseSiteDiagnostics.IsObjectType())
{
result |= DeclarationModifiers.Override;
}
Expand All @@ -48,21 +48,14 @@ protected override DeclarationModifiers MakeDeclarationModifiers(DeclarationModi
#endif
return result;

MethodSymbol? virtualPrintInBase()
#if DEBUG
bool modifiersAreValid(DeclarationModifiers modifiers)
{
NamedTypeSymbol baseType = ContainingType.BaseTypeNoUseSiteDiagnostics;

if (!baseType.IsObjectType())
if (ContainingType.IsRecordStruct)
{
return FindValidPrintMembersMethod(baseType, ContainingType.DeclaringCompilation);
return modifiers == DeclarationModifiers.Private;
}

return null;
}

#if DEBUG
static bool modifiersAreValid(DeclarationModifiers modifiers)
{
if ((modifiers & DeclarationModifiers.AccessibilityMask) != DeclarationModifiers.Private &&
(modifiers & DeclarationModifiers.AccessibilityMask) != DeclarationModifiers.Protected)
{
Expand All @@ -88,17 +81,31 @@ protected override (TypeWithAnnotations ReturnType, ImmutableArray<ParameterSymb
{
var compilation = DeclaringCompilation;
var location = ReturnTypeLocation;
var annotation = ContainingType.IsRecordStruct ? NullableAnnotation.Oblivious : NullableAnnotation.NotAnnotated;
return (ReturnType: TypeWithAnnotations.Create(Binder.GetSpecialType(compilation, SpecialType.System_Boolean, location, diagnostics)),
Parameters: ImmutableArray.Create<ParameterSymbol>(
new SourceSimpleParameterSymbol(owner: this,
TypeWithAnnotations.Create(Binder.GetWellKnownType(compilation, WellKnownType.System_Text_StringBuilder, diagnostics, location), NullableAnnotation.NotAnnotated),
TypeWithAnnotations.Create(Binder.GetWellKnownType(compilation, WellKnownType.System_Text_StringBuilder, diagnostics, location), annotation),
ordinal: 0, RefKind.None, "builder", isDiscard: false, Locations)),
IsVararg: false,
DeclaredConstraintsForOverrideOrImplementation: ImmutableArray<TypeParameterConstraintClause>.Empty);
}

protected override int GetParameterCountFromSyntax() => 1;

protected override void MethodChecks(BindingDiagnosticBag diagnostics)
{
base.MethodChecks(diagnostics);

var overridden = OverriddenMethod;

if (overridden is object &&
!overridden.ContainingType.Equals(ContainingType.BaseTypeNoUseSiteDiagnostics, TypeCompareKind.AllIgnoreOptions))
{
diagnostics.Add(ErrorCode.ERR_DoesNotOverrideBaseMethod, Locations[0], this, ContainingType.BaseTypeNoUseSiteDiagnostics);
}
}

internal override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics)
{
var F = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics);
Expand All @@ -113,27 +120,29 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState,
return;
}

ArrayBuilder<BoundStatement>? block = printableMembers.IsEmpty ? null : ArrayBuilder<BoundStatement>.GetInstance();
ArrayBuilder<BoundStatement> block;
BoundParameter builder = F.Parameter(this.Parameters[0]);
if (ContainingType.BaseTypeNoUseSiteDiagnostics.IsObjectType())
if (ContainingType.BaseTypeNoUseSiteDiagnostics.IsObjectType() || ContainingType.IsRecordStruct)
{
if (printableMembers.IsEmpty)
{
// return false;
F.CloseMethod(F.Return(F.Literal(false)));
return;
}
block = ArrayBuilder<BoundStatement>.GetInstance();
}
else
{
MethodSymbol? printMethod = FindValidPrintMembersMethod(ContainingType.BaseTypeNoUseSiteDiagnostics, DeclaringCompilation);
if (printMethod is null)
MethodSymbol? basePrintMethod = OverriddenMethod;
if (basePrintMethod is null ||
basePrintMethod.ReturnType.SpecialType != SpecialType.System_Boolean)
{
F.CloseMethod(F.ThrowNull()); // an error was reported in base checks already
return;
}

var basePrintCall = F.Call(receiver: F.Base(ContainingType.BaseTypeNoUseSiteDiagnostics), printMethod, builder);
var basePrintCall = F.Call(receiver: F.Base(ContainingType.BaseTypeNoUseSiteDiagnostics), basePrintMethod, builder);
if (printableMembers.IsEmpty)
{
// return base.PrintMembers(builder);
Expand All @@ -142,13 +151,14 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState,
}
else
{
block = ArrayBuilder<BoundStatement>.GetInstance();
// if (base.PrintMembers(builder))
// builder.Append(", ")
block!.Add(F.If(basePrintCall, makeAppendString(F, builder, ", ")));
block.Add(F.If(basePrintCall, makeAppendString(F, builder, ", ")));
}
}

Debug.Assert(!printableMembers.IsEmpty && block is object);
Debug.Assert(!printableMembers.IsEmpty);

for (var i = 0; i < printableMembers.Length; i++)
{
Expand Down Expand Up @@ -227,41 +237,6 @@ static bool isPrintable(Symbol m)
}
}

internal static MethodSymbol? FindValidPrintMembersMethod(TypeSymbol containingType, CSharpCompilation compilation)
{
if (containingType.IsObjectType())
{
return null;
}

MethodSymbol? candidate = null;
var stringBuilder = TypeWithAnnotations.Create(compilation.GetWellKnownType(WellKnownType.System_Text_StringBuilder));

foreach (var member in containingType.GetMembers(WellKnownMemberNames.PrintMembersMethodName))
{
if (member is MethodSymbol { DeclaredAccessibility: Accessibility.Protected, IsStatic: false, ParameterCount: 1, Arity: 0 } method &&
method.ParameterTypesWithAnnotations[0].Equals(stringBuilder, TypeCompareKind.AllIgnoreOptions))
{
if (candidate is object)
{
// An ambiguity case, can come from metadata, treat as an error for simplicity.
return null;
}

candidate = method;
}
}

if (candidate is null ||
!(containingType.IsSealed || candidate.IsOverride || candidate.IsVirtual) ||
candidate.ReturnType.SpecialType != SpecialType.System_Boolean)
{
return null;
}

return candidate;
}

internal static void VerifyOverridesPrintMembersFromBase(MethodSymbol overriding, BindingDiagnosticBag diagnostics)
{
NamedTypeSymbol baseType = overriding.ContainingType.BaseTypeNoUseSiteDiagnostics;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ protected override (TypeWithAnnotations ReturnType, ImmutableArray<ParameterSymb
{
var compilation = DeclaringCompilation;
var location = ReturnTypeLocation;
return (ReturnType: TypeWithAnnotations.Create(Binder.GetSpecialType(compilation, SpecialType.System_String, location, diagnostics)),
var annotation = ContainingType.IsRecordStruct ? NullableAnnotation.Oblivious : NullableAnnotation.NotAnnotated;
return (ReturnType: TypeWithAnnotations.Create(Binder.GetSpecialType(compilation, SpecialType.System_String, location, diagnostics), annotation),
Parameters: ImmutableArray<ParameterSymbol>.Empty,
IsVararg: false,
DeclaredConstraintsForOverrideOrImplementation: ImmutableArray<TypeParameterConstraintClause>.Empty);
Expand Down
Loading

0 comments on commit 7574293

Please sign in to comment.