-
Notifications
You must be signed in to change notification settings - Fork 8.2k
WIP: Allow self-referential generic type parameters in interface list #12863
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -265,6 +265,42 @@ internal static void DefineCustomAttributes(EnumBuilder member, ReadOnlyCollecti | |
| } | ||
|
||
| } | ||
|
|
||
| private class InterfaceExpression | ||
| { | ||
| private TypeConstraintAst ast; | ||
|
|
||
| internal bool IsGeneric => ast?.TypeName.IsGeneric ?? false; | ||
|
|
||
| internal InterfaceExpression(TypeConstraintAst ast) | ||
| { | ||
| this.ast = ast; | ||
| } | ||
|
|
||
| internal Type ResolveConcreteInterfaceType(TypeBuilder parameter) | ||
| => IsGeneric ? ResolveConcreteInterfaceTypeArguments(ast.TypeName, parameter) : ast.TypeName.GetReflectionType(); | ||
|
|
||
| private Type ResolveConcreteInterfaceTypeArguments(ITypeName typeName, TypeBuilder parameter) | ||
| { | ||
| var typeArgs = new List<Type>(); | ||
| if (typeName.IsGeneric && typeName is GenericTypeName genericName) | ||
| { | ||
| foreach (var typeArg in genericName.GenericArguments) | ||
| { | ||
| typeArgs.Add(ResolveConcreteInterfaceTypeArguments(typeArg, parameter)); | ||
| } | ||
|
|
||
| return genericName.TypeName.GetReflectionType().MakeGenericType(typeArgs.ToArray()); | ||
| } | ||
|
|
||
| if (parameter.FullName == typeName.FullName) | ||
| { | ||
| return parameter; | ||
| } | ||
|
|
||
| return typeName.GetReflectionType(); | ||
| } | ||
| } | ||
|
|
||
| private class DefineTypeHelper | ||
| { | ||
| private readonly Parser _parser; | ||
|
|
@@ -276,7 +312,7 @@ private class DefineTypeHelper | |
| internal readonly TypeBuilder _staticHelpersTypeBuilder; | ||
| private readonly Dictionary<string, PropertyMemberAst> _definedProperties; | ||
| private readonly Dictionary<string, List<Tuple<FunctionMemberAst, Type[]>>> _definedMethods; | ||
| private HashSet<Tuple<string, Type>> _interfaceProperties; | ||
| private Dictionary<string, Type> _interfaceProperties; | ||
| internal readonly List<(string fieldName, IParameterMetadataProvider bodyAst, bool isStatic)> _fieldsToInitForMemberFunctions; | ||
| private bool _baseClassHasDefaultCtor; | ||
|
|
||
|
|
@@ -292,10 +328,15 @@ public DefineTypeHelper(Parser parser, ModuleBuilder module, TypeDefinitionAst t | |
| _parser = parser; | ||
| _typeDefinitionAst = typeDefinitionAst; | ||
|
|
||
| List<Type> interfaces; | ||
| List<InterfaceExpression> interfaces; | ||
| var baseClass = this.GetBaseTypes(parser, typeDefinitionAst, out interfaces); | ||
|
|
||
| _typeBuilder = module.DefineType(typeName, Reflection.TypeAttributes.Class | Reflection.TypeAttributes.Public, baseClass, interfaces.ToArray()); | ||
| _typeBuilder = module.DefineType(typeName, Reflection.TypeAttributes.Class | Reflection.TypeAttributes.Public, baseClass, null); | ||
| foreach (var interfaceExpression in interfaces) | ||
| { | ||
| _typeBuilder.AddInterfaceImplementation(interfaceExpression.ResolveConcreteInterfaceType(_typeBuilder)); | ||
| } | ||
|
|
||
| _staticHelpersTypeBuilder = module.DefineType(string.Format(CultureInfo.InvariantCulture, "{0}_<staticHelpers>", typeName), Reflection.TypeAttributes.Class); | ||
| DefineCustomAttributes(_typeBuilder, typeDefinitionAst.Attributes, _parser, AttributeTargets.Class); | ||
| _typeDefinitionAst.Type = _typeBuilder; | ||
|
|
@@ -314,12 +355,31 @@ public DefineTypeHelper(Parser parser, ModuleBuilder module, TypeDefinitionAst t | |
| /// <param name="parser"></param> | ||
| /// <param name="typeDefinitionAst"></param> | ||
| /// <param name="interfaces">Return declared interfaces.</param> | ||
| /// <returns></returns> | ||
| private Type GetBaseTypes(Parser parser, TypeDefinitionAst typeDefinitionAst, out List<Type> interfaces) | ||
| /// <returns>The base type</returns> | ||
| private Type GetBaseTypes(Parser parser, TypeDefinitionAst typeDefinitionAst, out List<InterfaceExpression> interfaces) | ||
| { | ||
| // Define base types and report errors. | ||
| Type baseClass = null; | ||
| interfaces = new List<Type>(); | ||
| interfaces = new List<InterfaceExpression>(); | ||
|
|
||
| bool TryGetInterface(TypeConstraintAst ast, out InterfaceExpression interfaceExpression) | ||
| { | ||
| interfaceExpression = new InterfaceExpression(ast); | ||
| if (ast.TypeName.IsGeneric && ast.TypeName is GenericTypeName genericTypeName) | ||
| { | ||
| if (genericTypeName.TypeName.GetReflectionType().IsInterface) | ||
| { | ||
| return true; | ||
| } | ||
| } | ||
| else if (ast.TypeName.GetReflectionType()?.IsInterface ?? false) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| interfaceExpression = null; | ||
| return false; | ||
| } | ||
|
|
||
| // Default base class is System.Object and it has a default ctor. | ||
| _baseClassHasDefaultCtor = true; | ||
|
|
@@ -339,40 +399,43 @@ private Type GetBaseTypes(Parser parser, TypeDefinitionAst typeDefinitionAst, ou | |
| } | ||
| else | ||
| { | ||
| baseClass = firstBaseTypeAst.TypeName.GetReflectionType(); | ||
| if (baseClass == null) | ||
| if (TryGetInterface(firstBaseTypeAst, out InterfaceExpression interfaceExpression)) | ||
| { | ||
| parser.ReportError(firstBaseTypeAst.Extent, | ||
| nameof(ParserStrings.TypeNotFound), | ||
| ParserStrings.TypeNotFound, | ||
| firstBaseTypeAst.TypeName.FullName); | ||
| // fall to the default base type | ||
| // First Ast can represent interface as well as BaseClass. | ||
| interfaces.Add(interfaceExpression); | ||
| baseClass = null; | ||
| } | ||
| else | ||
| { | ||
| if (baseClass.IsSealed) | ||
| baseClass = firstBaseTypeAst.TypeName.GetReflectionType(); | ||
| if (baseClass == null) | ||
| { | ||
| parser.ReportError(firstBaseTypeAst.Extent, | ||
| nameof(ParserStrings.SealedBaseClass), | ||
| ParserStrings.SealedBaseClass, | ||
| baseClass.Name); | ||
| // ignore base type if it's sealed. | ||
| baseClass = null; | ||
| } | ||
| else if (baseClass.IsGenericType && !baseClass.IsConstructedGenericType) | ||
| { | ||
| parser.ReportError(firstBaseTypeAst.Extent, | ||
| nameof(ParserStrings.SubtypeUnclosedGeneric), | ||
| ParserStrings.SubtypeUnclosedGeneric, | ||
| baseClass.Name); | ||
| // ignore base type, we cannot inherit from unclosed generic. | ||
| baseClass = null; | ||
| nameof(ParserStrings.TypeNotFound), | ||
| ParserStrings.TypeNotFound, | ||
| firstBaseTypeAst.TypeName.FullName); | ||
| // fall to the default base type | ||
| } | ||
| else if (baseClass.IsInterface) | ||
| else | ||
| { | ||
| // First Ast can represent interface as well as BaseClass. | ||
| interfaces.Add(baseClass); | ||
| baseClass = null; | ||
| if (baseClass.IsSealed) | ||
| { | ||
| parser.ReportError(firstBaseTypeAst.Extent, | ||
| nameof(ParserStrings.SealedBaseClass), | ||
| ParserStrings.SealedBaseClass, | ||
| baseClass.Name); | ||
| // ignore base type if it's sealed. | ||
| baseClass = null; | ||
| } | ||
| else if (baseClass.IsGenericType && !baseClass.IsConstructedGenericType) | ||
| { | ||
| parser.ReportError(firstBaseTypeAst.Extent, | ||
| nameof(ParserStrings.SubtypeUnclosedGeneric), | ||
| ParserStrings.SubtypeUnclosedGeneric, | ||
| baseClass.Name); | ||
| // ignore base type, we cannot inherit from unclosed generic. | ||
| baseClass = null; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -416,26 +479,16 @@ private Type GetBaseTypes(Parser parser, TypeDefinitionAst typeDefinitionAst, ou | |
| else | ||
| { | ||
| Type interfaceType = baseTypeAsts[i].TypeName.GetReflectionType(); | ||
| if (interfaceType == null) | ||
| if (!TryGetInterface(baseTypeAsts[i], out InterfaceExpression interfaceExpression)) | ||
| { | ||
| parser.ReportError(baseTypeAsts[i].Extent, | ||
| nameof(ParserStrings.TypeNotFound), | ||
| ParserStrings.TypeNotFound, | ||
| baseTypeAsts[i].TypeName.FullName); | ||
| nameof(ParserStrings.InterfaceNameExpected), | ||
| ParserStrings.InterfaceNameExpected, | ||
| interfaceType.Name); | ||
| } | ||
| else | ||
| { | ||
| if (interfaceType.IsInterface) | ||
| { | ||
| interfaces.Add(interfaceType); | ||
| } | ||
| else | ||
| { | ||
| parser.ReportError(baseTypeAsts[i].Extent, | ||
| nameof(ParserStrings.InterfaceNameExpected), | ||
| ParserStrings.InterfaceNameExpected, | ||
| interfaceType.Name); | ||
| } | ||
| interfaces.Add(interfaceExpression); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -448,31 +501,37 @@ private bool ShouldImplementProperty(string name, Type type) | |
| { | ||
| if (_interfaceProperties == null) | ||
| { | ||
| _interfaceProperties = new HashSet<Tuple<string, Type>>(); | ||
| _interfaceProperties = new Dictionary<string, Type>(); | ||
| var allInterfaces = new HashSet<Type>(); | ||
|
|
||
| // TypeBuilder.GetInterfaces() returns only the interfaces that was explicitly passed to its constructor. | ||
| // During compilation the interface hierarchy is flattened, so we only need to resolve one level of ancestral interfaces. | ||
| foreach (var interfaceType in _typeBuilder.GetInterfaces()) | ||
| { | ||
| foreach (var parentInterface in interfaceType.GetInterfaces()) | ||
| var typeDefinition = interfaceType; | ||
| if (interfaceType.IsGenericType && interfaceType.GenericTypeArguments.Contains(_typeBuilder)) | ||
| { | ||
| typeDefinition = interfaceType.GetGenericTypeDefinition(); | ||
| } | ||
|
|
||
| foreach (var parentInterface in typeDefinition.GetInterfaces()) | ||
| { | ||
| allInterfaces.Add(parentInterface); | ||
| } | ||
|
|
||
| allInterfaces.Add(interfaceType); | ||
| allInterfaces.Add(typeDefinition); | ||
| } | ||
|
|
||
| foreach (var interfaceType in allInterfaces) | ||
| { | ||
| foreach (var property in interfaceType.GetProperties()) | ||
| { | ||
| _interfaceProperties.Add(Tuple.Create(property.Name, property.PropertyType)); | ||
| _interfaceProperties.Add(property.Name, property.PropertyType); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return _interfaceProperties.Contains(Tuple.Create(name, type)); | ||
| return _interfaceProperties.TryGetValue(name, out Type returnType) && (returnType == type || returnType.IsGenericParameter); | ||
| } | ||
|
|
||
| public void DefineMembers() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@IISResetMe, your last commit had 4 failures in
PowerShell-CI-linuxError occurred in test script '/home/vsts/work/1/s/test/powershell/Language/Classes/scripting.Classes.inheritance.tests.ps1'
Error occurred in test script '/home/vsts/work/1/s/test/powershell/Language/Classes/scripting.Classes.inheritance.tests.ps1'
Error occurred in test script '/home/vsts/work/1/s/test/powershell/Language/Classes/scripting.Classes.inheritance.tests.ps1'
Error occurred in test script '/home/vsts/work/1/s/test/powershell/Language/Classes/scripting.Classes.inheritance.tests.ps1'