Skip to content

Commit

Permalink
Consider types with ref fields as managed
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv committed Aug 4, 2022
1 parent 7ab83bb commit 27e8d56
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 2 deletions.
9 changes: 7 additions & 2 deletions src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ private static void StructDependsClosure(NamedTypeSymbol type, HashSet<Symbol> 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.
///
Expand All @@ -134,7 +134,6 @@ internal static ManagedKind GetManagedKind(NamedTypeSymbol type, ref CompoundUse
hs.Free();
}


if (definitelyManaged)
{
return ManagedKind.Managed;
Expand Down Expand Up @@ -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)
{
Expand Down
66 changes: 66 additions & 0 deletions src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<StructWithRefField>(); // 3
}

public ref struct StructWithRefField
{
public ref byte RefField;
}

class C
{
public static void M<T>() 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<StructWithRefField>(); // 3
Diagnostic(ErrorCode.ERR_BadTypeArgument, "M<StructWithRefField>").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()
Expand Down

0 comments on commit 27e8d56

Please sign in to comment.