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);
            }
        }