Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SynthesizedReadOnlyListTypeSymbol.Create - Check for MissingMetadataTypeSymbol #71330

Merged
merged 4 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ internal static NamedTypeSymbol Create(SourceModuleSymbol containingModule, stri
DiagnosticInfo? diagnosticInfo = null;

var hasReadOnlyInterfaces =
!compilation.IsTypeMissing(SpecialType.System_Collections_Generic_IReadOnlyCollection_T)
&& !compilation.IsTypeMissing(SpecialType.System_Collections_Generic_IReadOnlyList_T);
compilation.GetSpecialType(SpecialType.System_Collections_Generic_IReadOnlyCollection_T) is not MissingMetadataTypeSymbol &&
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compilation.GetSpecialType(SpecialType.System_Collections_Generic_IReadOnlyCollection_T) is not MissingMetadataTypeSymbol &&

It looks like we are fixing a real bug, but not adding any unit-tests. I think we should add a unit-test that makes the problem observable. I.e. a test that fails without the fix. #Closed

compilation.GetSpecialType(SpecialType.System_Collections_Generic_IReadOnlyList_T) is not MissingMetadataTypeSymbol;

foreach (var type in s_requiredSpecialTypes)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9893,7 +9893,7 @@ static void Main()
[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)
public void SynthesizedReadOnlyList_MissingOptionalSpecialTypes_01(SpecialType[] missingTypes)
{
string source = """
using System.Collections.Generic;
Expand Down Expand Up @@ -9952,6 +9952,269 @@ missingTypes is []
}
}

[ConditionalFact(typeof(CoreClrOnly))]
public void SynthesizedReadOnlyList_MissingOptionalSpecialTypes_02()
{
string runtime = @"
namespace System
{
using System.Collections;
public class NotSupportedException : Exception {}
public class Array : ICollection, IList
{
public static T[] Empty<T>() => throw null;
IEnumerator IEnumerable.GetEnumerator() => throw null;
void ICollection.CopyTo(Array array, int index) => throw null;
int ICollection.Count => throw null;
object ICollection.SyncRoot => throw null;
bool ICollection.IsSynchronized => throw null;
object IList.this[int index]
{
get => throw null;
set => throw null;
}
int IList.Add(object value) => throw null;
bool IList.Contains(object value) => throw null;
void IList.Clear() => throw null;
bool IList.IsReadOnly => throw null;
bool IList.IsFixedSize => throw null;
int IList.IndexOf(object value) => throw null;
void IList.Insert(int index, object value)=> throw null;
void IList.Remove(object value) => throw null;
void IList.RemoveAt(int index)=> throw null;
}
public class Attribute { }
[Flags]
public enum AttributeTargets
{
Assembly = 0x1,
Module = 0x2,
Class = 0x4,
Struct = 0x8,
Enum = 0x10,
Constructor = 0x20,
Method = 0x40,
Property = 0x80,
Field = 0x100,
Event = 0x200,
Interface = 0x400,
Parameter = 0x800,
Delegate = 0x1000,
ReturnValue = 0x2000,
GenericParameter = 0x4000,
All = 0x7FFF
}
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public sealed class AttributeUsageAttribute : Attribute
{
public AttributeUsageAttribute(AttributeTargets validOn) { }
public bool AllowMultiple
{
get => throw null;
set { }
}
public bool Inherited
{
get => throw null;
set { }
}
public AttributeTargets ValidOn => throw null;
}
public struct Boolean { }
public struct Byte { }
public class Delegate
{
public static Delegate CreateDelegate(Type type, object firstArgument, Reflection.MethodInfo method) => null;
}
public abstract class Enum : IComparable { }
public class Exception { }
public class FlagsAttribute : Attribute { }
public delegate T Func<out T>();
public delegate U Func<in T, out U>(T arg);
public interface IComparable { }
public interface IDisposable
{
void Dispose();
}
public struct Int16 { }
public struct Int32 { }
public struct Int64 { }
public struct IntPtr { }
public class MulticastDelegate : Delegate { }
public struct Nullable<T> { }
public class Object { }
public sealed class ParamArrayAttribute : Attribute { }
public struct RuntimeMethodHandle { }
public struct RuntimeTypeHandle { }
public class String : IComparable { public static String Empty = null; }
public class Type
{
public static Type GetTypeFromHandle(RuntimeTypeHandle handle) => null;
}
public class ValueType { }
public struct Void { }
namespace Collections
{
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
public interface ICollection : IEnumerable
{
void CopyTo(Array array, int index);
int Count { get; }
object SyncRoot { get; }
bool IsSynchronized { get; }
}
public interface IList : ICollection
{
object this[int index] { get; set; }
int Add(object value);
bool Contains(object value);
void Clear();
bool IsReadOnly { get; }
bool IsFixedSize { get; }
int IndexOf(object value);
void Insert(int index, object value);
void Remove(object value);
void RemoveAt(int index);
}
}
namespace Collections.Generic
{
public interface IEnumerable<out T> : IEnumerable
{
new IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<out T> : IEnumerator, IDisposable
{
new T Current { get; }
}
public interface ICollection<T> : IEnumerable<T>
{
int Count { get; }
bool IsReadOnly { get; }
void Add(T item);
void Clear();
bool Contains(T item);
void CopyTo(T[] array, int arrayIndex);
bool Remove(T item);
}
public interface IList<T> : ICollection<T>
{
T this[int index] { get; set; }
int IndexOf(T item);
void Insert(int index, T item);
void RemoveAt(int index);
}
public class List<T> : System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IList<T>, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList
{
public List() { }
public List(System.Collections.Generic.IEnumerable<T> collection) { }
public List(int capacity) { }
public int Capacity { get { throw null; } set { } }
public int Count { get { throw null; } }
public T this[int index] { get { throw null; } set { } }
bool System.Collections.Generic.ICollection<T>.IsReadOnly { get { throw null; } }
bool System.Collections.ICollection.IsSynchronized { get { throw null; } }
object System.Collections.ICollection.SyncRoot { get { throw null; } }
bool System.Collections.IList.IsFixedSize { get { throw null; } }
bool System.Collections.IList.IsReadOnly { get { throw null; } }
object System.Collections.IList.this[int index] { get { throw null; } set { } }
public void Add(T item) { }
public void Clear() { }
public bool Contains(T item) { throw null; }
public void CopyTo(int index, T[] array, int arrayIndex, int count) { }
public void CopyTo(T[] array) { }
public void CopyTo(T[] array, int arrayIndex) { }
public int EnsureCapacity(int capacity) { throw null; }
public int IndexOf(T item) { throw null; }
public int IndexOf(T item, int index) { throw null; }
public int IndexOf(T item, int index, int count) { throw null; }
public void Insert(int index, T item) { }
public bool Remove(T item) { throw null; }
public void RemoveAt(int index) { }
System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator() { throw null; }
void System.Collections.ICollection.CopyTo(System.Array array, int arrayIndex) { }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
int System.Collections.IList.Add(object item) { throw null; }
bool System.Collections.IList.Contains(object item) { throw null; }
int System.Collections.IList.IndexOf(object item) { throw null; }
void System.Collections.IList.Insert(int index, object item) { }
void System.Collections.IList.Remove(object item) { }
public T[] ToArray() { throw null; }
}
}
namespace Reflection
{
public class AssemblyVersionAttribute : Attribute
{
public AssemblyVersionAttribute(string version) { }
}
public class DefaultMemberAttribute : Attribute
{
public DefaultMemberAttribute(string name) { }
}
public abstract class MemberInfo { }
public abstract class MethodBase : MemberInfo
{
public static MethodBase GetMethodFromHandle(RuntimeMethodHandle handle) => throw null;
}
public abstract class MethodInfo : MethodBase
{
public virtual Delegate CreateDelegate(Type delegateType, object target) => throw null;
}
}
}
";

string source = """
using System.Collections.Generic;
class Program
{
static void Main()
{
IEnumerable<int> x = [0, 1];
IEnumerable<int> z = [..x];
}
}
""";
var reference = CreateEmptyCompilation(runtime, assemblyName: "System.Runtime").VerifyDiagnostics().EmitToImageReference();
var comp = CreateEmptyCompilation(source, references: [reference]);

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(
new[]
{
"System.Collections.IEnumerable",
"System.Collections.ICollection",
"System.Collections.IList",
"System.Collections.Generic.IEnumerable<T>",
"System.Collections.Generic.ICollection<T>",
"System.Collections.Generic.IList<T>",
},
interfaces.ToTestDisplayStrings());
}
}

[Theory]
[InlineData((int)SpecialMember.System_Collections_IEnumerable__GetEnumerator, "System.Collections.IEnumerable", "GetEnumerator")]
[InlineData((int)SpecialMember.System_Collections_Generic_IEnumerable_T__GetEnumerator, "System.Collections.Generic.IEnumerable`1", "GetEnumerator")]
Expand Down