From 4cce01e5ae924c3454106eeb57948247863d16ed Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Mon, 30 Dec 2024 14:04:11 -0800 Subject: [PATCH] Forbid unsafe types in records [The specification](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-9.0/records.md#members-of-a-record-type) for records forbids the use of unsafe types as an instance field of the type. However, we didn't check that in all cases; we only checked when generating the `Equals` method for the record type. This meant that if the user provided their own definition of `Equals`, they could bypass this restriction. We also did not check for nested unsafe types, so `int*[]` was permitted in any scenario. We have 2 options for fixing this: 1. Treat it as a specification bug, and update the spec to follow the current behavior. We'd therefore fix https://github.com/dotnet/roslyn/issues/66312 by moving the diagnostic to SourceNamedTypeSymbol.AfterMembersChecks, and only error when the user didn't provide their own implementation of `Equals`. 2. Treat it as a compiler bug, and forbid use of unsafe types in all scenarios. I've opted for path 2 here, but we can discuss whether this is too aggressive and if we should opt for path 1 instead. Fixes https://github.com/dotnet/roslyn/issues/66312. --- .../Compiler Breaking Changes - DotNet 10.md | 27 +++ .../Portable/Binder/Binder_Conversions.cs | 2 +- .../Portable/Binder/Binder_Expressions.cs | 4 +- .../CSharp/Portable/Binder/Binder_Symbols.cs | 2 +- .../Portable/BoundTree/UnboundLambda.cs | 2 +- .../Symbols/MemberSymbolExtensions.cs | 2 +- .../Symbols/Source/SourceMemberFieldSymbol.cs | 16 +- .../Source/SourcePropertySymbolBase.cs | 13 +- .../Records/SynthesizedRecordEquals.cs | 7 +- .../Portable/Symbols/TypeSymbolExtensions.cs | 2 +- .../Test/Emit3/Semantics/RecordTests.cs | 162 ++++++++++++--- .../Semantic/Semantics/RecordStructTests.cs | 195 +++++++++++++----- .../Test/Semantic/Semantics/UnsafeTests.cs | 12 +- 13 files changed, 343 insertions(+), 103 deletions(-) diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md index 657d95963527c..d149937e5b7a5 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md @@ -156,3 +156,30 @@ struct S public static void M([UnscopedRef] ref int x) { } } ``` + +## `record` and `record struct` types cannot define pointer type members, even when providing their own Equals implementations + +PROTOTYPE: Figure what version this is going into + +***Introduced in Visual Studio 2022 version 17.14*** + +The specification for `record class` and `record struct` types indicated that any unsafe types are disallowed. However, this was not +enforced correctly in 2 scenarios: + +1. When the `record class` or `record struct` type defined its own `Equals` implementation. +2. When the field type only used the unsafe type in a nested context, such as `int*[]`. + +The compiler now correctly forbids both scenarios. + +```cs +unsafe record struct R1( + int* P // Previously fine, now CS8908 +) +{ + public bool Equals(R other) => true; +} + +unsafe record struct R2( + int*[] P // Previously fine, now CS8908 +); +``` diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 7df03440b692e..bcb7c815a2a07 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -3022,7 +3022,7 @@ private bool MethodGroupConversionHasErrors( return true; } - if ((selectedMethod.HasParameterContainingPointerType() || selectedMethod.ReturnType.ContainsPointer()) + if ((selectedMethod.HasParameterContainingPointerType() || selectedMethod.ReturnType.ContainsPointerOrFunctionPointer()) && ReportUnsafeIfNotAllowed(syntax, diagnostics)) { return true; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index d4f2951aa8e2b..8fcc905011e8e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -561,7 +561,7 @@ private void CheckContextForPointerTypes(ExpressionSyntax node, BindingDiagnosti if (!expr.HasAnyErrors && !IsInsideNameof) { TypeSymbol exprType = expr.Type; - if ((object)exprType != null && exprType.ContainsPointer()) + if ((object)exprType != null && exprType.ContainsPointerOrFunctionPointer()) { ReportUnsafeIfNotAllowed(node, diagnostics); //CONSIDER: Return a bad expression so that HasErrors is true? @@ -3666,7 +3666,7 @@ void createParamsCollection( void reportUnsafeIfNeeded(MemberResolutionResult methodResult, BindingDiagnosticBag diagnostics, BoundExpression argument, TypeWithAnnotations parameterTypeWithAnnotations) { // NOTE: for some reason, dev10 doesn't report this for indexer accesses. - if (!methodResult.Member.IsIndexer() && !argument.HasAnyErrors && parameterTypeWithAnnotations.Type.ContainsPointer()) + if (!methodResult.Member.IsIndexer() && !argument.HasAnyErrors && parameterTypeWithAnnotations.Type.ContainsPointerOrFunctionPointer()) { // CONSIDER: dev10 uses the call syntax, but this seems clearer. ReportUnsafeIfNotAllowed(argument.Syntax, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index b225907821eb7..0961c7d0493c1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -915,7 +915,7 @@ protected NamespaceOrTypeOrAliasSymbolWithAnnotations BindNonGenericSimpleNamesp ReportUseSiteDiagnosticForDynamic(diagnostics, node); } - if (type.ContainsPointer()) + if (type.ContainsPointerOrFunctionPointer()) { ReportUnsafeIfNotAllowed(node, diagnostics); } diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index f0666b06cb720..d491d233c8d9a 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -919,7 +919,7 @@ private void ValidateUnsafeParameters(BindingDiagnosticBag diagnostics, Immutabl var numParametersToCheck = Math.Min(targetParameterTypes.Length, ParameterCount); for (int i = 0; i < numParametersToCheck; i++) { - if (targetParameterTypes[i].Type.ContainsPointer()) + if (targetParameterTypes[i].Type.ContainsPointerOrFunctionPointer()) { this.Binder.ReportUnsafeIfNotAllowed(this.ParameterLocation(i), diagnostics); } diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs index b55fd58855437..638734bf2cdf4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs @@ -115,7 +115,7 @@ internal static bool HasParameterContainingPointerType(this Symbol member) { foreach (var parameterType in member.GetParameterTypes()) { - if (parameterType.Type.ContainsPointer()) + if (parameterType.Type.ContainsPointerOrFunctionPointer()) { return true; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index c5366989a30bc..2e625f870522c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -57,15 +57,20 @@ protected void TypeChecks(TypeSymbol type, BindingDiagnosticBag diagnostics) } else if (type.IsVoidType()) { - diagnostics.Add(ErrorCode.ERR_FieldCantHaveVoidType, TypeSyntax?.Location ?? this.GetFirstLocation()); + diagnostics.Add(ErrorCode.ERR_FieldCantHaveVoidType, getTypeErrorLocation()); } else if (type.IsRestrictedType(ignoreSpanLikeTypes: true)) { - diagnostics.Add(ErrorCode.ERR_FieldCantBeRefAny, TypeSyntax?.Location ?? this.GetFirstLocation(), type); + diagnostics.Add(ErrorCode.ERR_FieldCantBeRefAny, getTypeErrorLocation(), type); } else if (type.IsRefLikeOrAllowsRefLikeType() && (this.IsStatic || !containingType.IsRefLikeType)) { - diagnostics.Add(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, TypeSyntax?.Location ?? this.GetFirstLocation(), type); + diagnostics.Add(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, getTypeErrorLocation(), type); + } + else if (!this.IsStatic && type.ContainsPointerOrFunctionPointer() && (ContainingType.IsRecord || ContainingType.IsRecordStruct)) + { + // The type '{0}' may not be used for a field of a record. + diagnostics.Add(ErrorCode.ERR_BadFieldTypeInRecord, getTypeErrorLocation(), type); } else if (IsConst && !type.CanBeConst()) { @@ -96,6 +101,11 @@ protected void TypeChecks(TypeSymbol type, BindingDiagnosticBag diagnostics) } diagnostics.Add(this.ErrorLocation, useSiteInfo); + + Location getTypeErrorLocation() + { + return TypeSyntax?.Location ?? this.GetFirstLocation(); + } } public abstract bool HasInitializer { get; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs index b9db8eceb84eb..d699f2cbeb737 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -1839,9 +1839,18 @@ protected virtual void ValidatePropertyType(BindingDiagnosticBag diagnostics) { diagnostics.Add(ErrorCode.ERR_FieldCantBeRefAny, TypeLocation, type); } - else if (this.IsAutoPropertyOrUsesFieldKeyword && type.IsRefLikeOrAllowsRefLikeType() && (this.IsStatic || !this.ContainingType.IsRefLikeType)) + + if (this.IsAutoPropertyOrUsesFieldKeyword) { - diagnostics.Add(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, TypeLocation, type); + if (!this.IsStatic && type.ContainsPointerOrFunctionPointer() && (ContainingType.IsRecord || ContainingType.IsRecordStruct)) + { + // The type '{0}' may not be used for a field of a record. + diagnostics.Add(ErrorCode.ERR_BadFieldTypeInRecord, TypeLocation, type); + } + else if (type.IsRefLikeOrAllowsRefLikeType() && (this.IsStatic || !this.ContainingType.IsRefLikeType)) + { + diagnostics.Add(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, TypeLocation, type); + } } if (type.IsStatic) diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEquals.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEquals.cs index 8301c43f491ec..caa49c6b63d92 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEquals.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEquals.cs @@ -136,12 +136,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, fields.Add(f); var parameterType = f.Type; - if (parameterType.IsPointerOrFunctionPointer()) - { - diagnostics.Add(ErrorCode.ERR_BadFieldTypeInRecord, f.GetFirstLocationOrNone(), parameterType); - foundBadField = true; - } - else if (parameterType.IsRestrictedType()) + if (parameterType.ContainsPointerOrFunctionPointer() || parameterType.IsRestrictedType()) { // We'll have reported a diagnostic elsewhere (SourceMemberFieldSymbol.TypeChecks) foundBadField = true; diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 4649012b006d8..4874ba96344ed 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -1278,7 +1278,7 @@ internal static bool ContainsTupleNames(this TypeSymbol type) => internal static bool ContainsFunctionPointer(this TypeSymbol type) => type.VisitType((TypeSymbol t, object? _, bool _) => t.IsFunctionPointer(), null) is object; - internal static bool ContainsPointer(this TypeSymbol type) => + internal static bool ContainsPointerOrFunctionPointer(this TypeSymbol type) => type.VisitType((TypeSymbol t, object? _, bool _) => t.TypeKind is TypeKind.Pointer or TypeKind.FunctionPointer, null) is object; /// diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/RecordTests.cs index 8fd3792ba13d2..52ad5a1a2ee16 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/RecordTests.cs @@ -1223,8 +1223,9 @@ class P1 { } ); } - [ConditionalFact(typeof(DesktopOnly), Reason = ConditionalSkipReason.RestrictedTypesNeedDesktop)] + [Fact] [WorkItem(48115, "https://github.com/dotnet/roslyn/issues/48115")] + [WorkItem("https://github.com/dotnet/roslyn/issues/66312")] public void RestrictedTypesAndPointerTypes() { var src = @" @@ -1246,14 +1247,20 @@ unsafe record C( "; - var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll); - comp.VerifyEmitDiagnostics( - // 0.cs(7,10): error CS8908: The type 'int*' may not be used for a field of a record. + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Mscorlib461); + DiagnosticDescription[] expected = [ + // 0.cs(7,5): error CS8908: The type 'int*' may not be used for a field of a record. // int* P1, // 1 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "P1").WithArguments("int*").WithLocation(7, 10), - // 0.cs(10,25): error CS8908: The type 'delegate*' may not be used for a field of a record. + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*").WithArguments("int*").WithLocation(7, 5), + // 0.cs(8,5): error CS8908: The type 'int*[]' may not be used for a field of a record. + // int*[] P2, + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*[]").WithArguments("int*[]").WithLocation(8, 5), + // 0.cs(9,5): error CS8908: The type 'C' may not be used for a field of a record. + // C P3, + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "C").WithArguments("C").WithLocation(9, 5), + // 0.cs(10,5): error CS8908: The type 'delegate*' may not be used for a field of a record. // delegate* P4, // 2 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "P4").WithArguments("delegate*").WithLocation(10, 25), + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*").WithArguments("delegate*").WithLocation(10, 5), // 0.cs(11,5): error CS1536: Invalid parameter type 'void' // void P5, // 3 Diagnostic(ErrorCode.ERR_NoVoidParameter, "void").WithLocation(11, 5), @@ -1271,12 +1278,85 @@ unsafe record C( Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "System.TypedReference").WithArguments("System.TypedReference").WithLocation(14, 5), // 0.cs(15,5): error CS8345: Field or auto-implemented property cannot be of type 'RefLike' unless it is an instance member of a ref struct. // RefLike P9, // 8 - Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(15, 5) - ); + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(15, 5), + // 0.cs(16,5): error CS8908: The type 'delegate*[]' may not be used for a field of a record. + // delegate*[] P10); + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*[]").WithArguments("delegate*[]").WithLocation(16, 5)]; + + comp.VerifyDiagnostics(expected); + comp.VerifyEmitDiagnostics(expected); } - [ConditionalFact(typeof(DesktopOnly), Reason = ConditionalSkipReason.RestrictedTypesNeedDesktop)] + [Fact] [WorkItem(48115, "https://github.com/dotnet/roslyn/issues/48115")] + [WorkItem("https://github.com/dotnet/roslyn/issues/66312")] + public void RestrictedTypesAndPointerTypes_WithEquals() + { + var src = @" +class C { } +static class C2 { } +ref struct RefLike{} + +unsafe record C( + int* P1, // 1 + int*[] P2, + C P3, + delegate* P4, // 2 + void P5, // 3 + C2 P6, // 4, 5 + System.ArgIterator P7, // 6 + System.TypedReference P8, // 7 + RefLike P9, // 8 + delegate*[] P10) +{ + public virtual bool Equals(C c) => true; + public override int GetHashCode() => 0; +} +"; + + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Mscorlib461); + DiagnosticDescription[] expected = [ + // 0.cs(7,5): error CS8908: The type 'int*' may not be used for a field of a record. + // int* P1, // 1 + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*").WithArguments("int*").WithLocation(7, 5), + // 0.cs(8,5): error CS8908: The type 'int*[]' may not be used for a field of a record. + // int*[] P2, + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*[]").WithArguments("int*[]").WithLocation(8, 5), + // 0.cs(9,5): error CS8908: The type 'C' may not be used for a field of a record. + // C P3, + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "C").WithArguments("C").WithLocation(9, 5), + // 0.cs(10,5): error CS8908: The type 'delegate*' may not be used for a field of a record. + // delegate* P4, // 2 + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*").WithArguments("delegate*").WithLocation(10, 5), + // 0.cs(11,5): error CS1536: Invalid parameter type 'void' + // void P5, // 3 + Diagnostic(ErrorCode.ERR_NoVoidParameter, "void").WithLocation(11, 5), + // 0.cs(12,5): error CS0721: 'C2': static types cannot be used as parameters + // C2 P6, // 4, 5 + Diagnostic(ErrorCode.ERR_ParameterIsStaticClass, "C2").WithArguments("C2").WithLocation(12, 5), + // 0.cs(12,5): error CS0722: 'C2': static types cannot be used as return types + // C2 P6, // 4, 5 + Diagnostic(ErrorCode.ERR_ReturnTypeIsStaticClass, "C2").WithArguments("C2").WithLocation(12, 5), + // 0.cs(13,5): error CS0610: Field or property cannot be of type 'ArgIterator' + // System.ArgIterator P7, // 6 + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "System.ArgIterator").WithArguments("System.ArgIterator").WithLocation(13, 5), + // 0.cs(14,5): error CS0610: Field or property cannot be of type 'TypedReference' + // System.TypedReference P8, // 7 + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "System.TypedReference").WithArguments("System.TypedReference").WithLocation(14, 5), + // 0.cs(15,5): error CS8345: Field or auto-implemented property cannot be of type 'RefLike' unless it is an instance member of a ref struct. + // RefLike P9, // 8 + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(15, 5), + // 0.cs(16,5): error CS8908: The type 'delegate*[]' may not be used for a field of a record. + // delegate*[] P10); + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*[]").WithArguments("delegate*[]").WithLocation(16, 5)]; + + comp.VerifyDiagnostics(expected); + comp.VerifyEmitDiagnostics(expected); + } + + [Fact] + [WorkItem(48115, "https://github.com/dotnet/roslyn/issues/48115")] + [WorkItem("https://github.com/dotnet/roslyn/issues/66312")] public void RestrictedTypesAndPointerTypes_NominalMembers() { var src = @" @@ -1299,14 +1379,20 @@ public unsafe record C } "; - var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll); - comp.VerifyEmitDiagnostics( - // 0.cs(8,17): error CS8908: The type 'int*' may not be used for a field of a record. + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Mscorlib461); + DiagnosticDescription[] expected = [ + // 0.cs(8,12): error CS8908: The type 'int*' may not be used for a field of a record. // public int* f1; // 1 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "f1").WithArguments("int*").WithLocation(8, 17), - // 0.cs(11,32): error CS8908: The type 'delegate*' may not be used for a field of a record. + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*").WithArguments("int*").WithLocation(8, 12), + // 0.cs(9,12): error CS8908: The type 'int*[]' may not be used for a field of a record. + // public int*[] f2; + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*[]").WithArguments("int*[]").WithLocation(9, 12), + // 0.cs(10,12): error CS8908: The type 'C' may not be used for a field of a record. + // public C f3; + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "C").WithArguments("C").WithLocation(10, 12), + // 0.cs(11,12): error CS8908: The type 'delegate*' may not be used for a field of a record. // public delegate* f4; // 3 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "f4").WithArguments("delegate*").WithLocation(11, 32), + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*").WithArguments("delegate*").WithLocation(11, 12), // 0.cs(12,12): error CS0670: Field cannot have void type // public void f5; // 4 Diagnostic(ErrorCode.ERR_FieldCantHaveVoidType, "void").WithLocation(12, 12), @@ -1321,12 +1407,17 @@ public unsafe record C Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "System.TypedReference").WithArguments("System.TypedReference").WithLocation(15, 12), // 0.cs(16,12): error CS8345: Field or auto-implemented property cannot be of type 'RefLike' unless it is an instance member of a ref struct. // public RefLike f9; // 8 - Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(16, 12) - ); + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(16, 12), + // 0.cs(17,12): error CS8908: The type 'delegate*[]' may not be used for a field of a record. + // public delegate*[] f10; + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*[]").WithArguments("delegate*[]").WithLocation(17, 12)]; + comp.VerifyDiagnostics(expected); + comp.VerifyEmitDiagnostics(expected); } - [ConditionalFact(typeof(DesktopOnly), Reason = ConditionalSkipReason.RestrictedTypesNeedDesktop)] + [Fact] [WorkItem(48115, "https://github.com/dotnet/roslyn/issues/48115")] + [WorkItem("https://github.com/dotnet/roslyn/issues/66312")] public void RestrictedTypesAndPointerTypes_NominalMembers_AutoProperties() { var src = @" @@ -1349,14 +1440,20 @@ public unsafe record C } "; - var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll); - comp.VerifyEmitDiagnostics( - // 0.cs(8,17): error CS8908: The type 'int*' may not be used for a field of a record. + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Mscorlib461); + DiagnosticDescription[] expected = [ + // 0.cs(8,12): error CS8908: The type 'int*' may not be used for a field of a record. // public int* f1 { get; set; } // 1 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "f1").WithArguments("int*").WithLocation(8, 17), - // 0.cs(11,32): error CS8908: The type 'delegate*' may not be used for a field of a record. + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*").WithArguments("int*").WithLocation(8, 12), + // 0.cs(9,12): error CS8908: The type 'int*[]' may not be used for a field of a record. + // public int*[] f2 { get; set; } + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*[]").WithArguments("int*[]").WithLocation(9, 12), + // 0.cs(10,12): error CS8908: The type 'C' may not be used for a field of a record. + // public C f3 { get; set; } + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "C").WithArguments("C").WithLocation(10, 12), + // 0.cs(11,12): error CS8908: The type 'delegate*' may not be used for a field of a record. // public delegate* f4 { get; set; } // 2 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "f4").WithArguments("delegate*").WithLocation(11, 32), + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*").WithArguments("delegate*").WithLocation(11, 12), // 0.cs(12,17): error CS0547: 'C.f5': property or indexer cannot have void type // public void f5 { get; set; } // 3 Diagnostic(ErrorCode.ERR_PropertyCantHaveVoidType, "f5").WithArguments("C.f5").WithLocation(12, 17), @@ -1371,12 +1468,18 @@ public unsafe record C Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "System.TypedReference").WithArguments("System.TypedReference").WithLocation(15, 12), // 0.cs(16,12): error CS8345: Field or auto-implemented property cannot be of type 'RefLike' unless it is an instance member of a ref struct. // public RefLike f9 { get; set; } // 8 - Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(16, 12) - ); + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(16, 12), + // 0.cs(17,12): error CS8908: The type 'delegate*[]' may not be used for a field of a record. + // public delegate*[] f10 { get; set; } + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*[]").WithArguments("delegate*[]").WithLocation(17, 12)]; + + comp.VerifyDiagnostics(expected); + comp.VerifyEmitDiagnostics(expected); } [Fact] [WorkItem(48115, "https://github.com/dotnet/roslyn/issues/48115")] + [WorkItem("https://github.com/dotnet/roslyn/issues/66312")] public void RestrictedTypesAndPointerTypes_PointerTypeAllowedForParameterAndProperty() { var src = @" @@ -1423,8 +1526,9 @@ public unsafe static void Main() CompileAndVerify(comp, expectedOutput: "P1 P2 P3 RAN", verify: Verification.Skipped /* pointers */); } - [ConditionalFact(typeof(DesktopOnly), Reason = ConditionalSkipReason.RestrictedTypesNeedDesktop)] + [Fact] [WorkItem(48115, "https://github.com/dotnet/roslyn/issues/48115")] + [WorkItem("https://github.com/dotnet/roslyn/issues/66312")] public void RestrictedTypesAndPointerTypes_StaticFields() { var src = @" @@ -1445,7 +1549,7 @@ public unsafe record C } "; - var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll); + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Mscorlib461); comp.VerifyEmitDiagnostics( // (12,22): error CS0723: Cannot declare a variable of static type 'C2' // public static C2 f6; // 1 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs index 83d327d060833..c3d6c0f655dff 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs @@ -1479,8 +1479,9 @@ record struct C9 : System.ICloneable ); } - [ConditionalFact(typeof(DesktopOnly), Reason = ConditionalSkipReason.RestrictedTypesNeedDesktop)] + [Fact] [WorkItem(48115, "https://github.com/dotnet/roslyn/issues/48115")] + [WorkItem("https://github.com/dotnet/roslyn/issues/66312")] public void RestrictedTypesAndPointerTypes() { var src = @" @@ -1498,17 +1499,23 @@ unsafe record struct C( System.ArgIterator P7, // 6 System.TypedReference P8, // 7 RefLike P9, // 8 - delegate* P10); // 9 + delegate*[] P10); // 9 "; - var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll); - comp.VerifyEmitDiagnostics( - // 0.cs(7,10): error CS8908: The type 'int*' may not be used for a field of a record. + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Mscorlib461); + DiagnosticDescription[] expected = [ + // 0.cs(7,5): error CS8908: The type 'int*' may not be used for a field of a record. // int* P1, // 1 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "P1").WithArguments("int*").WithLocation(7, 10), - // 0.cs(10,25): error CS8908: The type 'delegate*' may not be used for a field of a record. + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*").WithArguments("int*").WithLocation(7, 5), + // 0.cs(8,5): error CS8908: The type 'int*[]' may not be used for a field of a record. + // int*[] P2, + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*[]").WithArguments("int*[]").WithLocation(8, 5), + // 0.cs(9,5): error CS8908: The type 'C' may not be used for a field of a record. + // C P3, + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "C").WithArguments("C").WithLocation(9, 5), + // 0.cs(10,5): error CS8908: The type 'delegate*' may not be used for a field of a record. // delegate* P4, // 2 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "P4").WithArguments("delegate*").WithLocation(10, 25), + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*").WithArguments("delegate*").WithLocation(10, 5), // 0.cs(11,5): error CS1536: Invalid parameter type 'void' // void P5, // 3 Diagnostic(ErrorCode.ERR_NoVoidParameter, "void").WithLocation(11, 5), @@ -1527,14 +1534,82 @@ unsafe record struct C( // 0.cs(15,5): error CS8345: Field or auto-implemented property cannot be of type 'RefLike' unless it is an instance member of a ref struct. // RefLike P9, // 8 Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(15, 5), - // 0.cs(16,21): error CS8908: The type 'delegate*' may not be used for a field of a record. - // delegate* P10); // 9 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "P10").WithArguments("delegate*").WithLocation(16, 21) - ); + // 0.cs(16,5): error CS8908: The type 'delegate*[]' may not be used for a field of a record. + // delegate*[] P10); + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*[]").WithArguments("delegate*[]").WithLocation(16, 5)]; + comp.VerifyDiagnostics(expected); + comp.VerifyEmitDiagnostics(expected); } + [Fact] + [WorkItem(48115, "https://github.com/dotnet/roslyn/issues/48115")] + [WorkItem("https://github.com/dotnet/roslyn/issues/66312")] + public void RestrictedTypesAndPointerTypes_WithEquals() + { + var src = @" +class C { } +static class C2 { } +ref struct RefLike{} - [ConditionalFact(typeof(DesktopOnly), Reason = ConditionalSkipReason.RestrictedTypesNeedDesktop)] +unsafe record struct C( + int* P1, // 1 + int*[] P2, + C P3, + delegate* P4, // 2 + void P5, // 3 + C2 P6, // 4, 5 + System.ArgIterator P7, // 6 + System.TypedReference P8, // 7 + RefLike P9, // 8 + delegate*[] P10) +{ + public bool Equals(C c) => true; + public override int GetHashCode() => 0; +} +"; + + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Mscorlib461); + DiagnosticDescription[] expected = [ + // 0.cs(7,5): error CS8908: The type 'int*' may not be used for a field of a record. + // int* P1, // 1 + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*").WithArguments("int*").WithLocation(7, 5), + // 0.cs(8,5): error CS8908: The type 'int*[]' may not be used for a field of a record. + // int*[] P2, + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*[]").WithArguments("int*[]").WithLocation(8, 5), + // 0.cs(9,5): error CS8908: The type 'C' may not be used for a field of a record. + // C P3, + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "C").WithArguments("C").WithLocation(9, 5), + // 0.cs(10,5): error CS8908: The type 'delegate*' may not be used for a field of a record. + // delegate* P4, // 2 + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*").WithArguments("delegate*").WithLocation(10, 5), + // 0.cs(11,5): error CS1536: Invalid parameter type 'void' + // void P5, // 3 + Diagnostic(ErrorCode.ERR_NoVoidParameter, "void").WithLocation(11, 5), + // 0.cs(12,5): error CS0721: 'C2': static types cannot be used as parameters + // C2 P6, // 4, 5 + Diagnostic(ErrorCode.ERR_ParameterIsStaticClass, "C2").WithArguments("C2").WithLocation(12, 5), + // 0.cs(12,5): error CS0722: 'C2': static types cannot be used as return types + // C2 P6, // 4, 5 + Diagnostic(ErrorCode.ERR_ReturnTypeIsStaticClass, "C2").WithArguments("C2").WithLocation(12, 5), + // 0.cs(13,5): error CS0610: Field or property cannot be of type 'ArgIterator' + // System.ArgIterator P7, // 6 + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "System.ArgIterator").WithArguments("System.ArgIterator").WithLocation(13, 5), + // 0.cs(14,5): error CS0610: Field or property cannot be of type 'TypedReference' + // System.TypedReference P8, // 7 + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "System.TypedReference").WithArguments("System.TypedReference").WithLocation(14, 5), + // 0.cs(15,5): error CS8345: Field or auto-implemented property cannot be of type 'RefLike' unless it is an instance member of a ref struct. + // RefLike P9, // 8 + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(15, 5), + // 0.cs(16,5): error CS8908: The type 'delegate*[]' may not be used for a field of a record. + // delegate*[] P10); + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*[]").WithArguments("delegate*[]").WithLocation(16, 5)]; + + comp.VerifyDiagnostics(expected); + comp.VerifyEmitDiagnostics(expected); + } + + [Fact] [WorkItem(48115, "https://github.com/dotnet/roslyn/issues/48115")] + [WorkItem("https://github.com/dotnet/roslyn/issues/66312")] public void RestrictedTypesAndPointerTypes_NominalMembers() { var src = @" @@ -1553,41 +1628,49 @@ public unsafe record struct C public System.ArgIterator f7; // 5 public System.TypedReference f8; // 6 public RefLike f9; // 7 - public delegate* f10; // 8 + public delegate*[] f10; // 8 } "; - var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll); - comp.VerifyEmitDiagnostics( - // 0.cs(8,17): error CS8908: The type 'int*' may not be used for a field of a record. + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Mscorlib461); + DiagnosticDescription[] expected = [ + // 0.cs(8,12): error CS8908: The type 'int*' may not be used for a field of a record. // public int* f1; // 1 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "f1").WithArguments("int*").WithLocation(8, 17), - // 0.cs(11,32): error CS8908: The type 'delegate*' may not be used for a field of a record. - // public delegate* f4; // 2 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "f4").WithArguments("delegate*").WithLocation(11, 32), + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*").WithArguments("int*").WithLocation(8, 12), + // 0.cs(9,12): error CS8908: The type 'int*[]' may not be used for a field of a record. + // public int*[] f2; + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*[]").WithArguments("int*[]").WithLocation(9, 12), + // 0.cs(10,12): error CS8908: The type 'C' may not be used for a field of a record. + // public C f3; + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "C").WithArguments("C").WithLocation(10, 12), + // 0.cs(11,12): error CS8908: The type 'delegate*' may not be used for a field of a record. + // public delegate* f4; // 3 + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*").WithArguments("delegate*").WithLocation(11, 12), // 0.cs(12,12): error CS0670: Field cannot have void type - // public void f5; // 3 + // public void f5; // 4 Diagnostic(ErrorCode.ERR_FieldCantHaveVoidType, "void").WithLocation(12, 12), // 0.cs(13,15): error CS0723: Cannot declare a variable of static type 'C2' - // public C2 f6; // 4 + // public C2 f6; // 5 Diagnostic(ErrorCode.ERR_VarDeclIsStaticClass, "f6").WithArguments("C2").WithLocation(13, 15), // 0.cs(14,12): error CS0610: Field or property cannot be of type 'ArgIterator' - // public System.ArgIterator f7; // 5 + // public System.ArgIterator f7; // 6 Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "System.ArgIterator").WithArguments("System.ArgIterator").WithLocation(14, 12), // 0.cs(15,12): error CS0610: Field or property cannot be of type 'TypedReference' - // public System.TypedReference f8; // 6 + // public System.TypedReference f8; // 7 Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "System.TypedReference").WithArguments("System.TypedReference").WithLocation(15, 12), // 0.cs(16,12): error CS8345: Field or auto-implemented property cannot be of type 'RefLike' unless it is an instance member of a ref struct. - // public RefLike f9; // 7 + // public RefLike f9; // 8 Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(16, 12), - // 0.cs(17,28): error CS8908: The type 'delegate*' may not be used for a field of a record. - // public delegate* f10; // 8 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "f10").WithArguments("delegate*").WithLocation(17, 28) - ); + // 0.cs(17,12): error CS8908: The type 'delegate*[]' may not be used for a field of a record. + // public delegate*[] f10; + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*[]").WithArguments("delegate*[]").WithLocation(17, 12)]; + comp.VerifyDiagnostics(expected); + comp.VerifyEmitDiagnostics(expected); } - [ConditionalFact(typeof(DesktopOnly), Reason = ConditionalSkipReason.RestrictedTypesNeedDesktop)] + [Fact] [WorkItem(48115, "https://github.com/dotnet/roslyn/issues/48115")] + [WorkItem("https://github.com/dotnet/roslyn/issues/66312")] public void RestrictedTypesAndPointerTypes_NominalMembers_AutoProperties() { var src = @" @@ -1610,34 +1693,45 @@ public unsafe record struct C } "; - var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll); - comp.VerifyEmitDiagnostics( - // 0.cs(8,17): error CS8908: The type 'int*' may not be used for a field of a record. + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Mscorlib461); + DiagnosticDescription[] expected = [ + // 0.cs(8,12): error CS8908: The type 'int*' may not be used for a field of a record. // public int* f1 { get; set; } // 1 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "f1").WithArguments("int*").WithLocation(8, 17), - // 0.cs(11,32): error CS8908: The type 'delegate*' may not be used for a field of a record. + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*").WithArguments("int*").WithLocation(8, 12), + // 0.cs(9,12): error CS8908: The type 'int*[]' may not be used for a field of a record. + // public int*[] f2 { get; set; } + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int*[]").WithArguments("int*[]").WithLocation(9, 12), + // 0.cs(10,12): error CS8908: The type 'C' may not be used for a field of a record. + // public C f3 { get; set; } + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "C").WithArguments("C").WithLocation(10, 12), + // 0.cs(11,12): error CS8908: The type 'delegate*' may not be used for a field of a record. // public delegate* f4 { get; set; } // 2 - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "f4").WithArguments("delegate*").WithLocation(11, 32), + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*").WithArguments("delegate*").WithLocation(11, 12), // 0.cs(12,17): error CS0547: 'C.f5': property or indexer cannot have void type // public void f5 { get; set; } // 3 Diagnostic(ErrorCode.ERR_PropertyCantHaveVoidType, "f5").WithArguments("C.f5").WithLocation(12, 17), // 0.cs(13,12): error CS0722: 'C2': static types cannot be used as return types - // public C2 f6 { get; set; } // 4 + // public C2 f6 { get; set; } // 4, 5 Diagnostic(ErrorCode.ERR_ReturnTypeIsStaticClass, "C2").WithArguments("C2").WithLocation(13, 12), // 0.cs(14,12): error CS0610: Field or property cannot be of type 'ArgIterator' - // public System.ArgIterator f7 { get; set; } // 5 + // public System.ArgIterator f7 { get; set; } // 6 Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "System.ArgIterator").WithArguments("System.ArgIterator").WithLocation(14, 12), // 0.cs(15,12): error CS0610: Field or property cannot be of type 'TypedReference' - // public System.TypedReference f8 { get; set; } // 6 + // public System.TypedReference f8 { get; set; } // 7 Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "System.TypedReference").WithArguments("System.TypedReference").WithLocation(15, 12), // 0.cs(16,12): error CS8345: Field or auto-implemented property cannot be of type 'RefLike' unless it is an instance member of a ref struct. - // public RefLike f9 { get; set; } // 7 - Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(16, 12) - ); + // public RefLike f9 { get; set; } // 8 + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(16, 12), + // 0.cs(17,12): error CS8908: The type 'delegate*[]' may not be used for a field of a record. + // public delegate*[] f10 { get; set; } + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "delegate*[]").WithArguments("delegate*[]").WithLocation(17, 12)]; + comp.VerifyDiagnostics(expected); + comp.VerifyEmitDiagnostics(expected); } [Fact] [WorkItem(48115, "https://github.com/dotnet/roslyn/issues/48115")] + [WorkItem("https://github.com/dotnet/roslyn/issues/66312")] public void RestrictedTypesAndPointerTypes_PointerTypeAllowedForParameterAndProperty() { var src = @" @@ -1685,8 +1779,9 @@ public unsafe static void Main() CompileAndVerify(comp, expectedOutput: "P1 P2 P3 RAN", verify: Verification.Skipped /* pointers */); } - [ConditionalFact(typeof(DesktopOnly), Reason = ConditionalSkipReason.RestrictedTypesNeedDesktop)] + [Fact] [WorkItem(48115, "https://github.com/dotnet/roslyn/issues/48115")] + [WorkItem("https://github.com/dotnet/roslyn/issues/66312")] public void RestrictedTypesAndPointerTypes_StaticFields() { var src = @" @@ -1707,9 +1802,8 @@ public unsafe record C } "; - var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll); - comp.VerifyEmitDiagnostics( - // (12,22): error CS0723: Cannot declare a variable of static type 'C2' + var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Mscorlib461); + DiagnosticDescription[] expected = [// (12,22): error CS0723: Cannot declare a variable of static type 'C2' // public static C2 f6; // 1 Diagnostic(ErrorCode.ERR_VarDeclIsStaticClass, "f6").WithArguments("C2").WithLocation(12, 22), // (13,19): error CS0610: Field or property cannot be of type 'ArgIterator' @@ -1720,8 +1814,9 @@ public unsafe record C Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "System.TypedReference").WithArguments("System.TypedReference").WithLocation(14, 19), // (15,19): error CS8345: Field or auto-implemented property cannot be of type 'RefLike' unless it is an instance member of a ref struct. // public static RefLike f9; // 4 - Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(15, 19) - ); + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "RefLike").WithArguments("RefLike").WithLocation(15, 19)]; + comp.VerifyDiagnostics(expected); + comp.VerifyEmitDiagnostics(expected); } [Fact] @@ -10599,7 +10694,7 @@ unsafe record struct C(int[] P) Diagnostic(ErrorCode.ERR_BadRecordMemberForPositionalParameter, "P").WithArguments("C.P", "int[]", "P").WithLocation(2, 30), // (4,22): error CS8908: The type 'int*' may not be used for a field of a record. // public fixed int P[2]; - Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "P").WithArguments("int*").WithLocation(4, 22) + Diagnostic(ErrorCode.ERR_BadFieldTypeInRecord, "int").WithArguments("int*").WithLocation(4, 18) ); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs index 975aaf9cc10b7..9ca1ad18190eb 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs @@ -2673,18 +2673,18 @@ unsafe class C Assert.True(fieldTypes[0].Type.IsPointerOrFunctionPointer()); Assert.True(fieldTypes[1].Type.IsPointerOrFunctionPointer()); Assert.False(fieldTypes[2].Type.IsPointerOrFunctionPointer()); - Assert.True(fieldTypes[2].Type.ContainsPointer()); + Assert.True(fieldTypes[2].Type.ContainsPointerOrFunctionPointer()); Assert.False(fieldTypes[3].Type.IsPointerOrFunctionPointer()); - Assert.True(fieldTypes[3].Type.ContainsPointer()); + Assert.True(fieldTypes[3].Type.ContainsPointerOrFunctionPointer()); Assert.False(fieldTypes[4].Type.IsPointerOrFunctionPointer()); - Assert.True(fieldTypes[4].Type.ContainsPointer()); + Assert.True(fieldTypes[4].Type.ContainsPointerOrFunctionPointer()); Assert.False(fieldTypes[5].Type.IsPointerOrFunctionPointer()); - Assert.True(fieldTypes[5].Type.ContainsPointer()); + Assert.True(fieldTypes[5].Type.ContainsPointerOrFunctionPointer()); Assert.False(fieldTypes[6].Type.IsPointerOrFunctionPointer()); - Assert.True(fieldTypes[6].Type.ContainsPointer()); + Assert.True(fieldTypes[6].Type.ContainsPointerOrFunctionPointer()); Assert.False(fieldTypes[7].Type.IsPointerOrFunctionPointer()); - Assert.True(fieldTypes[7].Type.ContainsPointer()); + Assert.True(fieldTypes[7].Type.ContainsPointerOrFunctionPointer()); } [Fact]