diff --git a/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs b/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs index 5918d1d848ee6..7d0ab5cfe1283 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. /// @@ -128,13 +128,12 @@ internal static ManagedKind GetManagedKind(NamedTypeSymbol type, ref CompoundUse { // Otherwise, we have to build and inspect the closure of depended-upon types. var hs = PooledHashSet.GetInstance(); - var result = DependsOnDefinitelyManagedType(type, hs, ref useSiteInfo); + var result = dependsOnDefinitelyManagedType(type, hs, ref useSiteInfo); definitelyManaged = result.definitelyManaged; hasGenerics = hasGenerics || result.hasGenerics; hs.Free(); } - if (definitelyManaged) { return ManagedKind.Managed; @@ -147,93 +146,99 @@ internal static ManagedKind GetManagedKind(NamedTypeSymbol type, ref CompoundUse { return ManagedKind.Unmanaged; } - } - - // NOTE: If we do not check HasPointerType, we will unconditionally - // bind Type and that may cause infinite recursion. - // HasPointerType can use syntax directly and break recursion. - internal static TypeSymbol NonPointerType(this FieldSymbol field) => - field.HasPointerType ? null : field.Type; - - private static (bool definitelyManaged, bool hasGenerics) DependsOnDefinitelyManagedType(NamedTypeSymbol type, HashSet partialClosure, ref CompoundUseSiteInfo useSiteInfo) - { - Debug.Assert((object)type != null); - var hasGenerics = false; - if (partialClosure.Add(type)) + static (bool definitelyManaged, bool hasGenerics) dependsOnDefinitelyManagedType(NamedTypeSymbol type, HashSet partialClosure, ref CompoundUseSiteInfo useSiteInfo) { - foreach (var member in type.GetInstanceFieldsAndEvents()) - { - // Only instance fields (including field-like events) affect the outcome. - FieldSymbol field; - switch (member.Kind) - { - case SymbolKind.Field: - field = (FieldSymbol)member; - Debug.Assert((object)(field.AssociatedSymbol as EventSymbol) == null, - "Didn't expect to find a field-like event backing field in the member list."); - break; - case SymbolKind.Event: - field = ((EventSymbol)member).AssociatedField; - break; - default: - throw ExceptionUtilities.UnexpectedValue(member.Kind); - } + Debug.Assert((object)type != null); - if ((object)field == null) + var hasGenerics = false; + if (partialClosure.Add(type)) + { + foreach (var member in type.GetInstanceFieldsAndEvents()) { - continue; - } + // Only instance fields (including field-like events) affect the outcome. + FieldSymbol field; + switch (member.Kind) + { + case SymbolKind.Field: + field = (FieldSymbol)member; + Debug.Assert((object)(field.AssociatedSymbol as EventSymbol) == null, + "Didn't expect to find a field-like event backing field in the member list."); + break; + case SymbolKind.Event: + field = ((EventSymbol)member).AssociatedField; + break; + default: + throw ExceptionUtilities.UnexpectedValue(member.Kind); + } - TypeSymbol fieldType = field.NonPointerType(); - if (fieldType is null) - { - // pointers are unmanaged - continue; - } + if ((object)field == null) + { + continue; + } - fieldType.AddUseSiteInfo(ref useSiteInfo); - NamedTypeSymbol fieldNamedType = fieldType as NamedTypeSymbol; - if ((object)fieldNamedType == null) - { - if (fieldType.IsManagedType(ref useSiteInfo)) + if (field.RefKind != RefKind.None) { + // A ref struct which has a ref field is never considered unmanaged return (true, hasGenerics); } - } - else - { - var result = IsManagedTypeHelper(fieldNamedType); - hasGenerics = hasGenerics || result.hasGenerics; - // NOTE: don't use ManagedKind.get on a NamedTypeSymbol - that could lead - // to infinite recursion. - switch (result.isManaged) - { - case ThreeState.True: - return (true, hasGenerics); - case ThreeState.False: - continue; + TypeSymbol fieldType = field.NonPointerType(); + if (fieldType is null) + { + // pointers are unmanaged + continue; + } - case ThreeState.Unknown: - if (!fieldNamedType.OriginalDefinition.KnownCircularStruct) - { - var (definitelyManaged, childHasGenerics) = DependsOnDefinitelyManagedType(fieldNamedType, partialClosure, ref useSiteInfo); - hasGenerics = hasGenerics || childHasGenerics; - if (definitelyManaged) + fieldType.AddUseSiteInfo(ref useSiteInfo); + NamedTypeSymbol fieldNamedType = fieldType as NamedTypeSymbol; + if ((object)fieldNamedType == null) + { + if (fieldType.IsManagedType(ref useSiteInfo)) + { + return (true, hasGenerics); + } + } + else + { + var result = IsManagedTypeHelper(fieldNamedType); + hasGenerics = hasGenerics || result.hasGenerics; + // NOTE: don't use ManagedKind.get on a NamedTypeSymbol - that could lead + // to infinite recursion. + switch (result.isManaged) + { + case ThreeState.True: + return (true, hasGenerics); + + case ThreeState.False: + continue; + + case ThreeState.Unknown: + if (!fieldNamedType.OriginalDefinition.KnownCircularStruct) { - return (true, hasGenerics); + var (definitelyManaged, childHasGenerics) = dependsOnDefinitelyManagedType(fieldNamedType, partialClosure, ref useSiteInfo); + hasGenerics = hasGenerics || childHasGenerics; + if (definitelyManaged) + { + return (true, hasGenerics); + } } - } - continue; + continue; + } } } } - } - return (false, hasGenerics); + return (false, hasGenerics); + } } + // NOTE: If we do not check HasPointerType, we will unconditionally + // bind Type and that may cause infinite recursion. + // HasPointerType can use syntax directly and break recursion. + internal static TypeSymbol NonPointerType(this FieldSymbol field) => + field.HasPointerType ? null : field.Type; + /// /// Returns True or False if we can determine whether the type is managed /// without looking at its fields and Unknown otherwise. diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index affb1351cfc93..d77bd388836a1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -7408,6 +7408,117 @@ 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, runtimeFeature: RuntimeFlag.ByRefFields); + 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_Generic() + { + var source = @" +unsafe +{ + StructWithIndirectRefField* p = stackalloc StructWithIndirectRefField[10]; // 1, 2 + C.M(); // 3 +} + +ref struct StructWithIndirectRefField +{ + public StructWithRefField Field; +} +ref struct StructWithRefField +{ + public ref T RefField; +} + +class C +{ + public static void M() where T : unmanaged { } +}"; + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugExe, runtimeFeature: RuntimeFlag.ByRefFields); + 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), + // (5,9): error CS0305: Using the generic type 'StructWithRefField' requires 1 type arguments + // C.M(); // 3 + Diagnostic(ErrorCode.ERR_BadArity, "StructWithRefField").WithArguments("StructWithRefField", "type", "1").WithLocation(5, 9), + // (10,36): warning CS0649: Field 'StructWithIndirectRefField.Field' is never assigned to, and will always have its default value + // public StructWithRefField Field; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field").WithArguments("StructWithIndirectRefField.Field", "").WithLocation(10, 36), + // (14,18): warning CS0649: Field 'StructWithRefField.RefField' is never assigned to, and will always have its default value + // public ref T RefField; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "RefField").WithArguments("StructWithRefField.RefField", "").WithLocation(14, 18) + ); + + Assert.True(comp.GetTypeByMetadataName("StructWithIndirectRefField").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, runtimeFeature: RuntimeFlag.ByRefFields); + 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()