diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs index 3c05db15f7c2d..d95a736e0405d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs @@ -29,12 +29,16 @@ internal sealed class SynthesizedReadOnlyListTypeSymbol : NamedTypeSymbol { SpecialType.System_Collections_IEnumerable, SpecialType.System_Collections_Generic_IEnumerable_T, - SpecialType.System_Collections_Generic_IReadOnlyCollection_T, - SpecialType.System_Collections_Generic_IReadOnlyList_T, SpecialType.System_Collections_Generic_ICollection_T, SpecialType.System_Collections_Generic_IList_T, }; + private static readonly SpecialType[] s_readOnlyInterfacesSpecialTypes = new[] + { + SpecialType.System_Collections_Generic_IReadOnlyCollection_T, + SpecialType.System_Collections_Generic_IReadOnlyList_T, + }; + private static readonly WellKnownType[] s_requiredWellKnownTypes = new[] { WellKnownType.System_Collections_ICollection, @@ -63,8 +67,6 @@ internal sealed class SynthesizedReadOnlyListTypeSymbol : NamedTypeSymbol WellKnownMember.System_Collections_IList__Insert, WellKnownMember.System_Collections_IList__Remove, WellKnownMember.System_Collections_IList__RemoveAt, - WellKnownMember.System_Collections_Generic_IReadOnlyCollection_T__Count, - WellKnownMember.System_Collections_Generic_IReadOnlyList_T__get_Item, WellKnownMember.System_Collections_Generic_ICollection_T__Count, WellKnownMember.System_Collections_Generic_ICollection_T__IsReadOnly, WellKnownMember.System_Collections_Generic_ICollection_T__Add, @@ -79,6 +81,12 @@ internal sealed class SynthesizedReadOnlyListTypeSymbol : NamedTypeSymbol WellKnownMember.System_NotSupportedException__ctor, }; + private static readonly WellKnownMember[] s_readOnlyInterfacesWellKnownMembers = new[] + { + WellKnownMember.System_Collections_Generic_IReadOnlyCollection_T__Count, + WellKnownMember.System_Collections_Generic_IReadOnlyList_T__get_Item, + }; + private static readonly WellKnownMember[] s_requiredWellKnownMembersUnknownLength = new[] { WellKnownMember.System_Collections_Generic_List_T__Count, @@ -93,6 +101,10 @@ internal static NamedTypeSymbol Create(SourceModuleSymbol containingModule, stri var compilation = containingModule.DeclaringCompilation; DiagnosticInfo? diagnosticInfo = null; + var hasReadOnlyInterfaces = + !compilation.IsTypeMissing(SpecialType.System_Collections_Generic_IReadOnlyCollection_T) + && !compilation.IsTypeMissing(SpecialType.System_Collections_Generic_IReadOnlyList_T); + foreach (var type in s_requiredSpecialTypes) { diagnosticInfo = compilation.GetSpecialType(type).GetUseSiteInfo().DiagnosticInfo; @@ -102,6 +114,18 @@ internal static NamedTypeSymbol Create(SourceModuleSymbol containingModule, stri } } + if (hasReadOnlyInterfaces && diagnosticInfo is null) + { + foreach (var type in s_readOnlyInterfacesSpecialTypes) + { + diagnosticInfo = compilation.GetSpecialType(type).GetUseSiteInfo().DiagnosticInfo; + if (diagnosticInfo is { }) + { + break; + } + } + } + if (diagnosticInfo is null) { foreach (var type in s_requiredWellKnownTypes) @@ -138,6 +162,18 @@ internal static NamedTypeSymbol Create(SourceModuleSymbol containingModule, stri } } + if (hasReadOnlyInterfaces && diagnosticInfo is null) + { + foreach (var member in s_readOnlyInterfacesWellKnownMembers) + { + diagnosticInfo = getWellKnownTypeMemberDiagnosticInfo(compilation, member); + if (diagnosticInfo is { }) + { + break; + } + } + } + if (!hasKnownLength) { if (diagnosticInfo is null) @@ -163,7 +199,7 @@ internal static NamedTypeSymbol Create(SourceModuleSymbol containingModule, stri return new ExtendedErrorTypeSymbol(compilation, name, arity: 1, diagnosticInfo, unreported: true); } - return new SynthesizedReadOnlyListTypeSymbol(containingModule, name, hasKnownLength); + return new SynthesizedReadOnlyListTypeSymbol(containingModule, name, hasKnownLength, hasReadOnlyInterfaces); static DiagnosticInfo? getSpecialTypeMemberDiagnosticInfo(CSharpCompilation compilation, SpecialMember member) { @@ -194,7 +230,7 @@ internal static NamedTypeSymbol Create(SourceModuleSymbol containingModule, stri private readonly ImmutableArray _members; private readonly FieldSymbol _field; - private SynthesizedReadOnlyListTypeSymbol(SourceModuleSymbol containingModule, string name, bool hasKnownLength) + private SynthesizedReadOnlyListTypeSymbol(SourceModuleSymbol containingModule, string name, bool hasKnownLength, bool hasReadOnlyInterfaces) { var compilation = containingModule.DeclaringCompilation; @@ -218,15 +254,23 @@ private SynthesizedReadOnlyListTypeSymbol(SourceModuleSymbol containingModule, s var iCollectionT = compilation.GetSpecialType(SpecialType.System_Collections_Generic_ICollection_T).Construct(typeArgs); var iListT = compilation.GetSpecialType(SpecialType.System_Collections_Generic_IList_T).Construct(typeArgs); - _interfaces = ImmutableArray.Create( - iEnumerable, - iCollection, - iList, - iEnumerableT, - iReadOnlyCollectionT, - iReadOnlyListT, - iCollectionT, - iListT); + _interfaces = hasReadOnlyInterfaces + ? ImmutableArray.Create( + iEnumerable, + iCollection, + iList, + iEnumerableT, + iReadOnlyCollectionT, + iReadOnlyListT, + iCollectionT, + iListT) + : ImmutableArray.Create( + iEnumerable, + iCollection, + iList, + iEnumerableT, + iCollectionT, + iListT); var membersBuilder = ArrayBuilder.GetInstance(); membersBuilder.Add( @@ -314,16 +358,19 @@ private SynthesizedReadOnlyListTypeSymbol(SourceModuleSymbol containingModule, s this, ((MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IEnumerable_T__GetEnumerator)!).AsMember(iEnumerableT), generateGetEnumeratorT)); - addProperty(membersBuilder, - new SynthesizedReadOnlyListProperty( - this, - ((PropertySymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_IReadOnlyCollection_T__Count)!).AsMember(iReadOnlyCollectionT), - generateCount)); - addProperty(membersBuilder, - new SynthesizedReadOnlyListProperty( - this, - ((PropertySymbol)((MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_IReadOnlyList_T__get_Item)!).AssociatedSymbol).AsMember(iReadOnlyListT), - generateIndexer)); + if (hasReadOnlyInterfaces) + { + addProperty(membersBuilder, + new SynthesizedReadOnlyListProperty( + this, + ((PropertySymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_IReadOnlyCollection_T__Count)!).AsMember(iReadOnlyCollectionT), + generateCount)); + addProperty(membersBuilder, + new SynthesizedReadOnlyListProperty( + this, + ((PropertySymbol)((MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_IReadOnlyList_T__get_Item)!).AssociatedSymbol).AsMember(iReadOnlyListT), + generateIndexer)); + } addProperty(membersBuilder, new SynthesizedReadOnlyListProperty( this, diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index cf432dc6ac66e..5d69e61707763 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -9468,11 +9468,11 @@ static void compareMembers(NamedTypeSymbol sourceType, NamedTypeSymbol synthesiz [Theory] [InlineData(SpecialType.System_Collections_IEnumerable, "System.Collections.IEnumerable")] [InlineData(SpecialType.System_Collections_Generic_IEnumerable_T, "System.Collections.Generic.IEnumerable`1")] - [InlineData(SpecialType.System_Collections_Generic_IReadOnlyCollection_T, "System.Collections.Generic.IReadOnlyCollection`1")] - [InlineData(SpecialType.System_Collections_Generic_IReadOnlyList_T, "System.Collections.Generic.IReadOnlyList`1")] + [InlineData(SpecialType.System_Collections_Generic_IReadOnlyCollection_T, "System.Collections.Generic.IReadOnlyCollection`1", true)] + [InlineData(SpecialType.System_Collections_Generic_IReadOnlyList_T, "System.Collections.Generic.IReadOnlyList`1", true)] [InlineData(SpecialType.System_Collections_Generic_ICollection_T, "System.Collections.Generic.ICollection`1")] [InlineData(SpecialType.System_Collections_Generic_IList_T, "System.Collections.Generic.IList`1")] - public void SynthesizedReadOnlyList_MissingSpecialTypes(SpecialType missingType, string missingTypeName) + public void SynthesizedReadOnlyList_MissingSpecialTypes(SpecialType missingType, string missingTypeName, bool isOptional = false) { string source = """ using System.Collections.Generic; @@ -9487,13 +9487,81 @@ static void Main() """; var comp = CreateCompilation(source); comp.MakeTypeMissing(missingType); - comp.VerifyEmitDiagnostics( - // (6,30): error CS0518: Predefined type 'System.Collections.IEnumerable' is not defined or imported - // IEnumerable x = [0]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[0]").WithArguments(missingTypeName).WithLocation(6, 30), - // (7,30): error CS0518: Predefined type 'System.Collections.IEnumerable' is not defined or imported - // IEnumerable y = [..x]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[..x]").WithArguments(missingTypeName).WithLocation(7, 30)); + comp.VerifyEmitDiagnostics(isOptional + ? [] + : [ + // (6,30): error CS0518: Predefined type 'System.Collections.IEnumerable' is not defined or imported + // IEnumerable x = [0]; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[0]").WithArguments(missingTypeName).WithLocation(6, 30), + // (7,30): error CS0518: Predefined type 'System.Collections.IEnumerable' is not defined or imported + // IEnumerable y = [..x]; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[..x]").WithArguments(missingTypeName).WithLocation(7, 30), + ]); + } + + [Theory] + [InlineData(new SpecialType[0])] + [InlineData(new[] { SpecialType.System_Collections_Generic_IReadOnlyCollection_T })] + [InlineData(new[] { SpecialType.System_Collections_Generic_IReadOnlyList_T })] + [InlineData(new[] { SpecialType.System_Collections_Generic_IReadOnlyCollection_T, + SpecialType.System_Collections_Generic_IReadOnlyList_T })] + public void SynthesizedReadOnlyList_MissingOptionalSpecialTypes(SpecialType[] missingTypes) + { + string source = """ + using System.Collections.Generic; + class Program + { + static void Main() + { + IEnumerable x = [0]; + IEnumerable y = [..x]; + } + } + """; + + var comp = CreateCompilation(source); + foreach (var missingType in missingTypes) + { + comp.MakeTypeMissing(missingType); + } + + var verifier = CompileAndVerify( + comp, + symbolValidator: module => + { + verifyInterfaces(module, "<>z__ReadOnlyArray"); + verifyInterfaces(module, "<>z__ReadOnlyList"); + }); + verifier.VerifyDiagnostics(); + + void verifyInterfaces(ModuleSymbol module, string typeName) + { + var synthesizedType = module.GlobalNamespace.GetTypeMember(typeName); + var interfaces = synthesizedType.InterfacesNoUseSiteDiagnostics(); + AssertEx.Equal( + missingTypes is [] + ? new[] + { + "System.Collections.IEnumerable", + "System.Collections.ICollection", + "System.Collections.IList", + "System.Collections.Generic.IEnumerable", + "System.Collections.Generic.IReadOnlyCollection", + "System.Collections.Generic.IReadOnlyList", + "System.Collections.Generic.ICollection", + "System.Collections.Generic.IList", + } + : new[] + { + "System.Collections.IEnumerable", + "System.Collections.ICollection", + "System.Collections.IList", + "System.Collections.Generic.IEnumerable", + "System.Collections.Generic.ICollection", + "System.Collections.Generic.IList", + }, + interfaces.ToTestDisplayStrings()); + } } [Theory]