@NotNull
  private static PsiSubstitutor replaceVariables(Collection<InferenceVariable> inferenceVariables) {
    final List<InferenceVariable> targetVars = new ArrayList<InferenceVariable>();
    PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
    final InferenceVariable[] oldVars =
        inferenceVariables.toArray(new InferenceVariable[inferenceVariables.size()]);
    for (InferenceVariable variable : oldVars) {
      final InferenceVariable newVariable =
          new InferenceVariable(
              variable.getCallContext(), variable.getParameter(), variable.getName());
      substitutor =
          substitutor.put(
              variable,
              JavaPsiFacade.getElementFactory(variable.getProject()).createType(newVariable));
      targetVars.add(newVariable);
      if (variable.isThrownBound()) {
        newVariable.setThrownBound();
      }
    }

    for (int i = 0; i < targetVars.size(); i++) {
      InferenceVariable var = targetVars.get(i);
      for (InferenceBound boundType : InferenceBound.values()) {
        for (PsiType bound : oldVars[i].getBounds(boundType)) {
          var.addBound(substitutor.substitute(bound), boundType, null);
        }
      }
    }
    return substitutor;
  }
 public void collectCaptureDependencies(
     InferenceVariable variable, Set<InferenceVariable> dependencies) {
   final PsiTypeParameter parameter = variable.getParameter();
   for (Pair<PsiTypeParameter[], PsiClassType> capture : myCaptures) {
     for (PsiTypeParameter typeParameter : capture.first) {
       if (typeParameter == parameter) {
         collectAllVariablesOnBothSides(dependencies, capture);
         break;
       }
     }
   }
 }
 public boolean hasCaptureConstraints(Iterable<InferenceVariable> variables) {
   for (InferenceVariable variable : variables) {
     final PsiTypeParameter parameter = variable.getParameter();
     for (Pair<PsiTypeParameter[], PsiClassType> capture : myCaptures) {
       for (PsiTypeParameter typeParameter : capture.first) {
         if (parameter == typeParameter) {
           return true;
         }
       }
     }
   }
   return false;
 }
 public void forgetCaptures(List<InferenceVariable> variables) {
   for (InferenceVariable variable : variables) {
     final PsiTypeParameter parameter = variable.getParameter();
     for (Iterator<Pair<PsiTypeParameter[], PsiClassType>> iterator = myCaptures.iterator();
         iterator.hasNext(); ) {
       Pair<PsiTypeParameter[], PsiClassType> capture = iterator.next();
       for (PsiTypeParameter typeParameter : capture.first) {
         if (parameter == typeParameter) {
           iterator.remove();
           break;
         }
       }
     }
   }
 }
  public boolean incorporate() {
    final Collection<InferenceVariable> inferenceVariables = mySession.getInferenceVariables();
    final PsiSubstitutor substitutor =
        mySession.retrieveNonPrimitiveEqualsBounds(inferenceVariables);
    for (InferenceVariable inferenceVariable : inferenceVariables) {
      if (inferenceVariable.getInstantiation() != PsiType.NULL) continue;
      final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ);
      final List<PsiType> upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER);
      final List<PsiType> lowerBounds = inferenceVariable.getBounds(InferenceBound.LOWER);

      eqEq(eqBounds);

      upDown(lowerBounds, upperBounds, substitutor);
      upDown(eqBounds, upperBounds, substitutor);
      upDown(lowerBounds, eqBounds, substitutor);

      upUp(upperBounds);
    }

    for (Pair<PsiTypeParameter[], PsiClassType> capture : myCaptures) {
      final PsiClassType right = capture.second;
      final PsiClass gClass = right.resolve();
      LOG.assertTrue(gClass != null);
      final PsiTypeParameter[] parameters = capture.first;
      PsiType[] typeArgs = right.getParameters();
      if (parameters.length != typeArgs.length) continue;
      for (int i = 0; i < typeArgs.length; i++) {
        PsiType aType = typeArgs[i];
        if (aType instanceof PsiCapturedWildcardType) {
          aType = ((PsiCapturedWildcardType) aType).getWildcard();
        }
        final InferenceVariable inferenceVariable = mySession.getInferenceVariable(parameters[i]);
        LOG.assertTrue(inferenceVariable != null);

        final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ);
        final List<PsiType> upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER);
        final List<PsiType> lowerBounds = inferenceVariable.getBounds(InferenceBound.LOWER);

        if (aType instanceof PsiWildcardType) {

          for (PsiType eqBound : eqBounds) {
            if (!isInferenceVariableOrFreshTypeParameter(eqBound)) return false;
          }

          final PsiClassType[] paramBounds = inferenceVariable.getParameter().getExtendsListTypes();

          PsiType glb = null;
          for (PsiClassType paramBound : paramBounds) {
            if (glb == null) {
              glb = paramBound;
            } else {
              glb = GenericsUtil.getGreatestLowerBound(glb, paramBound);
            }
          }

          if (!((PsiWildcardType) aType).isBounded()) {

            for (PsiType upperBound : upperBounds) {
              if (glb != null && mySession.getInferenceVariable(upperBound) == null) {
                addConstraint(
                    new StrictSubtypingConstraint(
                        upperBound, mySession.substituteWithInferenceVariables(glb)));
              }
            }

            for (PsiType lowerBound : lowerBounds) {
              if (isInferenceVariableOrFreshTypeParameter(lowerBound)) return false;
            }

          } else if (((PsiWildcardType) aType).isExtends()) {

            final PsiType extendsBound = ((PsiWildcardType) aType).getExtendsBound();

            for (PsiType upperBound : upperBounds) {
              if (mySession.getInferenceVariable(upperBound) == null) {
                if (paramBounds.length == 1
                        && paramBounds[0].equalsToText(CommonClassNames.JAVA_LANG_OBJECT)
                    || paramBounds.length == 0) {
                  addConstraint(new StrictSubtypingConstraint(upperBound, extendsBound));
                } else if (extendsBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)
                    && glb != null) {
                  addConstraint(
                      new StrictSubtypingConstraint(
                          upperBound, mySession.substituteWithInferenceVariables(glb)));
                }
              }
            }

            for (PsiType lowerBound : lowerBounds) {
              if (isInferenceVariableOrFreshTypeParameter(lowerBound)) return false;
            }

          } else {
            LOG.assertTrue(((PsiWildcardType) aType).isSuper());
            final PsiType superBound = ((PsiWildcardType) aType).getSuperBound();

            for (PsiType upperBound : upperBounds) {
              if (glb != null && mySession.getInferenceVariable(upperBound) == null) {
                addConstraint(
                    new StrictSubtypingConstraint(
                        mySession.substituteWithInferenceVariables(glb), upperBound));
              }
            }

            for (PsiType lowerBound : lowerBounds) {
              if (mySession.getInferenceVariable(lowerBound) == null) {
                addConstraint(new StrictSubtypingConstraint(lowerBound, superBound));
              }
            }
          }
        } else {
          inferenceVariable.addBound(aType, InferenceBound.EQ);
        }
      }
    }
    return true;
  }