From 27e8d561ead7eddd309447d3c0f2022dbab25fc4 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 4 Aug 2022 10:34:54 -0700 Subject: [PATCH] Consider types with ref fields as managed --- .../Portable/Symbols/BaseTypeAnalysis.cs | 9 ++- .../Test/Semantic/Semantics/RefFieldTests.cs | 66 +++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs b/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs index 5918d1d848ee6..c9f9de0e57915 100644 --- a/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs @@ -112,7 +112,7 @@ private static void StructDependsClosure(NamedTypeSymbol type, HashSet p /// all special types have spec'd values (basically, (non-string) primitives) are not managed; /// /// Only structs are complicated, because the definition is recursive. A struct type is managed - /// if one of its instance fields is managed. Unfortunately, this can result in infinite recursion. + /// if one of its instance fields is managed or a ref field. Unfortunately, this can result in infinite recursion. /// If the closure is finite, and we don't find anything definitely managed, then we return true. /// If the closure is infinite, we disregard all but a representative of any expanding cycle. /// @@ -134,7 +134,6 @@ internal static ManagedKind GetManagedKind(NamedTypeSymbol type, ref CompoundUse hs.Free(); } - if (definitelyManaged) { return ManagedKind.Managed; @@ -185,6 +184,12 @@ private static (bool definitelyManaged, bool hasGenerics) DependsOnDefinitelyMan continue; } + if (field.RefKind != RefKind.None) + { + // A ref struct which has a ref field is never considered unmanaged + return (true, hasGenerics); + } + TypeSymbol fieldType = field.NonPointerType(); if (fieldType is null) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index c982a430e8886..c5103e53e5411 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -6305,6 +6305,72 @@ static ref int F2() Diagnostic(ErrorCode.ERR_EscapeVariable, "s1").WithArguments("s1").WithLocation(7, 20)); } + [Fact, WorkItem(63104, "https://github.com/dotnet/roslyn/issues/63104")] + public void RefFieldsConsideredManaged() + { + var source = @" +unsafe +{ + StructWithRefField* p = stackalloc StructWithRefField[10]; // 1, 2 + C.M(); // 3 +} + +public ref struct StructWithRefField +{ + public ref byte RefField; +} + +class C +{ + public static void M() where T : unmanaged { } +}"; + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugExe); + comp.VerifyDiagnostics( + // (4,5): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('StructWithRefField') + // StructWithRefField* p = stackalloc StructWithRefField[10]; // 1, 2 + Diagnostic(ErrorCode.ERR_ManagedAddr, "StructWithRefField*").WithArguments("StructWithRefField").WithLocation(4, 5), + // (4,40): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('StructWithRefField') + // StructWithRefField* p = stackalloc StructWithRefField[10]; // 1, 2 + Diagnostic(ErrorCode.ERR_ManagedAddr, "StructWithRefField").WithArguments("StructWithRefField").WithLocation(4, 40), + // (5,7): error CS0306: The type 'StructWithRefField' may not be used as a type argument + // C.M(); // 3 + Diagnostic(ErrorCode.ERR_BadTypeArgument, "M").WithArguments("StructWithRefField").WithLocation(5, 7) + ); + + Assert.True(comp.GetTypeByMetadataName("StructWithRefField").IsManagedTypeNoUseSiteDiagnostics); + } + + [Fact, WorkItem(63104, "https://github.com/dotnet/roslyn/issues/63104")] + public void RefFieldsConsideredManaged_Indirect() + { + var source = @" +unsafe +{ + StructWithIndirectRefField* p = stackalloc StructWithIndirectRefField[10]; // 1, 2 +} + +public ref struct StructWithIndirectRefField +{ + public StructWithRefField Field; +} + +public ref struct StructWithRefField +{ + public ref byte RefField; +}"; + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugExe); + comp.VerifyDiagnostics( + // (4,5): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('StructWithIndirectRefField') + // StructWithIndirectRefField* p = stackalloc StructWithIndirectRefField[10]; // 1, 2 + Diagnostic(ErrorCode.ERR_ManagedAddr, "StructWithIndirectRefField*").WithArguments("StructWithIndirectRefField").WithLocation(4, 5), + // (4,48): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('StructWithIndirectRefField') + // StructWithIndirectRefField* p = stackalloc StructWithIndirectRefField[10]; // 1, 2 + Diagnostic(ErrorCode.ERR_ManagedAddr, "StructWithIndirectRefField").WithArguments("StructWithIndirectRefField").WithLocation(4, 48) + ); + + Assert.True(comp.GetTypeByMetadataName("StructWithIndirectRefField").IsManagedTypeNoUseSiteDiagnostics); + } + // Breaking change in C#11: Cannot return an 'out' parameter by reference. [Fact] public void BreakingChange_ReturnOutByRef()