protected void mergeTypeParametersIntoParent(ResolvedTypes parent) {
   for (UnboundTypeReference unbound : basicGetTypeParameters().values()) {
     LightweightTypeReference resolvedTo = unbound.getResolvedTo();
     if (resolvedTo == null) {
       List<JvmTypeParameter> typeParameters = basicGetDeclardTypeParameters();
       if (typeParameters != null && typeParameters.contains(unbound.getTypeParameter())) {
         unbound.tryResolve();
         if (!unbound.internalIsResolved()) {
           if (unbound.getExpression() instanceof XConstructorCall) {
             unbound.resolve(); // resolve against constraints
           } else {
             unbound.acceptHint(
                 unbound.getOwner().newParameterizedTypeReference(unbound.getTypeParameter()),
                 BoundTypeArgumentSource.RESOLVED,
                 unbound,
                 VarianceInfo.INVARIANT,
                 VarianceInfo.INVARIANT);
           }
         }
       } else {
         LightweightTypeReference reference = unbound.copyInto(parent.getReferenceOwner());
         if (reference instanceof UnboundTypeReference) {
           parent.acceptUnboundTypeReference(
               unbound.getHandle(), (UnboundTypeReference) reference);
         }
       }
     }
   }
   Map<Object, List<LightweightBoundTypeArgument>> typeParameterHints =
       basicGetTypeParameterHints();
   for (Map.Entry<Object, List<LightweightBoundTypeArgument>> hint :
       typeParameterHints.entrySet()) {
     if (!parent.isResolved(hint.getKey())) {
       List<LightweightBoundTypeArgument> boundTypeArguments = hint.getValue();
       for (LightweightBoundTypeArgument boundTypeArgument : boundTypeArguments) {
         if (boundTypeArgument.getOrigin() instanceof VarianceInfo) {
           parent.acceptHint(hint.getKey(), boundTypeArgument);
         } else {
           LightweightBoundTypeArgument copy =
               new LightweightBoundTypeArgument(
                   boundTypeArgument.getTypeReference().copyInto(parent.getReferenceOwner()),
                   boundTypeArgument.getSource(),
                   boundTypeArgument.getOrigin(),
                   boundTypeArgument.getDeclaredVariance(),
                   boundTypeArgument.getActualVariance());
           parent.acceptHint(hint.getKey(), copy);
         }
       }
     }
   }
 }
 @Override
 protected List<LightweightBoundTypeArgument> getHints(Object handle) {
   List<LightweightBoundTypeArgument> result = super.getHints(handle);
   if (result.size() == 1 && super.isResolved(handle)) {
     return result;
   }
   List<LightweightBoundTypeArgument> parentHints = getParent().getHints(handle);
   if (parentHints.size() == 1 && getParent().isResolved(handle)) {
     LightweightBoundTypeArgument parentHint = parentHints.get(0);
     LightweightBoundTypeArgument copy =
         new LightweightBoundTypeArgument(
             parentHint.getTypeReference().copyInto(getReferenceOwner()),
             parentHint.getSource(),
             parentHint.getOrigin(),
             parentHint.getDeclaredVariance(),
             parentHint.getActualVariance());
     return Collections.singletonList(copy);
   }
   if (parentHints.isEmpty()) return result;
   List<LightweightBoundTypeArgument> withParentHints =
       Lists.newArrayListWithCapacity(parentHints.size() + result.size());
   for (LightweightBoundTypeArgument parentHint : parentHints) {
     if (parentHint.getTypeReference() == null) {
       withParentHints.add(parentHint);
     } else {
       LightweightBoundTypeArgument copy =
           new LightweightBoundTypeArgument(
               parentHint.getTypeReference().copyInto(getReferenceOwner()),
               parentHint.getSource(),
               parentHint.getOrigin(),
               parentHint.getDeclaredVariance(),
               parentHint.getActualVariance());
       withParentHints.add(copy);
     }
   }
   withParentHints.addAll(result);
   return withParentHints;
 }