diff --git a/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs b/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs index 7d0ab5cfe1283..5918d1d848ee6 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 or a ref field. Unfortunately, this can result in infinite recursion. + /// if one of its instance fields is managed. 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,12 +128,13 @@ 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; @@ -146,98 +147,92 @@ internal static ManagedKind GetManagedKind(NamedTypeSymbol type, ref CompoundUse { return ManagedKind.Unmanaged; } + } - static (bool definitelyManaged, bool hasGenerics) dependsOnDefinitelyManagedType(NamedTypeSymbol type, HashSet partialClosure, ref CompoundUseSiteInfo useSiteInfo) - { - Debug.Assert((object)type != null); + // 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)) + var hasGenerics = false; + if (partialClosure.Add(type)) + { + foreach (var member in type.GetInstanceFieldsAndEvents()) { - foreach (var member in type.GetInstanceFieldsAndEvents()) + // Only instance fields (including field-like events) affect the outcome. + FieldSymbol field; + switch (member.Kind) { - // 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); - } + 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); + } - if ((object)field == null) - { - continue; - } + if ((object)field == null) + { + 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) + { + // pointers are unmanaged + continue; + } - TypeSymbol fieldType = field.NonPointerType(); - if (fieldType is null) + fieldType.AddUseSiteInfo(ref useSiteInfo); + NamedTypeSymbol fieldNamedType = fieldType as NamedTypeSymbol; + if ((object)fieldNamedType == null) + { + if (fieldType.IsManagedType(ref useSiteInfo)) { - // pointers are unmanaged - continue; + return (true, hasGenerics); } - - fieldType.AddUseSiteInfo(ref useSiteInfo); - NamedTypeSymbol fieldNamedType = fieldType as NamedTypeSymbol; - if ((object)fieldNamedType == null) + } + 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) { - if (fieldType.IsManagedType(ref useSiteInfo)) - { + case ThreeState.True: 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) + + case ThreeState.False: + continue; + + case ThreeState.Unknown: + if (!fieldNamedType.OriginalDefinition.KnownCircularStruct) + { + var (definitelyManaged, childHasGenerics) = DependsOnDefinitelyManagedType(fieldNamedType, partialClosure, ref useSiteInfo); + hasGenerics = hasGenerics || childHasGenerics; + if (definitelyManaged) { - var (definitelyManaged, childHasGenerics) = dependsOnDefinitelyManagedType(fieldNamedType, partialClosure, ref useSiteInfo); - hasGenerics = hasGenerics || childHasGenerics; - if (definitelyManaged) - { - return (true, hasGenerics); - } + return (true, hasGenerics); } - continue; - } + } + continue; } } } - - 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; + return (false, hasGenerics); + } /// /// Returns True or False if we can determine whether the type is managed diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 7afcdbf268d2a..f9c2b43d59737 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -7211,117 +7211,6 @@ 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()