public static <T> T performWithSubstitutedParameterBounds(final PsiTypeParameter[] typeParameters,
                                                            final PsiSubstitutor substitutor,
                                                            final Supplier<? extends T> producer) {
    return ThreadLocalTypes.performWithTypes(map -> {
      for (PsiTypeParameter parameter : typeParameters) {
        final PsiClassType[] types = parameter.getExtendsListTypes();
        if (types.length > 0) {
          final List<PsiType> conjuncts = ContainerUtil.map(types, substitutor::substitute);
          //don't glb to avoid flattening = Object&Interface would be preserved
          //otherwise methods with different signatures could get same erasure
          final PsiType upperBound = PsiIntersectionType.createIntersection(false, conjuncts.toArray(PsiType.EMPTY_ARRAY));
          map.forceType(parameter, upperBound);
        }
      }
      return producer.get();
    });
  }
public static <T> T performWithSubstitutedParameterBounds(final PsiTypeParameter[] typeParameters,
                                                            final PsiSubstitutor substitutor,
                                                            final Supplier<? extends T> producer)