boolean (@NotNull PyGenericType expected, @Nullable PyType actual, @NotNull MatchContext context) { if (expected.isDefinition() && actual instanceof PyInstantiableType && !((PyInstantiableType<?>)actual).isDefinition()) { return false; } final PyType substitution = context.mySubstitutions.typeVars.get(expected); PyType bound = expected.getBound(); // Promote int in Type[TypeVar('T', int)] to Type[int] before checking that bounds match if (expected.isDefinition()) { final Function<PyType, PyType> toDefinition = t -> t instanceof PyInstantiableType ? ((PyInstantiableType<?>)t).toClass() : t; bound = PyUnionType.union(PyTypeUtil.toStream(bound).map(toDefinition).toList()); } // Remove value-specific components from the actual type to make it safe to propagate PyType safeActual = bound instanceof PyLiteralStringType ? actual : replaceLiteralStringWithStr(actual); Optional<Boolean> match = match(bound, safeActual, context); if (match.isPresent() && !match.get()) { return false; } if (substitution != null) { if (expected.equals(safeActual) || substitution.equals(expected)) { return true; } Optional<Boolean> recursiveMatch = RecursionManager.doPreventingRecursion( expected, false, context.reversedSubstitutions ? () -> match(safeActual, substitution, context) : () -> match(substitution, safeActual, context) ); return recursiveMatch != null ? recursiveMatch.orElse(false) : false; } if (safeActual != null) { context.mySubstitutions.typeVars.put(expected, safeActual); } else if (bound != null) { context.mySubstitutions.typeVars.put(expected, PyUnionType.createWeakType(bound)); } return true; }