private IMember CreateSpecificReturnFromClassType(IPythonClassType selfClassType, PythonClassType returnClassType, IArgumentSet args) { // -> A[_T1, _T2, ...] // Match arguments IReadOnlyList <IPythonType> typeArgs = null; var classGenericParameters = selfClassType?.GenericParameters.Keys.ToArray() ?? Array.Empty <string>(); if (classGenericParameters.Length > 0 && selfClassType != null) { // Declaring class is specific and provides definitions of generic parameters typeArgs = classGenericParameters .Select(n => selfClassType.GenericParameters.TryGetValue(n, out var t) ? t : null) .ExcludeDefault() .ToArray(); } else if (args != null) { typeArgs = ExpressionEval.GetTypeArgumentsFromParameters(this, args); } if (typeArgs != null) { var specificReturnValue = returnClassType.CreateSpecificType(new ArgumentSet(typeArgs)); return(new PythonInstance(specificReturnValue)); } return(null); }
/// <summary> /// Given an argument set with types, e.g int, float, str, look at the generic type parameters for the class /// and create a new class type with those parameters filled in. Additionally, transmit the specific class /// types to bases and recreate them with their specific types. /// /// e.g /// class A(Generic[T, U]): ... /// class B(A[T, str]): ... /// /// B[int] defines the type parameter T to be of type int and type parameter U to be type str. /// B[int] inherits from A[int, str] /// </summary> public virtual IPythonType CreateSpecificType(IArgumentSet args) { lock (_membersLock) { var newGenericTypeParameters = GetTypeParameters(); var newBases = new List <IPythonType>(); // Get map of generic type parameter to specific type - fill in what type goes to what // type parameter T -> int, U -> str, etc. var genericTypeToSpecificType = GetSpecificTypes(args, newGenericTypeParameters, newBases); var classType = new PythonClassType(BaseName, new Location(DeclaringModule)); classType.SetDocumentation(Documentation); // Storing generic parameters allows methods returning generic types // to know what type parameter returns what specific type classType.StoreGenericParameters(this, newGenericTypeParameters.Select(p => p.Name).ToArray(), genericTypeToSpecificType); // Set generic name classType.SetNames(); // Locking so threads can only access class after it's been initialized // Store generic parameters first so name updates correctly, then check if class type has been cached _specificTypeCache = _specificTypeCache ?? new Dictionary <string, PythonClassType>(); if (_specificTypeCache.TryGetValue(classType.Name, out var cachedType)) { return(cachedType); } _specificTypeCache[classType.Name] = classType; // Prevent re-entrancy when resolving generic class where method may be returning instance of type of the same class. // e.g // class C(Generic[T]): // def tmp(self): // return C(5) // C(5).tmp() // We try to resolve generic type when instantiating 'C' but end up resolving it again on 'tmp' method call, looping infinitely using (_genericSpecializationGuard.Push(classType, out var reentered)) { if (reentered) { return(classType); } // Bases can be null when not set var bases = Bases ?? Array.Empty <IPythonType>(); // Get declared generic class parameters, i.e. Generic[T1, T2, ...], Optional[Generic[T1, ...]] var genericClassParameters = bases.OfType <IGenericClassBase>().ToArray(); // Get list of bases that are generic but not generic class parameters, e.g A[T], B[T] but not Generic[T1, T2] var genericTypeBases = bases.Except(genericClassParameters).OfType <IGenericType>().Where(g => g.IsGeneric).ToArray(); // Removing all generic bases, and will only specialize genericTypeBases, remove generic class parameters entirely // We remove generic class parameters entirely because the type information is now stored in GenericParameters field // We still need generic bases so we can specialize them var specificBases = bases.Except(genericTypeBases).Except(genericClassParameters).ToList(); // Create specific types for generic type bases foreach (var gt in genericTypeBases) { // Look through generic type bases and see if any of their required type parameters // have received a specific type, and if so create specific type var st = gt.Parameters .Select(p => classType.GenericParameters.TryGetValue(p.Name, out var t) ? t : null) .Where(p => !p.IsUnknown()) .ToArray(); if (st.Length > 0) { var type = gt.CreateSpecificType(new ArgumentSet(st, args.Expression, args.Eval)); if (!type.IsUnknown()) { specificBases.Add(type); } } } // Set specific class bases classType.SetBases(specificBases.Concat(newBases), args.Eval?.CurrentScope); // Now that parameters are set, check if class is generic classType.SetGenericParameters(); // Transfer members from generic to specific type. classType.SetClassMembers(this, args); } return(classType); } }