@SuppressWarnings("rawtypes")
  public TypeModel build(@Nullable PyType type,
                         boolean allowUnions) {
    final TypeModel evaluated = myVisited.get(type);
    if (evaluated != null) {
      return evaluated;
    }
    if (myVisited.containsKey(type)) { //already evaluating?
      return NamedType.nameOrAny(type);
    }
    myVisited.put(type, null); //mark as evaluating

    TypeModel result = null;
    if (type instanceof PyTypedDictType typedDictType) {
      if (typedDictType.isInferred()) {
        return build(new PyCollectionTypeImpl(typedDictType.getPyClass(), false,
                                              typedDictType.getElementTypes()), allowUnions);
      }
      else {
        result = NamedType.nameOrAny(type);
      }
    }
    else if (type instanceof PyInstantiableType && ((PyInstantiableType<?>)type).isDefinition()) {
      final PyInstantiableType instanceType = ((PyInstantiableType<?>)type).toInstance();
      // Special case: render Type[type] as just type
      if (type instanceof PyClassType && instanceType.equals(PyBuiltinCache.getInstance(((PyClassType)type).getPyClass()).getTypeType())) {
        result = NamedType.nameOrAny(type);
      }
      else {
        result = new ClassObjectType(build(instanceType, allowUnions));
      }
    }
    else if (type instanceof PyNamedTupleType) {
      result = NamedType.nameOrAny(type);
    }
    else if (type instanceof PyTupleType tupleType) {

      final List<PyType> elementTypes = tupleType.isHomogeneous()
                                        ? Collections.singletonList(tupleType.getIteratedItemType())
                                        : tupleType.getElementTypes();

      boolean useTypingAlias = PyiUtil.getOriginalLanguageLevel(tupleType.getPyClass()).isOlderThan(LanguageLevel.PYTHON39);
      final List<TypeModel> elementModels = ContainerUtil.map(elementTypes, elementType -> build(elementType, true));
      result = new TupleType(elementModels, tupleType.isHomogeneous(), useTypingAlias);
    }
    else if (type instanceof PyCollectionType asCollection) {
      final List<TypeModel> elementModels = new ArrayList<>();
      for (PyType elementType : asCollection.getElementTypes()) {
        elementModels.add(build(elementType, true));
      }
      if (!elementModels.isEmpty()) {
        final TypeModel collectionType = build(new PyClassTypeImpl(asCollection.getPyClass(), asCollection.isDefinition()), false);
        boolean useTypingAlias = PyiUtil.getOriginalLanguageLevel(asCollection.getPyClass()).isOlderThan(LanguageLevel.PYTHON39);
        result = new CollectionOf(collectionType, elementModels, useTypingAlias);
      }
    }
    else if (type instanceof PyUnionType unionType && allowUnions) {
      final Collection<PyType> unionMembers = unionType.getMembers();
      final Pair<List<PyLiteralType>, List<PyType>> literalsAndOthers = extractLiterals(unionType);
      final Ref<PyType> optionalType = getOptionalType(unionType);
      if (literalsAndOthers != null) {
        final OneOfLiterals oneOfLiterals = new OneOfLiterals(literalsAndOthers.first);

        if (!literalsAndOthers.second.isEmpty()) {
          final List<TypeModel> otherTypeModels = ContainerUtil.map(literalsAndOthers.second, t -> build(t, false));
          result = new OneOf(ContainerUtil.prepend(otherTypeModels, oneOfLiterals),
                             PyTypingTypeProvider.isBitwiseOrUnionAvailable(myContext));
        }
        else {
          result = oneOfLiterals;
        }
      }
      else if (optionalType != null) {
        result = new OptionalType(build(optionalType.get(), true), PyTypingTypeProvider.isBitwiseOrUnionAvailable(myContext));
      }
      else if (type instanceof PyDynamicallyEvaluatedType || PyTypeChecker.isUnknown(type, false, myContext)) {
        result = new UnknownType(build(unionType.excludeNull(), true), PyTypingTypeProvider.isBitwiseOrUnionAvailable(myContext));
      }
      else if (ContainerUtil.all(unionMembers, t -> t instanceof PyClassType && ((PyClassType)t).isDefinition())) {
        final List<TypeModel> instanceTypes = ContainerUtil.map(unionMembers, t -> build(((PyClassType)t).toInstance(), allowUnions));
        result = new ClassObjectType(new OneOf(instanceTypes, PyTypingTypeProvider.isBitwiseOrUnionAvailable(myContext)));
      }
      else {
        result = new OneOf(Collections2.transform(unionMembers, t -> build(t, false)),
                           PyTypingTypeProvider.isBitwiseOrUnionAvailable(myContext));
      }
    }
    else if (type instanceof PyCallableType && !(type instanceof PyClassLikeType)) {
      result = buildCallable((PyCallableType)type);
    }
    else if (type instanceof PyTypeVarType typeVarType) {
      result = new TypeVarType(type.getName(), typeVarType.getBound() != null ? build(typeVarType.getBound(), true) : null);
    }
    if (result == null) {
      result = NamedType.nameOrAny(type);
    }
    myVisited.put(type, result);
    return result;
  }
@SuppressWarnings("rawtypes")
  public TypeModel build(@Nullable PyType type,
                         boolean allowUnions)