private boolean doReduce(InferenceSession session, Set<? super InferenceVariable> dependencies, List<? super ConstraintFormula> constraints) {
    if (!session.collectDependencies(myS, dependencies) && !session.collectDependencies(myT, dependencies)) {
      if (myT == null) return myS == null || myS.equalsToText(CommonClassNames.JAVA_LANG_OBJECT);
      if (myS == null) return true;
      return TypeConversionUtil.isAssignable(myT, myS);
    }

    if (PsiTypes.nullType().equals(myT) || myT == null) return false;
    if (PsiTypes.nullType().equals(myS) || myS == null || myT.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) return true;

    if (PsiTypes.voidType().equals(myS) ^ PsiTypes.voidType().equals(myT)) return false;

    InferenceVariable inferenceVariable = session.getInferenceVariable(myS);
    if (inferenceVariable != null) {
      InferenceVariable.addBound(myS, myT, InferenceBound.UPPER, session);
      return true;
    }
    inferenceVariable = session.getInferenceVariable(myT);
    if (inferenceVariable != null) {
      InferenceVariable.addBound(myT, myS, InferenceBound.LOWER, session);
      return true;
    }
    if (myT instanceof PsiArrayType) {
      PsiType sType = myS;
      if (myS instanceof PsiCapturedWildcardType) {
        final PsiType upperBound = ((PsiCapturedWildcardType)myS).getUpperBound();
        if (upperBound instanceof PsiArrayType) {
          sType = upperBound;
        }
      }
      if (!(sType instanceof PsiArrayType)) return false; //todo most specific array supertype
      final PsiType tComponentType = ((PsiArrayType)myT).getComponentType();
      final PsiType sComponentType = ((PsiArrayType)sType).getComponentType();
      if (!(tComponentType instanceof PsiPrimitiveType) && !(sComponentType instanceof PsiPrimitiveType)) {
        constraints.add(new StrictSubtypingConstraint(tComponentType, sComponentType, myCapture));
        return true;
      }
      return sComponentType instanceof PsiPrimitiveType && sComponentType.equals(tComponentType);
    }
    if (myT instanceof PsiClassType) {
      final PsiClassType.ClassResolveResult TResult = ((PsiClassType)myT).resolveGenerics();
      final PsiClass CClass = TResult.getElement();
      if (CClass != null) {
        if (CClass instanceof PsiTypeParameter) {
          if (myS instanceof PsiIntersectionType) {
            if (ArrayUtil.contains(myT, ((PsiIntersectionType)myS).getConjuncts())) {
              return true;
            }
          }
          final PsiType lowerBound = TypeConversionUtil.getInferredLowerBoundForSynthetic((PsiTypeParameter)CClass);
          if (lowerBound != null) {
            constraints.add(new StrictSubtypingConstraint(lowerBound, myS, myCapture));
            return true;
          }
          return false;
        }

        if (myS instanceof PsiArrayType) {
          return myT.isAssignableFrom(myS);
        }

        PsiClassType sType = getSubclassType(CClass, myS, myCapture);

        if (sType == null) return false;
        final PsiClassType.ClassResolveResult SResult = sType.resolveGenerics();
        PsiClass SClass = SResult.getElement();

        if (SClass == null) return false;

        if (((PsiClassType)myT).isRaw() || myCapture && sType.isRaw()) {
          return InheritanceUtil.isInheritorOrSelf(SClass, CClass, true);
        }

        PsiSubstitutor substitutor = SResult.getSubstitutor();
        Map<PsiTypeParameter, PsiType> map = new HashMap<>(); 
        for (PsiTypeParameter typeParameter : SClass.getTypeParameters()) {
          map.put(typeParameter, substitutor.substituteWithBoundsPromotion(typeParameter));
        }
        substitutor = substitutor.putAll(map);

        final PsiSubstitutor tSubstitutor = TResult.getSubstitutor();
        final PsiSubstitutor sSubstitutor = TypeConversionUtil.getClassSubstitutor(CClass, SClass, substitutor);
        if (sSubstitutor != null) {
          for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(CClass)) {
            final PsiType tSubstituted = tSubstitutor.substitute(parameter);
            final PsiType sSubstituted = sSubstitutor.substitute(parameter);
            if (tSubstituted == null ^ sSubstituted == null) {
              return false;
            }
            constraints.add(new SubtypingConstraint(tSubstituted, sSubstituted));
          }
          return true;
        }
      }
      return false;
    }

    if (myT instanceof PsiIntersectionType) {
      for (PsiType conjunct : ((PsiIntersectionType)myT).getConjuncts()) {
        constraints.add(new StrictSubtypingConstraint(conjunct, myS, myCapture));
      }
      return true;
    }

    if (myT instanceof PsiCapturedWildcardType) {
      PsiType lowerBound = ((PsiCapturedWildcardType)myT).getLowerBound();
      if (lowerBound != PsiTypes.nullType()) {
        constraints.add(new StrictSubtypingConstraint(lowerBound, myS, myCapture));
      }
    }

    return true;
  }
private boolean doReduce(InferenceSession session, Set<? super InferenceVariable> dependencies, List<? super ConstraintFormula> constraints)