docs: private ConstraintSet makeAtomic() {
      boolean done = false;
      List<Constraint> current = subConstraints;
      List<Constraint> next = Lists.newArrayList();
      TypeReplacement nextBindings = new TypeReplacement(bindings);
      boolean solvable = this.solvable;
      while (!done) {
        done = true;
        for (Constraint c : current) {
          Constraint substituted = c.substitute(nextBindings.getBindings());
          Type subtype = substituted.subtype;
          Type supertype = substituted.supertype;
          
          if (subtype.isFunctional() && supertype.isFunctional()) {
            next.add(new Constraint(supertype.getArgumentType(), subtype.getArgumentType()));
            next.add(new Constraint(subtype.getReturnType(), supertype.getReturnType()));
            done = false;
          } else if (!subtype.isFunctional() && supertype.isFunctional()) {
            if (subtype.hasTypeVariables()) {
              Type subtypeReplacement = upcast(supertype, next, false);
              nextBindings.add(subtype.getAtomicTypeVar(), subtypeReplacement);
              done = false;
            } else {
              solvable = false;
            }
          } else if (subtype.isFunctional() && !supertype.isFunctional()) {
            if (supertype.hasTypeVariables()) {
              Type supertypeReplacement = upcast(subtype, next, true);
              nextBindings.add(supertype.getAtomicTypeVar(), supertypeReplacement);
              done = false;
            } else {
              solvable = false;
            }
          } else {
            next.add(substituted);
          }
        }
  
        current = next;
        next = Lists.newArrayList();
      }
  
      return new ConstraintSet(Lists.newArrayList(), current, nextBindings,
          solvable, typeVarCounter);
    }