Program: 27422
Project: 27422
Buggy source:
private ImmutableMap<String,JSType> calcTypeInstantiation(Node callNode,Node receiver,Node firstArg,FunctionType funType,TypeEnv typeEnv,boolean isFwd){
  Preconditions.checkState(receiver == null || isFwd);
  List<String> typeParameters=funType.getTypeParameters();
  Multimap<String,JSType> typeMultimap=LinkedHashMultimap.create();
  JSType funRecvType=funType.getThisType();
  if (receiver != null && funRecvType != null && !funRecvType.isSingletonObj()) {
    EnvTypePair pair=analyzeExprFwd(receiver,typeEnv);
    unifyWithSubtypeWarnIfFail(funRecvType,pair.type,typeParameters,typeMultimap,receiver,isFwd);
    typeEnv=pair.env;
  }
  Node arg=firstArg;
  int i=0;
  while (arg != null) {
    EnvTypePair pair=isFwd ? analyzeExprFwd(arg,typeEnv) : analyzeExprBwd(arg,typeEnv);
    unifyWithSubtypeWarnIfFail(funType.getFormalType(i),pair.type,typeParameters,typeMultimap,arg,isFwd);
    arg=arg.getNext();
    typeEnv=pair.env;
    i++;
  }
  ImmutableMap.Builder<String,JSType> builder=ImmutableMap.builder();
  for (  String typeParam : typeParameters) {
    Collection<JSType> types=typeMultimap.get(typeParam);
    if (types.size() > 1) {
      if (isFwd) {
        warnings.add(JSError.make(callNode,NOT_UNIQUE_INSTANTIATION,funType.toString(),typeParam,types.toString()));
      }
      builder.put(typeParam,JSType.UNKNOWN);
    }
 else     if (types.size() == 1) {
      JSType t=Iterables.getOnlyElement(types);
      builder.put(typeParam,t.isBottom() ? JSType.UNKNOWN : t);
    }
 else {
      builder.put(typeParam,JSType.UNKNOWN);
    }
  }
  return builder.build();
}

Fixed version (ground truth):
/** 
 * We don't use the requiredType of the context to unify with the return type. There are several difficulties: 1) A polymorphic function is allowed to return ANY subtype of the requiredType, so we would need to use a heuristic to determine the type to unify with. 2) It's hard to give good error messages in cases like: id('str') - 5 We want an invalid-operand-type, not a not-unique-instantiation. We don't take the arg evaluation order into account during instantiation. When calculating the instantiation, when do we use the receiver type? See the following snippet: / * @constructor * @template T * @param {T} x * / function Foo(x) {} / * @template T * @param {T} x * / Foo.prototype.f = function(x) {}; Foo.prototype.f.bind(new Foo(123), 'asdf'); Here, the receiver type of f is Foo<T>, but the T is the class's T, not the T of f's template declaration. OTOH, if f had a @this annotation that contained T, T would refer to f's T. There is no way of knowing what's the scope of the type variables in the receiver of the function type. But when THIS comes from the class, it is always a singleton object. So, we use a heuristic: if THIS is not a singleton obj, we know it comes from and we use it for the instantiation.
 */
private ImmutableMap<String,JSType> calcTypeInstantiation(Node callNode,Node receiver,Node firstArg,FunctionType funType,TypeEnv typeEnv,boolean isFwd){
  Preconditions.checkState(receiver == null || isFwd);
  List<String> typeParameters=funType.getTypeParameters();
  Multimap<String,JSType> typeMultimap=LinkedHashMultimap.create();
  JSType funRecvType=funType.getThisType();
  if (receiver != null && funRecvType != null && !funRecvType.isSingletonObj()) {
    EnvTypePair pair=analyzeExprFwd(receiver,typeEnv);
    unifyWithSubtypeWarnIfFail(funRecvType,pair.type,typeParameters,typeMultimap,receiver,isFwd);
    typeEnv=pair.env;
  }
  Node arg=firstArg;
  int i=0;
  while (arg != null) {
    EnvTypePair pair=isFwd ? analyzeExprFwd(arg,typeEnv) : analyzeExprBwd(arg,typeEnv);
    unifyWithSubtypeWarnIfFail(funType.getFormalType(i),pair.type,typeParameters,typeMultimap,arg,isFwd);
    arg=arg.getNext();
    typeEnv=pair.env;
    i++;
  }
  ImmutableMap.Builder<String,JSType> builder=ImmutableMap.builder();
  for (  String typeParam : typeParameters) {
    Collection<JSType> types=typeMultimap.get(typeParam);
    if (types.size() > 1) {
      if (isFwd) {
        warnings.add(JSError.make(callNode,NOT_UNIQUE_INSTANTIATION,funType.toString(),typeParam,types.toString()));
      }
      builder.put(typeParam,JSType.UNKNOWN);
    }
 else     if (types.size() == 1) {
      JSType t=Iterables.getOnlyElement(types);
      builder.put(typeParam,t.isBottom() ? JSType.UNKNOWN : t);
    }
 else {
      builder.put(typeParam,JSType.UNKNOWN);
    }
  }
  return builder.build();
}