From 757429320aa2638b4f1432570bf6dc5b4074fae8 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 29 Mar 2021 14:43:12 -0700 Subject: [PATCH] Record structs: add ToString and PrintMembers (#52012) --- .../Source/SourceMemberContainerSymbol.cs | 13 +- .../Source/SourceNamedTypeSymbol_Bases.cs | 3 +- .../Records/SynthesizedRecordPrintMembers.cs | 87 +- .../Records/SynthesizedRecordToString.cs | 3 +- .../Semantic/Semantics/RecordStructTests.cs | 843 +++++++++++++++++- .../Test/Semantic/Semantics/RecordTests.cs | 256 ++++-- .../Test/Symbol/Symbols/Source/RecordTests.cs | 8 +- 7 files changed, 1040 insertions(+), 173 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 8f1b460bcea5c..620d757ba0962 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -3509,7 +3509,6 @@ private void AddSynthesizedRecordMembersIfNecessary(MembersAndInitializersBuilde } } - if (isRecordClass) { addCopyCtor(primaryAndCopyCtorAmbiguity); @@ -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(); @@ -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) { @@ -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); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Bases.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Bases.cs index e4d5b5e5a29c0..985687c269754 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Bases.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Bases.cs @@ -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); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPrintMembers.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPrintMembers.cs index c1d1e92d24953..7066d331c7426 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPrintMembers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPrintMembers.cs @@ -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; } @@ -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) { @@ -88,10 +81,11 @@ protected override (TypeWithAnnotations ReturnType, ImmutableArray( 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.Empty); @@ -99,6 +93,19 @@ protected override (TypeWithAnnotations ReturnType, ImmutableArray 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); @@ -113,9 +120,9 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, return; } - ArrayBuilder? block = printableMembers.IsEmpty ? null : ArrayBuilder.GetInstance(); + ArrayBuilder block; BoundParameter builder = F.Parameter(this.Parameters[0]); - if (ContainingType.BaseTypeNoUseSiteDiagnostics.IsObjectType()) + if (ContainingType.BaseTypeNoUseSiteDiagnostics.IsObjectType() || ContainingType.IsRecordStruct) { if (printableMembers.IsEmpty) { @@ -123,17 +130,19 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, F.CloseMethod(F.Return(F.Literal(false))); return; } + block = ArrayBuilder.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); @@ -142,13 +151,14 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, } else { + block = ArrayBuilder.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++) { @@ -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; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordToString.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordToString.cs index 0d2298cadbd01..9ca58140fface 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordToString.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordToString.cs @@ -33,7 +33,8 @@ protected override (TypeWithAnnotations ReturnType, ImmutableArray.Empty, IsVararg: false, DeclaredConstraintsForOverrideOrImplementation: ImmutableArray.Empty); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs index 72b6296d363d1..a2fc2b2d6ba05 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs @@ -757,6 +757,8 @@ int I.M(string s) AssertEx.Equal(new[] { "System.Int32 S.M(System.String s)", + "System.String S.ToString()", + "System.Boolean S.PrintMembers(System.Text.StringBuilder builder)", "System.Boolean S.op_Inequality(S r1, S r2)", "System.Boolean S.op_Equality(S r1, S r2)", "System.Int32 S.GetHashCode()", @@ -868,7 +870,7 @@ public record struct S public int Property { get; set; } = 43; } "; - // PROTOTYPE(record-structs): this will be allowed in C# 10 + // PROTOTYPE(record-structs): this will be allowed in C# 10, or we need to improve the message var comp = CreateCompilation(src); comp.VerifyDiagnostics( // (4,16): error CS0573: 'S': cannot have instance property or field initializers in structs @@ -1054,6 +1056,8 @@ public void M(string s) { } "X", "M", "M", + "ToString", + "PrintMembers", "op_Inequality", "op_Equality", "GetHashCode", @@ -1708,6 +1712,8 @@ record struct C(int X, int X) "get_X", "set_X", "X", + "ToString", + "PrintMembers", "op_Inequality", "op_Equality", "GetHashCode", @@ -1756,6 +1762,8 @@ public void set_X() { } "void C.set_X()", "System.Int32 C.get_Y(System.Int32 value)", "System.Int32 C.set_Y(System.Int32 value)", + "System.String C.ToString()", + "System.Boolean C.PrintMembers(System.Text.StringBuilder builder)", "System.Boolean C.op_Inequality(C r1, C r2)", "System.Boolean C.op_Equality(C r1, C r2)", "System.Int32 C.GetHashCode()", @@ -1909,6 +1917,7 @@ public class Object { public virtual bool Equals(object x) => throw null; public virtual int GetHashCode() => throw null; + public virtual string ToString() => throw null; } public class Exception { } public class ValueType @@ -1916,6 +1925,7 @@ public class ValueType public bool X { get; set; } } public class Attribute { } + public class String { } public struct Void { } public struct Boolean { } public struct Int32 { } @@ -1929,6 +1939,14 @@ public abstract class EqualityComparer public abstract int GetHashCode(T t); } } +namespace System.Text +{ + public class StringBuilder + { + public StringBuilder Append(string s) => null; + public StringBuilder Append(object s) => null; + } +} "; var corlibRef = CreateEmptyCompilation(corlib_cs).EmitToImageReference(); @@ -1991,6 +2009,7 @@ public class Object { public virtual bool Equals(object x) => throw null; public virtual int GetHashCode() => throw null; + public virtual string ToString() => throw null; } public class Exception { } public class ValueType @@ -1998,6 +2017,7 @@ public class ValueType public static bool X { get; set; } } public class Attribute { } + public class String { } public struct Void { } public struct Boolean { } public struct Int32 { } @@ -2011,6 +2031,14 @@ public abstract class EqualityComparer public abstract int GetHashCode(T t); } } +namespace System.Text +{ + public class StringBuilder + { + public StringBuilder Append(string s) => null; + public StringBuilder Append(object s) => null; + } +} "; var corlibRef = CreateEmptyCompilation(corlib_cs).EmitToImageReference(); var src = @" @@ -3801,6 +3829,9 @@ public bool Equals(A other) // (2,15): error CS0518: Predefined type 'System.Boolean' is not defined or imported // record struct A Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "A").WithArguments("System.Boolean").WithLocation(2, 15), + // (2,15): error CS0518: Predefined type 'System.Boolean' is not defined or imported + // record struct A + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "A").WithArguments("System.Boolean").WithLocation(2, 15), // (4,12): error CS0518: Predefined type 'System.Boolean' is not defined or imported // public bool Equals(A other) Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "bool").WithArguments("System.Boolean").WithLocation(4, 12), @@ -3837,12 +3868,15 @@ record struct A public void RecordEquals_RecordEqualsInValueType() { var src = @" +public record struct A; + namespace System { public class Object { public virtual bool Equals(object x) => throw null; public virtual int GetHashCode() => throw null; + public virtual string ToString() => throw null; } public class Exception { } public class ValueType @@ -3850,6 +3884,7 @@ public class ValueType public bool Equals(A x) => throw null; } public class Attribute { } + public class String { } public struct Void { } public struct Boolean { } public struct Int32 { } @@ -3863,7 +3898,14 @@ public abstract class EqualityComparer public abstract int GetHashCode(T t); } } -public record struct A; +namespace System.Text +{ + public class StringBuilder + { + public StringBuilder Append(string s) => null; + public StringBuilder Append(object s) => null; + } +} "; var comp = CreateEmptyCompilation(src, parseOptions: TestOptions.RegularPreview); @@ -4078,11 +4120,14 @@ record struct A public void GetHashCode_GetHashCodeInValueType() { var src = @" +public record struct A; + namespace System { public class Object { public virtual bool Equals(object x) => throw null; + public virtual string ToString() => throw null; } public class Exception { } public class ValueType @@ -4090,6 +4135,7 @@ public class ValueType public virtual int GetHashCode() => throw null; } public class Attribute { } + public class String { } public struct Void { } public struct Boolean { } public struct Int32 { } @@ -4103,16 +4149,23 @@ public abstract class EqualityComparer public abstract int GetHashCode(T t); } } -public record struct A; +namespace System.Text +{ + public class StringBuilder + { + public StringBuilder Append(string s) => null; + public StringBuilder Append(object s) => null; + } +} "; var comp = CreateEmptyCompilation(src, parseOptions: TestOptions.RegularPreview); comp.VerifyEmitDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), - // (27,22): error CS8869: 'A.GetHashCode()' does not override expected method from 'object'. + // (2,22): error CS8869: 'A.GetHashCode()' does not override expected method from 'object'. // public record struct A; - Diagnostic(ErrorCode.ERR_DoesNotOverrideMethodFromObject, "A").WithArguments("A.GetHashCode()").WithLocation(27, 22) + Diagnostic(ErrorCode.ERR_DoesNotOverrideMethodFromObject, "A").WithArguments("A.GetHashCode()").WithLocation(2, 22) ); } @@ -4120,63 +4173,29 @@ public record struct A; public void GetHashCode_MissingEqualityComparer_EmptyRecord() { var src = @" -namespace System -{ - public class Object - { - public virtual bool Equals(object x) => throw null; - public virtual int GetHashCode() => throw null; - } - public class Exception { } - public class ValueType { } - public class Attribute { } - public struct Void { } - public struct Boolean { } - public struct Int32 { } - public interface IEquatable { } -} public record struct A; "; - var comp = CreateEmptyCompilation(src, parseOptions: TestOptions.RegularPreview); - - comp.VerifyEmitDiagnostics( - // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. - Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1) - ); + var comp = CreateCompilation(src); + comp.MakeTypeMissing(WellKnownType.System_Collections_Generic_EqualityComparer_T); + comp.VerifyEmitDiagnostics(); } [Fact] public void GetHashCode_MissingEqualityComparer_NonEmptyRecord() { var src = @" -namespace System -{ - public class Object - { - public virtual bool Equals(object x) => throw null; - public virtual int GetHashCode() => throw null; - } - public class Exception { } - public class ValueType { } - public class Attribute { } - public struct Void { } - public struct Boolean { } - public struct Int32 { } - public interface IEquatable { } -} public record struct A(int I); "; - var comp = CreateEmptyCompilation(src, parseOptions: TestOptions.RegularPreview); + var comp = CreateCompilation(src); + comp.MakeTypeMissing(WellKnownType.System_Collections_Generic_EqualityComparer_T); comp.VerifyEmitDiagnostics( - // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. - Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), - // (17,1): error CS0656: Missing compiler required member 'System.Collections.Generic.EqualityComparer`1.GetHashCode' + // (2,1): error CS0656: Missing compiler required member 'System.Collections.Generic.EqualityComparer`1.GetHashCode' // public record struct A(int I); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "public record struct A(int I);").WithArguments("System.Collections.Generic.EqualityComparer`1", "GetHashCode").WithLocation(17, 1), - // (17,1): error CS0656: Missing compiler required member 'System.Collections.Generic.EqualityComparer`1.get_Default' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "public record struct A(int I);").WithArguments("System.Collections.Generic.EqualityComparer`1", "GetHashCode").WithLocation(2, 1), + // (2,1): error CS0656: Missing compiler required member 'System.Collections.Generic.EqualityComparer`1.get_Default' // public record struct A(int I); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "public record struct A(int I);").WithArguments("System.Collections.Generic.EqualityComparer`1", "get_Default").WithLocation(17, 1) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "public record struct A(int I);").WithArguments("System.Collections.Generic.EqualityComparer`1", "get_Default").WithLocation(2, 1) ); } @@ -4443,5 +4462,733 @@ public record struct RecordB(); AssertEx.SetEqual(new[] { "System.Boolean RecordB.op_Equality(RecordB r1, RecordB r2)" }, b.GetSimpleNonTypeMembers("op_Equality").ToTestDisplayStrings()); } + + [Fact] + public void ToString_NestedRecord() + { + var src = @" +var c1 = new Outer.C1(42); +System.Console.Write(c1.ToString()); + +public class Outer +{ + public record struct C1(int I1); +} +"; + + var compDebug = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe); + var compRelease = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseExe); + CompileAndVerify(compDebug, expectedOutput: "C1 { I1 = 42 }"); + compDebug.VerifyEmitDiagnostics(); + + CompileAndVerify(compRelease, expectedOutput: "C1 { I1 = 42 }"); + compRelease.VerifyEmitDiagnostics(); + } + + [Fact] + public void ToString_TopLevelRecord_Empty() + { + var src = @" +var c1 = new C1(); +System.Console.Write(c1.ToString()); + +record struct C1; +"; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + var v = CompileAndVerify(comp, expectedOutput: "C1 { }"); + + var print = comp.GetMember("C1." + WellKnownMemberNames.PrintMembersMethodName); + Assert.Equal(Accessibility.Private, print.DeclaredAccessibility); + Assert.False(print.IsOverride); + Assert.False(print.IsVirtual); + Assert.False(print.IsAbstract); + Assert.False(print.IsSealed); + Assert.True(print.IsImplicitlyDeclared); + + var toString = comp.GetMember("C1." + WellKnownMemberNames.ObjectToString); + Assert.Equal(Accessibility.Public, toString.DeclaredAccessibility); + Assert.True(toString.IsOverride); + Assert.False(toString.IsVirtual); + Assert.False(toString.IsAbstract); + Assert.False(toString.IsSealed); + Assert.True(toString.IsImplicitlyDeclared); + + v.VerifyIL("C1." + WellKnownMemberNames.PrintMembersMethodName, @" +{ + // Code size 2 (0x2) + .maxstack 1 + IL_0000: ldc.i4.0 + IL_0001: ret +} +"); + v.VerifyIL("C1." + WellKnownMemberNames.ObjectToString, @" +{ + // Code size 70 (0x46) + .maxstack 2 + .locals init (System.Text.StringBuilder V_0) + IL_0000: newobj ""System.Text.StringBuilder..ctor()"" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldstr ""C1"" + IL_000c: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_0011: pop + IL_0012: ldloc.0 + IL_0013: ldstr "" { "" + IL_0018: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_001d: pop + IL_001e: ldarg.0 + IL_001f: ldloc.0 + IL_0020: call ""bool C1.PrintMembers(System.Text.StringBuilder)"" + IL_0025: brfalse.s IL_0033 + IL_0027: ldloc.0 + IL_0028: ldstr "" "" + IL_002d: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_0032: pop + IL_0033: ldloc.0 + IL_0034: ldstr ""}"" + IL_0039: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_003e: pop + IL_003f: ldloc.0 + IL_0040: callvirt ""string object.ToString()"" + IL_0045: ret +} +"); + } + + [Fact] + public void ToString_TopLevelRecord_MissingStringBuilder() + { + var src = @" +record struct C1; +"; + + var comp = CreateCompilation(src); + comp.MakeTypeMissing(WellKnownType.System_Text_StringBuilder); + comp.VerifyEmitDiagnostics( + // (2,1): error CS0518: Predefined type 'System.Text.StringBuilder' is not defined or imported + // record struct C1; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "record struct C1;").WithArguments("System.Text.StringBuilder").WithLocation(2, 1), + // (2,1): error CS0656: Missing compiler required member 'System.Text.StringBuilder..ctor' + // record struct C1; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "record struct C1;").WithArguments("System.Text.StringBuilder", ".ctor").WithLocation(2, 1), + // (2,15): error CS0518: Predefined type 'System.Text.StringBuilder' is not defined or imported + // record struct C1; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "C1").WithArguments("System.Text.StringBuilder").WithLocation(2, 15) + ); + } + + [Fact] + public void ToString_TopLevelRecord_MissingStringBuilderCtor() + { + var src = @" +record struct C1; +"; + + var comp = CreateCompilation(src); + comp.MakeMemberMissing(WellKnownMember.System_Text_StringBuilder__ctor); + comp.VerifyEmitDiagnostics( + // (2,1): error CS0656: Missing compiler required member 'System.Text.StringBuilder..ctor' + // record struct C1; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "record struct C1;").WithArguments("System.Text.StringBuilder", ".ctor").WithLocation(2, 1) + ); + } + + [Fact] + public void ToString_TopLevelRecord_MissingStringBuilderAppendString() + { + var src = @" +record struct C1; +"; + + var comp = CreateCompilation(src); + comp.MakeMemberMissing(WellKnownMember.System_Text_StringBuilder__AppendString); + comp.VerifyEmitDiagnostics( + // (2,1): error CS0656: Missing compiler required member 'System.Text.StringBuilder.Append' + // record struct C1; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "record struct C1;").WithArguments("System.Text.StringBuilder", "Append").WithLocation(2, 1) + ); + } + + [Fact] + public void ToString_TopLevelRecord_OneProperty_MissingStringBuilderAppendString() + { + var src = @" +record struct C1(int P); +"; + + var comp = CreateCompilation(src); + comp.MakeMemberMissing(WellKnownMember.System_Text_StringBuilder__AppendString); + comp.VerifyEmitDiagnostics( + // (2,1): error CS0656: Missing compiler required member 'System.Text.StringBuilder.Append' + // record struct C1(int P); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "record struct C1(int P);").WithArguments("System.Text.StringBuilder", "Append").WithLocation(2, 1), + // (2,1): error CS0656: Missing compiler required member 'System.Text.StringBuilder.Append' + // record struct C1(int P); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "record struct C1(int P);").WithArguments("System.Text.StringBuilder", "Append").WithLocation(2, 1) + ); + } + + [Fact] + public void ToString_RecordWithIndexer() + { + var src = @" +var c1 = new C1(42); +System.Console.Write(c1.ToString()); + +record struct C1(int I1) +{ + private int field = 44; + public int this[int i] => 0; + public int PropertyWithoutGetter { set { } } + public int P2 { get => 43; } + public event System.Action a = null; + + private int field1 = 100; + internal int field2 = 100; + + private int Property1 { get; set; } = 100; + internal int Property2 { get; set; } = 100; +} +"; + + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "C1 { I1 = 42, P2 = 43 }"); + comp.VerifyEmitDiagnostics( + // (7,17): warning CS0414: The field 'C1.field' is assigned but its value is never used + // private int field = 44; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "field").WithArguments("C1.field").WithLocation(7, 17), + // (11,32): warning CS0414: The field 'C1.a' is assigned but its value is never used + // public event System.Action a = null; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "a").WithArguments("C1.a").WithLocation(11, 32), + // (13,17): warning CS0414: The field 'C1.field1' is assigned but its value is never used + // private int field1 = 100; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "field1").WithArguments("C1.field1").WithLocation(13, 17) + ); + } + + [Fact] + public void ToString_PrivateGetter() + { + var src = @" +var c1 = new C1(); +System.Console.Write(c1.ToString()); + +record struct C1 +{ + public int P1 { private get => 43; set => throw null; } +} +"; + + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "C1 { P1 = 43 }"); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ToString_TopLevelRecord_OneField_ValueType() + { + var src = @" +var c1 = new C1() { field = 42 }; +System.Console.Write(c1.ToString()); + +record struct C1 +{ + public int field; +} +"; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + var v = CompileAndVerify(comp, expectedOutput: "C1 { field = 42 }"); + + var print = comp.GetMember("C1." + WellKnownMemberNames.PrintMembersMethodName); + Assert.Equal(Accessibility.Private, print.DeclaredAccessibility); + Assert.False(print.IsOverride); + Assert.False(print.IsVirtual); + Assert.False(print.IsAbstract); + Assert.False(print.IsSealed); + Assert.True(print.IsImplicitlyDeclared); + + var toString = comp.GetMember("C1." + WellKnownMemberNames.ObjectToString); + Assert.Equal(Accessibility.Public, toString.DeclaredAccessibility); + Assert.True(toString.IsOverride); + Assert.False(toString.IsVirtual); + Assert.False(toString.IsAbstract); + Assert.False(toString.IsSealed); + Assert.True(toString.IsImplicitlyDeclared); + + v.VerifyIL("C1." + WellKnownMemberNames.PrintMembersMethodName, @" +{ + // Code size 50 (0x32) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: ldstr ""field"" + IL_0006: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_000b: pop + IL_000c: ldarg.1 + IL_000d: ldstr "" = "" + IL_0012: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_0017: pop + IL_0018: ldarg.1 + IL_0019: ldarg.0 + IL_001a: ldflda ""int C1.field"" + IL_001f: constrained. ""int"" + IL_0025: callvirt ""string object.ToString()"" + IL_002a: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_002f: pop + IL_0030: ldc.i4.1 + IL_0031: ret +} +"); + } + + [Fact] + public void ToString_TopLevelRecord_OneField_ConstrainedValueType() + { + var src = @" +var c1 = new C1() { field = 42 }; +System.Console.Write(c1.ToString()); + +record struct C1 where T : struct +{ + public T field; +} +"; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + var v = CompileAndVerify(comp, expectedOutput: "C1 { field = 42 }"); + + v.VerifyIL("C1." + WellKnownMemberNames.PrintMembersMethodName, @" +{ + // Code size 50 (0x32) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: ldstr ""field"" + IL_0006: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_000b: pop + IL_000c: ldarg.1 + IL_000d: ldstr "" = "" + IL_0012: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_0017: pop + IL_0018: ldarg.1 + IL_0019: ldarg.0 + IL_001a: ldflda ""T C1.field"" + IL_001f: constrained. ""T"" + IL_0025: callvirt ""string object.ToString()"" + IL_002a: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_002f: pop + IL_0030: ldc.i4.1 + IL_0031: ret +} +"); + } + + [Fact] + public void ToString_TopLevelRecord_OneField_ReferenceType() + { + var src = @" +var c1 = new C1() { field = ""hello"" }; +System.Console.Write(c1.ToString()); + +record struct C1 +{ + public string field; +} +"; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + var v = CompileAndVerify(comp, expectedOutput: "C1 { field = hello }"); + + v.VerifyIL("C1." + WellKnownMemberNames.PrintMembersMethodName, @" +{ + // Code size 39 (0x27) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: ldstr ""field"" + IL_0006: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_000b: pop + IL_000c: ldarg.1 + IL_000d: ldstr "" = "" + IL_0012: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_0017: pop + IL_0018: ldarg.1 + IL_0019: ldarg.0 + IL_001a: ldfld ""string C1.field"" + IL_001f: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(object)"" + IL_0024: pop + IL_0025: ldc.i4.1 + IL_0026: ret +} +"); + } + + [Fact] + public void ToString_TopLevelRecord_TwoFields_ReferenceType() + { + var src = @" +var c1 = new C1(42) { field1 = ""hi"", field2 = null }; +System.Console.Write(c1.ToString()); + +record struct C1(int I) +{ + public string field1 = null; + public string field2 = null; + + private string field3 = null; + internal string field4 = null; +} +"; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (10,20): warning CS0414: The field 'C1.field3' is assigned but its value is never used + // private string field3 = null; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "field3").WithArguments("C1.field3").WithLocation(10, 20) + ); + var v = CompileAndVerify(comp, expectedOutput: "C1 { I = 42, field1 = hi, field2 = }"); + + v.VerifyIL("C1." + WellKnownMemberNames.PrintMembersMethodName, @" +{ + // Code size 151 (0x97) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.1 + IL_0001: ldstr ""I"" + IL_0006: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_000b: pop + IL_000c: ldarg.1 + IL_000d: ldstr "" = "" + IL_0012: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_0017: pop + IL_0018: ldarg.1 + IL_0019: ldarg.0 + IL_001a: call ""readonly int C1.I.get"" + IL_001f: stloc.0 + IL_0020: ldloca.s V_0 + IL_0022: constrained. ""int"" + IL_0028: callvirt ""string object.ToString()"" + IL_002d: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_0032: pop + IL_0033: ldarg.1 + IL_0034: ldstr "", "" + IL_0039: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_003e: pop + IL_003f: ldarg.1 + IL_0040: ldstr ""field1"" + IL_0045: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_004a: pop + IL_004b: ldarg.1 + IL_004c: ldstr "" = "" + IL_0051: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_0056: pop + IL_0057: ldarg.1 + IL_0058: ldarg.0 + IL_0059: ldfld ""string C1.field1"" + IL_005e: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(object)"" + IL_0063: pop + IL_0064: ldarg.1 + IL_0065: ldstr "", "" + IL_006a: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_006f: pop + IL_0070: ldarg.1 + IL_0071: ldstr ""field2"" + IL_0076: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_007b: pop + IL_007c: ldarg.1 + IL_007d: ldstr "" = "" + IL_0082: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_0087: pop + IL_0088: ldarg.1 + IL_0089: ldarg.0 + IL_008a: ldfld ""string C1.field2"" + IL_008f: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(object)"" + IL_0094: pop + IL_0095: ldc.i4.1 + IL_0096: ret +} +"); + } + + [Fact] + public void ToString_TopLevelRecord_Readonly() + { + var src = @" +var c1 = new C1(42); +System.Console.Write(c1.ToString()); + +readonly record struct C1(int I); +"; + + var comp = CreateCompilation(src); + comp.VerifyDiagnostics(); + var v = CompileAndVerify(comp, expectedOutput: "C1 { I = 42 }", verify: Verification.Skipped /* init-only */); + + v.VerifyIL("C1." + WellKnownMemberNames.PrintMembersMethodName, @" +{ + // Code size 53 (0x35) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.1 + IL_0001: ldstr ""I"" + IL_0006: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_000b: pop + IL_000c: ldarg.1 + IL_000d: ldstr "" = "" + IL_0012: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_0017: pop + IL_0018: ldarg.1 + IL_0019: ldarg.0 + IL_001a: call ""int C1.I.get"" + IL_001f: stloc.0 + IL_0020: ldloca.s V_0 + IL_0022: constrained. ""int"" + IL_0028: callvirt ""string object.ToString()"" + IL_002d: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_0032: pop + IL_0033: ldc.i4.1 + IL_0034: ret +} +"); + v.VerifyIL("C1." + WellKnownMemberNames.ObjectToString, @" +{ + // Code size 70 (0x46) + .maxstack 2 + .locals init (System.Text.StringBuilder V_0) + IL_0000: newobj ""System.Text.StringBuilder..ctor()"" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldstr ""C1"" + IL_000c: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_0011: pop + IL_0012: ldloc.0 + IL_0013: ldstr "" { "" + IL_0018: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_001d: pop + IL_001e: ldarg.0 + IL_001f: ldloc.0 + IL_0020: call ""bool C1.PrintMembers(System.Text.StringBuilder)"" + IL_0025: brfalse.s IL_0033 + IL_0027: ldloc.0 + IL_0028: ldstr "" "" + IL_002d: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_0032: pop + IL_0033: ldloc.0 + IL_0034: ldstr ""}"" + IL_0039: callvirt ""System.Text.StringBuilder System.Text.StringBuilder.Append(string)"" + IL_003e: pop + IL_003f: ldloc.0 + IL_0040: callvirt ""string object.ToString()"" + IL_0045: ret +} +"); + } + + [Fact] + public void ToString_TopLevelRecord_UserDefinedToString() + { + var src = @" +var c1 = new C1(); +System.Console.Write(c1.ToString()); + +record struct C1 +{ + public override string ToString() => ""RAN""; +} +"; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "RAN"); + + var print = comp.GetMember("C1." + WellKnownMemberNames.PrintMembersMethodName); + Assert.Equal("System.Boolean C1." + WellKnownMemberNames.PrintMembersMethodName + "(System.Text.StringBuilder builder)", print.ToTestDisplayString()); + } + + [Fact] + public void ToString_TopLevelRecord_UserDefinedToString_New() + { + var src = @" +record struct C1 +{ + public new string ToString() => throw null; +} +"; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (4,23): error CS8869: 'C1.ToString()' does not override expected method from 'object'. + // public new string ToString() => throw null; + Diagnostic(ErrorCode.ERR_DoesNotOverrideMethodFromObject, "ToString").WithArguments("C1.ToString()").WithLocation(4, 23) + ); + } + + [Fact] + public void ToString_TopLevelRecord_UserDefinedToString_Sealed() + { + var src = @" +record struct C1 +{ + public sealed override string ToString() => throw null; +} +"; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (4,35): error CS0106: The modifier 'sealed' is not valid for this item + // public sealed override string ToString() => throw null; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "ToString").WithArguments("sealed").WithLocation(4, 35) + ); + } + + [Fact] + public void ToString_UserDefinedPrintMembers_WithNullableStringBuilder() + { + var src = @" +#nullable enable +record struct C1 +{ + private bool PrintMembers(System.Text.StringBuilder? builder) => throw null!; +} +"; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ToString_UserDefinedPrintMembers_ErrorReturnType() + { + var src = @" +record struct C1 +{ + private Error PrintMembers(System.Text.StringBuilder builder) => throw null; +} +"; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (4,13): error CS0246: The type or namespace name 'Error' could not be found (are you missing a using directive or an assembly reference?) + // private Error PrintMembers(System.Text.StringBuilder builder) => throw null; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Error").WithArguments("Error").WithLocation(4, 13) + ); + } + + [Fact] + public void ToString_UserDefinedPrintMembers_WrongReturnType() + { + var src = @" +record struct C1 +{ + private int PrintMembers(System.Text.StringBuilder builder) => throw null; +} +"; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (4,17): error CS8874: Record member 'C1.PrintMembers(StringBuilder)' must return 'bool'. + // private int PrintMembers(System.Text.StringBuilder builder) => throw null; + Diagnostic(ErrorCode.ERR_SignatureMismatchInRecord, "PrintMembers").WithArguments("C1.PrintMembers(System.Text.StringBuilder)", "bool").WithLocation(4, 17) + ); + } + + [Fact] + public void ToString_UserDefinedPrintMembers() + { + var src = @" +var c1 = new C1(); +System.Console.Write(c1.ToString()); +System.Console.Write("" - ""); +c1.M(); + +record struct C1 +{ + private bool PrintMembers(System.Text.StringBuilder builder) + { + builder.Append(""RAN""); + return true; + } + + public void M() + { + var builder = new System.Text.StringBuilder(); + if (PrintMembers(builder)) + { + System.Console.Write(builder.ToString()); + } + } +} +"; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "C1 { RAN } - RAN"); + } + + [Fact] + public void ToString_CallingSynthesizedPrintMembers() + { + var src = @" +var c1 = new C1(1, 2, 3); +System.Console.Write(c1.ToString()); +System.Console.Write("" - ""); +c1.M(); + +record struct C1(int I, int I2, int I3) +{ + public void M() + { + var builder = new System.Text.StringBuilder(); + if (PrintMembers(builder)) + { + System.Console.Write(builder.ToString()); + } + } +} +"; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "C1 { I = 1, I2 = 2, I3 = 3 } - I = 1, I2 = 2, I3 = 3"); + } + + [Fact] + public void ToString_UserDefinedPrintMembers_WrongAccessibility() + { + var src = @" +var c = new C1(); +System.Console.Write(c.ToString()); + +record struct C1 +{ + internal bool PrintMembers(System.Text.StringBuilder builder) => throw null; +} +"; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (7,19): error CS8879: Record member 'C1.PrintMembers(StringBuilder)' must be private. + // internal bool PrintMembers(System.Text.StringBuilder builder) => throw null; + Diagnostic(ErrorCode.ERR_NonPrivateAPIInRecord, "PrintMembers").WithArguments("C1.PrintMembers(System.Text.StringBuilder)").WithLocation(7, 19) + ); + } + + [Fact] + public void ToString_UserDefinedPrintMembers_Static() + { + var src = @" +record struct C1 +{ + static private bool PrintMembers(System.Text.StringBuilder builder) => throw null; +} +"; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (4,25): error CS8877: Record member 'C1.PrintMembers(StringBuilder)' may not be static. + // static private bool PrintMembers(System.Text.StringBuilder builder) => throw null; + Diagnostic(ErrorCode.ERR_StaticAPIInRecord, "PrintMembers").WithArguments("C1.PrintMembers(System.Text.StringBuilder)").WithLocation(4, 25) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index 792c9b6459071..da861212e765c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -3668,6 +3668,12 @@ .property instance class [mscorlib]System.Type EqualityContract() { .get instance class [mscorlib]System.Type A::get_EqualityContract() } + + .method family hidebysig newslot virtual instance bool PrintMembers ( class [mscorlib]System.Text.StringBuilder builder ) cil managed + { + IL_0000: ldnull + IL_0001: throw + } } // end of class A "; var source = @" @@ -3878,6 +3884,12 @@ .property instance class [mscorlib]System.Type EqualityContract() { .get instance class [mscorlib]System.Type A::get_EqualityContract() } + + .method family hidebysig newslot virtual instance bool PrintMembers ( class [mscorlib]System.Text.StringBuilder builder ) cil managed + { + IL_0000: ldnull + IL_0001: throw + } } // end of class A "; var source = @" @@ -4097,6 +4109,12 @@ .property instance class [mscorlib]System.Type EqualityContract() { .get instance class [mscorlib]System.Type A::get_EqualityContract() } + + .method family hidebysig newslot virtual instance bool PrintMembers ( class [mscorlib]System.Text.StringBuilder builder ) cil managed + { + IL_0000: ldnull + IL_0001: throw + } } // end of class A "; var source = @" @@ -4612,6 +4630,12 @@ .property instance class [mscorlib]System.Type EqualityContract() { .get instance class [mscorlib]System.Type A::get_EqualityContract() } + + .method family hidebysig newslot virtual instance bool PrintMembers ( class [mscorlib]System.Text.StringBuilder builder ) cil managed + { + IL_0000: ldnull + IL_0001: throw + } } // end of class A "; var source = @" @@ -4709,6 +4733,12 @@ .property instance class [mscorlib]System.Type EqualityContract() { .get instance class [mscorlib]System.Type A::get_EqualityContract() } + + .method family hidebysig newslot virtual instance bool PrintMembers ( class [mscorlib]System.Text.StringBuilder builder ) cil managed + { + IL_0000: ldnull + IL_0001: throw + } } // end of class A "; var source = @" @@ -4925,6 +4955,7 @@ public record C : B { [Fact, WorkItem(47093, "https://github.com/dotnet/roslyn/issues/47093")] public void ToString_TopLevelRecord_Empty() { + // PROTOTYPE(record-structs): ported var src = @" var c1 = new C1(); System.Console.Write(c1.ToString()); @@ -5151,6 +5182,7 @@ .locals init (System.Text.StringBuilder V_0) [Fact] public void ToString_TopLevelRecord_MissingStringBuilder() { + // PROTOTYPE(record-structs): ported var src = @" record C1; "; @@ -5173,6 +5205,7 @@ record C1; [Fact] public void ToString_TopLevelRecord_MissingStringBuilderCtor() { + // PROTOTYPE(record-structs): ported var src = @" record C1; "; @@ -5189,6 +5222,7 @@ record C1; [Fact] public void ToString_TopLevelRecord_MissingStringBuilderAppendString() { + // PROTOTYPE(record-structs): ported var src = @" record C1; "; @@ -5205,6 +5239,7 @@ record C1; [Fact] public void ToString_TopLevelRecord_OneProperty_MissingStringBuilderAppendString() { + // PROTOTYPE(record-structs): ported var src = @" record C1(int P); "; @@ -5271,6 +5306,7 @@ sealed record C2(int I1, int I2) : C1(I1); [Fact, WorkItem(47672, "https://github.com/dotnet/roslyn/issues/47672")] public void ToString_RecordWithIndexer() { + // PROTOTYPE(record-structs): ported var src = @" var c1 = new C1(42); System.Console.Write(c1.ToString()); @@ -5313,6 +5349,7 @@ public int PropertyWithoutGetter { set { } } [Fact, WorkItem(47672, "https://github.com/dotnet/roslyn/issues/47672")] public void ToString_PrivateGetter() { + // PROTOTYPE(record-structs): ported var src = @" var c1 = new C1(); System.Console.Write(c1.ToString()); @@ -5324,7 +5361,7 @@ record C1 "; var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "C1 { P1 = 43 }", verify: Verification.Skipped /* init-only */); + CompileAndVerify(comp, expectedOutput: "C1 { P1 = 43 }"); comp.VerifyEmitDiagnostics(); } @@ -5396,6 +5433,9 @@ record C2: Error; // (2,8): error CS0115: 'C2.GetHashCode()': no suitable method found to override // record C2: Error; Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C2").WithArguments("C2.GetHashCode()").WithLocation(2, 8), + // (2,8): error CS0115: 'C2.PrintMembers(StringBuilder)': no suitable method found to override + // record C2: Error; + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C2").WithArguments("C2.PrintMembers(System.Text.StringBuilder)").WithLocation(2, 8), // (2,12): error CS0246: The type or namespace name 'Error' could not be found (are you missing a using directive or an assembly reference?) // record C2: Error; Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Error").WithArguments("Error").WithLocation(2, 12) @@ -5425,7 +5465,10 @@ record R : R; Diagnostic(ErrorCode.ERR_OverrideNotExpected, "R").WithArguments("R.Equals(object?)").WithLocation(2, 8), // (2,8): error CS0115: 'R.GetHashCode()': no suitable method found to override // record R : R; - Diagnostic(ErrorCode.ERR_OverrideNotExpected, "R").WithArguments("R.GetHashCode()").WithLocation(2, 8) + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "R").WithArguments("R.GetHashCode()").WithLocation(2, 8), + // (2,8): error CS0115: 'R.PrintMembers(StringBuilder)': no suitable method found to override + // record R : R; + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "R").WithArguments("R.PrintMembers(System.Text.StringBuilder)").WithLocation(2, 8) ); } @@ -5475,7 +5518,7 @@ record C1 var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.Regular9, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics(); - var v = CompileAndVerify(comp, expectedOutput: "C1 { field = 42 }", verify: Verification.Skipped /* init-only */); + var v = CompileAndVerify(comp, expectedOutput: "C1 { field = 42 }"); var print = comp.GetMember("C1." + WellKnownMemberNames.PrintMembersMethodName); Assert.Equal(Accessibility.Protected, print.DeclaredAccessibility); @@ -5521,6 +5564,7 @@ .maxstack 2 [Fact, WorkItem(47092, "https://github.com/dotnet/roslyn/issues/47092")] public void ToString_TopLevelRecord_OneField_ConstrainedValueType() { + // PROTOTYPE(record-structs): ported var src = @" var c1 = new C1() { field = 42 }; System.Console.Write(c1.ToString()); @@ -5533,7 +5577,7 @@ record C1 where T : struct var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.Regular9, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics(); - var v = CompileAndVerify(comp, expectedOutput: "C1 { field = 42 }", verify: Verification.Skipped /* init-only */); + var v = CompileAndVerify(comp, expectedOutput: "C1 { field = 42 }"); v.VerifyIL("C1." + WellKnownMemberNames.PrintMembersMethodName, @" { @@ -5563,6 +5607,7 @@ .maxstack 2 [Fact, WorkItem(47092, "https://github.com/dotnet/roslyn/issues/47092")] public void ToString_TopLevelRecord_OneField_ReferenceType() { + // PROTOTYPE(record-structs): ported var src = @" var c1 = new C1() { field = ""hello"" }; System.Console.Write(c1.ToString()); @@ -5575,7 +5620,7 @@ record C1 var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.Regular9, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics(); - var v = CompileAndVerify(comp, expectedOutput: "C1 { field = hello }", verify: Verification.Skipped /* init-only */); + var v = CompileAndVerify(comp, expectedOutput: "C1 { field = hello }"); v.VerifyIL("C1." + WellKnownMemberNames.PrintMembersMethodName, @" { @@ -5669,6 +5714,7 @@ record C1 [Fact] public void ToString_TopLevelRecord_TwoFields_ReferenceType() { + // PROTOTYPE(record-structs): ported var src = @" var c1 = new C1() { field1 = ""hi"", field2 = null }; System.Console.Write(c1.ToString()); @@ -5704,7 +5750,7 @@ record C1 // private protected string field7; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "field7").WithArguments("C1.field7", "null").WithLocation(14, 30) ); - var v = CompileAndVerify(comp, expectedOutput: "C1 { field1 = hi, field2 = }", verify: Verification.Skipped /* init-only */); + var v = CompileAndVerify(comp, expectedOutput: "C1 { field1 = hi, field2 = }"); v.VerifyIL("C1." + WellKnownMemberNames.PrintMembersMethodName, @" { @@ -6244,12 +6290,9 @@ public record B : A { }"; var comp = CreateCompilationWithIL(new[] { source, IsExternalInitTypeDefinition }, ilSource: ilSource, parseOptions: TestOptions.Regular9); comp.VerifyEmitDiagnostics( - // (2,15): warning CS0108: 'B.PrintMembers(StringBuilder)' hides inherited member 'A.PrintMembers(StringBuilder)'. Use the new keyword if hiding was intended. - // public record B : A { - Diagnostic(ErrorCode.WRN_NewRequired, "B").WithArguments("B.PrintMembers(System.Text.StringBuilder)", "A.PrintMembers(System.Text.StringBuilder)").WithLocation(2, 15), - // (2,19): error CS8864: Records may only inherit from object or another record + // (2,15): error CS0506: 'B.PrintMembers(StringBuilder)': cannot override inherited member 'A.PrintMembers(StringBuilder)' because it is not marked virtual, abstract, or override // public record B : A { - Diagnostic(ErrorCode.ERR_BadRecordBase, "A").WithLocation(2, 19) + Diagnostic(ErrorCode.ERR_CantOverrideNonVirtual, "B").WithArguments("B.PrintMembers(System.Text.StringBuilder)", "A.PrintMembers(System.Text.StringBuilder)").WithLocation(2, 15) ); } @@ -6319,9 +6362,9 @@ public record B : A { }"; var comp = CreateCompilationWithIL(new[] { source, IsExternalInitTypeDefinition }, ilSource: ilSource, parseOptions: TestOptions.Regular9); comp.VerifyEmitDiagnostics( - // (2,19): error CS8864: Records may only inherit from object or another record + // (2,15): error CS0115: 'B.PrintMembers(StringBuilder)': no suitable method found to override // public record B : A { - Diagnostic(ErrorCode.ERR_BadRecordBase, "A").WithLocation(2, 19) + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "B").WithArguments("B.PrintMembers(System.Text.StringBuilder)").WithLocation(2, 15) ); } @@ -6391,12 +6434,81 @@ public record B : A { }"; var comp = CreateCompilationWithIL(new[] { source, IsExternalInitTypeDefinition }, ilSource: ilSource, parseOptions: TestOptions.Regular9); comp.VerifyEmitDiagnostics( - // (2,15): warning CS0114: 'B.PrintMembers(StringBuilder)' hides inherited member 'A.PrintMembers(StringBuilder)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. + // (2,15): error CS0508: 'B.PrintMembers(StringBuilder)': return type must be 'int' to match overridden member 'A.PrintMembers(StringBuilder)' // public record B : A { - Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "B").WithArguments("B.PrintMembers(System.Text.StringBuilder)", "A.PrintMembers(System.Text.StringBuilder)").WithLocation(2, 15), - // (2,19): error CS8864: Records may only inherit from object or another record + Diagnostic(ErrorCode.ERR_CantChangeReturnTypeOnOverride, "B").WithArguments("B.PrintMembers(System.Text.StringBuilder)", "A.PrintMembers(System.Text.StringBuilder)", "int").WithLocation(2, 15) + ); + } + + [Fact] + public void EqualityContract_BadBase_ReturnsInt() + { + var ilSource = @" +.class public auto ansi beforefieldinit A + extends System.Object +{ + .method public hidebysig specialname newslot virtual instance class A '" + WellKnownMemberNames.CloneMethodName + @"' () cil managed + { + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual instance bool Equals ( object other ) cil managed + { + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual instance int32 GetHashCode () cil managed + { + IL_0000: ldnull + IL_0001: throw + } + + .method public newslot virtual instance bool Equals ( class A '' ) cil managed + { + IL_0000: ldnull + IL_0001: throw + } + + .method family hidebysig specialname rtspecialname instance void .ctor ( class A '' ) cil managed + { + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig specialname rtspecialname instance void .ctor () cil managed + { + IL_0000: ldnull + IL_0001: throw + } + + .method family hidebysig newslot virtual instance int32 get_EqualityContract () cil managed + { + IL_0000: ldnull + IL_0001: throw + } + + .property instance int32 EqualityContract() + { + .get instance int32 A::get_EqualityContract() + } + + .method family hidebysig newslot virtual instance bool '" + WellKnownMemberNames.PrintMembersMethodName + @"' (class [mscorlib]System.Text.StringBuilder builder) cil managed + { + IL_0000: ldnull + IL_0001: throw + } +} +"; + var source = @" +public record B : A { +}"; + var comp = CreateCompilationWithIL(new[] { source, IsExternalInitTypeDefinition }, ilSource: ilSource, parseOptions: TestOptions.Regular9); + comp.VerifyEmitDiagnostics( + // (2,15): error CS1715: 'B.EqualityContract': type must be 'int' to match overridden member 'A.EqualityContract' // public record B : A { - Diagnostic(ErrorCode.ERR_BadRecordBase, "A").WithLocation(2, 19) + Diagnostic(ErrorCode.ERR_CantChangeTypeOnOverride, "B").WithArguments("B.EqualityContract", "A.EqualityContract", "int").WithLocation(2, 15) ); } @@ -6471,14 +6583,7 @@ .property instance class [mscorlib]System.Type EqualityContract() public record B : A { }"; var comp = CreateCompilationWithIL(new[] { source, IsExternalInitTypeDefinition }, ilSource: ilSource, parseOptions: TestOptions.Regular9); - comp.VerifyEmitDiagnostics( - // (2,15): warning CS0114: 'B.PrintMembers(StringBuilder)' hides inherited member 'A.PrintMembers(StringBuilder)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. - // public record B : A { - Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "B").WithArguments("B.PrintMembers(System.Text.StringBuilder)", "A.PrintMembers(System.Text.StringBuilder)").WithLocation(2, 15), - // (2,19): error CS8864: Records may only inherit from object or another record - // public record B : A { - Diagnostic(ErrorCode.ERR_BadRecordBase, "A").WithLocation(2, 19) - ); + comp.VerifyEmitDiagnostics(); } [Fact] @@ -6541,9 +6646,9 @@ public record B : A { }"; var comp = CreateCompilationWithIL(new[] { source, IsExternalInitTypeDefinition }, ilSource: ilSource, parseOptions: TestOptions.Regular9); comp.VerifyEmitDiagnostics( - // (2,19): error CS8864: Records may only inherit from object or another record + // (2,15): error CS0115: 'B.PrintMembers(StringBuilder)': no suitable method found to override // public record B : A { - Diagnostic(ErrorCode.ERR_BadRecordBase, "A").WithLocation(2, 19) + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "B").WithArguments("B.PrintMembers(System.Text.StringBuilder)").WithLocation(2, 15) ); } @@ -6624,14 +6729,7 @@ .method public hidebysig virtual instance string ToString () cil managed public record B : A { }"; var comp = CreateCompilationWithIL(new[] { source, IsExternalInitTypeDefinition }, ilSource: ilSource, parseOptions: TestOptions.Regular9); - comp.VerifyEmitDiagnostics( - // (2,15): warning CS0114: 'B.PrintMembers(StringBuilder)' hides inherited member 'A.PrintMembers(StringBuilder)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. - // public record B : A { - Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "B").WithArguments("B.PrintMembers(System.Text.StringBuilder)", "A.PrintMembers(System.Text.StringBuilder)").WithLocation(2, 15), - // (2,19): error CS8864: Records may only inherit from object or another record - // public record B : A { - Diagnostic(ErrorCode.ERR_BadRecordBase, "A").WithLocation(2, 19) - ); + comp.VerifyEmitDiagnostics(); } [Fact] @@ -6781,13 +6879,20 @@ public record C : B "; var comp = CreateCompilationWithIL(new[] { source, IsExternalInitTypeDefinition }, ilSource: ilSource, parseOptions: TestOptions.Regular9); comp.VerifyEmitDiagnostics( - // (2,19): error CS8864: Records may only inherit from object or another record - // public record C : B - Diagnostic(ErrorCode.ERR_BadRecordBase, "B").WithLocation(2, 19), // (4,29): error CS8871: 'C.PrintMembers(StringBuilder)' does not override expected method from 'B'. // protected override bool PrintMembers(System.Text.StringBuilder builder) => throw null; Diagnostic(ErrorCode.ERR_DoesNotOverrideBaseMethod, "PrintMembers").WithArguments("C.PrintMembers(System.Text.StringBuilder)", "B").WithLocation(4, 29) ); + + var source2 = @" +public record C : B; +"; + comp = CreateCompilationWithIL(new[] { source2, IsExternalInitTypeDefinition }, ilSource: ilSource, parseOptions: TestOptions.Regular9); + comp.VerifyEmitDiagnostics( + // (2,15): error CS8871: 'C.PrintMembers(StringBuilder)' does not override expected method from 'B'. + // public record C : B; + Diagnostic(ErrorCode.ERR_DoesNotOverrideBaseMethod, "C").WithArguments("C.PrintMembers(System.Text.StringBuilder)", "B").WithLocation(2, 15) + ); } [Fact] @@ -7027,6 +7132,7 @@ public record B : A { [Fact] public void ToString_TopLevelRecord_UserDefinedToString() { + // PROTOTYPE(record-structs): ported var src = @" var c1 = new C1(); System.Console.Write(c1.ToString()); @@ -7048,6 +7154,7 @@ record C1 [Fact] public void ToString_TopLevelRecord_UserDefinedToString_Sealed() { + // PROTOTYPE(record-structs): ported var src = @" record C1 { @@ -7080,6 +7187,7 @@ sealed record C1 [Fact] public void ToString_UserDefinedPrintMembers_WithNullableStringBuilder() { + // PROTOTYPE(record-structs): ported var src = @" #nullable enable record C1 @@ -7095,6 +7203,7 @@ record C1 [Fact] public void ToString_UserDefinedPrintMembers_ErrorReturnType() { + // PROTOTYPE(record-structs): ported var src = @" record C1 { @@ -7113,6 +7222,7 @@ record C1 [Fact] public void ToString_UserDefinedPrintMembers_WrongReturnType() { + // PROTOTYPE(record-structs): ported var src = @" record C1 { @@ -7201,6 +7311,7 @@ sealed record C [Fact] public void ToString_UserDefinedPrintMembers() { + // PROTOTYPE(record-structs): ported var src = @" var c1 = new C1(); System.Console.Write(c1.ToString()); @@ -7223,6 +7334,7 @@ protected virtual bool PrintMembers(System.Text.StringBuilder builder) [Fact] public void ToString_UserDefinedPrintMembers_WrongAccessibility() { + // PROTOTYPE(record-structs): ported var src = @" record C1 { @@ -7290,6 +7402,7 @@ record C1 : B [Fact] public void ToString_UserDefinedPrintMembers_New() { + // PROTOTYPE(record-structs): ported var src = @" record B; record C1 : B @@ -10656,6 +10769,9 @@ End Class // (1,8): error CS0115: 'B.Equals(A?)': no suitable method found to override // record B(object P, object Q) : A Diagnostic(ErrorCode.ERR_OverrideNotExpected, "B").WithArguments("B.Equals(A?)").WithLocation(1, 8), + // (1,8): error CS0115: 'B.PrintMembers(StringBuilder)': no suitable method found to override + // record B(object P, object Q) : A + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "B").WithArguments("B.PrintMembers(System.Text.StringBuilder)").WithLocation(1, 8), // (1,8): error CS8867: No accessible copy constructor found in base type 'A'. // record B(object P, object Q) : A Diagnostic(ErrorCode.ERR_NoCopyConstructorInBaseType, "B").WithArguments("A").WithLocation(1, 8), @@ -10724,6 +10840,9 @@ End Class // (1,8): error CS0115: 'B.Equals(A?)': no suitable method found to override // record B(object P, object Q) : A Diagnostic(ErrorCode.ERR_OverrideNotExpected, "B").WithArguments("B.Equals(A?)").WithLocation(1, 8), + // (1,8): error CS0115: 'B.PrintMembers(StringBuilder)': no suitable method found to override + // record B(object P, object Q) : A + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "B").WithArguments("B.PrintMembers(System.Text.StringBuilder)").WithLocation(1, 8), // (1,8): error CS8867: No accessible copy constructor found in base type 'A'. // record B(object P, object Q) : A Diagnostic(ErrorCode.ERR_NoCopyConstructorInBaseType, "B").WithArguments("A").WithLocation(1, 8), @@ -10736,7 +10855,7 @@ End Class // (1,32): error CS8864: Records may only inherit from object or another record // record B(object P, object Q) : A Diagnostic(ErrorCode.ERR_BadRecordBase, "A").WithLocation(1, 32) - ); + ); var actualMembers = GetProperties(compB, "B").ToTestDisplayStrings(); AssertEx.Equal(new[] { "System.Type B.EqualityContract { get; }" }, actualMembers); @@ -10811,6 +10930,9 @@ End Class // (1,8): error CS0115: 'C.Equals(B?)': no suitable method found to override // record C(object P, object Q, object R) : B Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C").WithArguments("C.Equals(B?)").WithLocation(1, 8), + // (1,8): error CS0115: 'C.PrintMembers(StringBuilder)': no suitable method found to override + // record C(object P, object Q, object R) : B + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C").WithArguments("C.PrintMembers(System.Text.StringBuilder)").WithLocation(1, 8), // (1,8): error CS7036: There is no argument given that corresponds to the required formal parameter 'b' of 'B.B(B)' // record C(object P, object Q, object R) : B Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "C").WithArguments("b", "B.B(B)").WithLocation(1, 8), @@ -10823,7 +10945,7 @@ End Class // (1,42): error CS8864: Records may only inherit from object or another record // record C(object P, object Q, object R) : B Diagnostic(ErrorCode.ERR_BadRecordBase, "B").WithLocation(1, 42) - ); + ); var actualMembers = GetProperties(compB, "C").ToTestDisplayStrings(); var expectedMembers = new[] @@ -13041,6 +13163,9 @@ protected C(C c) : base(c) { } // (2,8): error CS0115: 'C.Equals(object?)': no suitable method found to override // record C(int j) : B(3, 4); Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C").WithArguments("C.Equals(object?)").WithLocation(2, 8), + // (2,8): error CS0115: 'C.PrintMembers(StringBuilder)': no suitable method found to override + // record C(int j) : B(3, 4); + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C").WithArguments("C.PrintMembers(System.Text.StringBuilder)").WithLocation(2, 8), // (2,19): error CS0122: 'B' is inaccessible due to its protection level // record C(int j) : B(3, 4); Diagnostic(ErrorCode.ERR_BadAccess, "B").WithArguments("B").WithLocation(2, 19), @@ -23894,15 +24019,18 @@ record B : A, System.IEquatable; // (1,8): error CS0115: 'A.ToString()': no suitable method found to override // record A : System.IEquatable>; Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.ToString()").WithLocation(1, 8), - // (1,8): error CS0115: 'A.GetHashCode()': no suitable method found to override - // record A : System.IEquatable>; - Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.GetHashCode()").WithLocation(1, 8), // (1,8): error CS0115: 'A.EqualityContract': no suitable method found to override // record A : System.IEquatable>; Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.EqualityContract").WithLocation(1, 8), // (1,8): error CS0115: 'A.Equals(object?)': no suitable method found to override // record A : System.IEquatable>; Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.Equals(object?)").WithLocation(1, 8), + // (1,8): error CS0115: 'A.GetHashCode()': no suitable method found to override + // record A : System.IEquatable>; + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.GetHashCode()").WithLocation(1, 8), + // (1,8): error CS0115: 'A.PrintMembers(StringBuilder)': no suitable method found to override + // record A : System.IEquatable>; + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.PrintMembers(System.Text.StringBuilder)").WithLocation(1, 8), // (1,22): error CS0234: The type or namespace name 'IEquatable<>' does not exist in the namespace 'System' (are you missing an assembly reference?) // record A : System.IEquatable>; Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IEquatable>").WithArguments("IEquatable<>", "System").WithLocation(1, 22), @@ -23980,15 +24108,18 @@ record B : A, IEquatable; // (2,8): error CS0115: 'A.ToString()': no suitable method found to override // record A : IEquatable>; Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.ToString()").WithLocation(2, 8), - // (2,8): error CS0115: 'A.GetHashCode()': no suitable method found to override - // record A : IEquatable>; - Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.GetHashCode()").WithLocation(2, 8), // (2,8): error CS0115: 'A.EqualityContract': no suitable method found to override // record A : IEquatable>; Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.EqualityContract").WithLocation(2, 8), // (2,8): error CS0115: 'A.Equals(object?)': no suitable method found to override // record A : IEquatable>; Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.Equals(object?)").WithLocation(2, 8), + // (2,8): error CS0115: 'A.GetHashCode()': no suitable method found to override + // record A : IEquatable>; + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.GetHashCode()").WithLocation(2, 8), + // (2,8): error CS0115: 'A.PrintMembers(StringBuilder)': no suitable method found to override + // record A : IEquatable>; + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.PrintMembers(System.Text.StringBuilder)").WithLocation(2, 8), // (2,15): error CS0246: The type or namespace name 'IEquatable<>' could not be found (are you missing a using directive or an assembly reference?) // record A : IEquatable>; Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "IEquatable>").WithArguments("IEquatable<>").WithLocation(2, 15), @@ -25311,15 +25442,18 @@ record B : A> { // (2,8): error CS0115: 'A.ToString()': no suitable method found to override // record A : B> { } Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.ToString()").WithLocation(2, 8), - // (2,8): error CS0115: 'A.GetHashCode()': no suitable method found to override - // record A : B> { } - Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.GetHashCode()").WithLocation(2, 8), // (2,8): error CS0115: 'A.EqualityContract': no suitable method found to override // record A : B> { } Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.EqualityContract").WithLocation(2, 8), // (2,8): error CS0115: 'A.Equals(object?)': no suitable method found to override // record A : B> { } Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.Equals(object?)").WithLocation(2, 8), + // (2,8): error CS0115: 'A.GetHashCode()': no suitable method found to override + // record A : B> { } + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.GetHashCode()").WithLocation(2, 8), + // (2,8): error CS0115: 'A.PrintMembers(StringBuilder)': no suitable method found to override + // record A : B> { } + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "A").WithArguments("A.PrintMembers(System.Text.StringBuilder)").WithLocation(2, 8), // (3,8): error CS0115: 'B.EqualityContract': no suitable method found to override // record B : A> { Diagnostic(ErrorCode.ERR_OverrideNotExpected, "B").WithArguments("B.EqualityContract").WithLocation(3, 8), @@ -25329,6 +25463,9 @@ record B : A> { // (3,8): error CS0115: 'B.GetHashCode()': no suitable method found to override // record B : A> { Diagnostic(ErrorCode.ERR_OverrideNotExpected, "B").WithArguments("B.GetHashCode()").WithLocation(3, 8), + // (3,8): error CS0115: 'B.PrintMembers(StringBuilder)': no suitable method found to override + // record B : A> { + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "B").WithArguments("B.PrintMembers(System.Text.StringBuilder)").WithLocation(3, 8), // (3,8): error CS0115: 'B.ToString()': no suitable method found to override // record B : A> { Diagnostic(ErrorCode.ERR_OverrideNotExpected, "B").WithArguments("B.ToString()").WithLocation(3, 8) @@ -25381,9 +25518,6 @@ public partial record C3 : Base<(int a, int b)> { } // (3,23): error CS0263: Partial declarations of 'C1' must not specify different base classes // public partial record C1 : Base<(int a, int b)> { } Diagnostic(ErrorCode.ERR_PartialMultipleBases, "C1").WithArguments("C1").WithLocation(3, 23), - // (5,23): error CS0115: 'C2.GetHashCode()': no suitable method found to override - // public partial record C2 : Base<(int a, int b)> { } - Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C2").WithArguments("C2.GetHashCode()").WithLocation(5, 23), // (5,23): error CS0115: 'C2.ToString()': no suitable method found to override // public partial record C2 : Base<(int a, int b)> { } Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C2").WithArguments("C2.ToString()").WithLocation(5, 23), @@ -25393,18 +25527,27 @@ public partial record C3 : Base<(int a, int b)> { } // (5,23): error CS0115: 'C2.Equals(object?)': no suitable method found to override // public partial record C2 : Base<(int a, int b)> { } Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C2").WithArguments("C2.Equals(object?)").WithLocation(5, 23), + // (5,23): error CS0115: 'C2.GetHashCode()': no suitable method found to override + // public partial record C2 : Base<(int a, int b)> { } + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C2").WithArguments("C2.GetHashCode()").WithLocation(5, 23), + // (5,23): error CS0115: 'C2.PrintMembers(StringBuilder)': no suitable method found to override + // public partial record C2 : Base<(int a, int b)> { } + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C2").WithArguments("C2.PrintMembers(System.Text.StringBuilder)").WithLocation(5, 23), // (3,23): error CS0115: 'C1.ToString()': no suitable method found to override // public partial record C1 : Base<(int a, int b)> { } Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C1").WithArguments("C1.ToString()").WithLocation(3, 23), - // (3,23): error CS0115: 'C1.GetHashCode()': no suitable method found to override - // public partial record C1 : Base<(int a, int b)> { } - Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C1").WithArguments("C1.GetHashCode()").WithLocation(3, 23), // (3,23): error CS0115: 'C1.EqualityContract': no suitable method found to override // public partial record C1 : Base<(int a, int b)> { } Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C1").WithArguments("C1.EqualityContract").WithLocation(3, 23), // (3,23): error CS0115: 'C1.Equals(object?)': no suitable method found to override // public partial record C1 : Base<(int a, int b)> { } - Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C1").WithArguments("C1.Equals(object?)").WithLocation(3, 23) + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C1").WithArguments("C1.Equals(object?)").WithLocation(3, 23), + // (3,23): error CS0115: 'C1.GetHashCode()': no suitable method found to override + // public partial record C1 : Base<(int a, int b)> { } + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C1").WithArguments("C1.GetHashCode()").WithLocation(3, 23), + // (3,23): error CS0115: 'C1.PrintMembers(StringBuilder)': no suitable method found to override + // public partial record C1 : Base<(int a, int b)> { } + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "C1").WithArguments("C1.PrintMembers(System.Text.StringBuilder)").WithLocation(3, 23) ); } @@ -27529,6 +27672,7 @@ public static int P1 { set { } } [WorkItem(47867, "https://github.com/dotnet/roslyn/issues/47867")] public void ToString_NestedRecord() { + // PROTOTYPE(record-structs): ported var src = @" var c1 = new Outer.C1(42); System.Console.Write(c1.ToString()); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs index adfee51b82806..bbb3dd156217b 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs @@ -1081,7 +1081,7 @@ record C "System.String! C.Y { get; init; }", "System.String! C.Y.get", "void C.Y.init", - "System.String C.ToString()", + "System.String! C.ToString()", "System.Boolean C." + WellKnownMemberNames.PrintMembersMethodName + "(System.Text.StringBuilder! builder)", "System.Boolean C.operator !=(C? r1, C? r2)", "System.Boolean C.operator ==(C? r1, C? r2)", @@ -1315,6 +1315,9 @@ enum G : C { }"; // (3,8): error CS0115: 'B.Equals(A?)': no suitable method found to override // record B : A { } Diagnostic(ErrorCode.ERR_OverrideNotExpected, "B").WithArguments("B.Equals(A?)").WithLocation(3, 8), + // (3,8): error CS0115: 'B.PrintMembers(StringBuilder)': no suitable method found to override + // record B : A { } + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "B").WithArguments("B.PrintMembers(System.Text.StringBuilder)").WithLocation(3, 8), // (3,8): error CS8867: No accessible copy constructor found in base type 'A'. // record B : A { } Diagnostic(ErrorCode.ERR_NoCopyConstructorInBaseType, "B").WithArguments("A").WithLocation(3, 8), @@ -1368,6 +1371,9 @@ enum H : C { } // (3,8): error CS0115: 'E.Equals(A?)': no suitable method found to override // record E : A { } Diagnostic(ErrorCode.ERR_OverrideNotExpected, "E").WithArguments("E.Equals(A?)").WithLocation(3, 8), + // (3,8): error CS0115: 'E.PrintMembers(StringBuilder)': no suitable method found to override + // record E : A { } + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "E").WithArguments("E.PrintMembers(System.Text.StringBuilder)").WithLocation(3, 8), // (3,8): error CS8867: No accessible copy constructor found in base type 'A'. // record E : A { } Diagnostic(ErrorCode.ERR_NoCopyConstructorInBaseType, "E").WithArguments("A").WithLocation(3, 8),