TypeBinding substituteInferenceVariable(InferenceVariable var, TypeBinding substituteType) {
   if (this.inRecursiveFunction) return this;
   this.inRecursiveFunction = true;
   try {
     boolean haveSubstitution = false;
     ReferenceBinding currentSuperclass = this.superclass;
     if (currentSuperclass != null) {
       currentSuperclass =
           (ReferenceBinding) currentSuperclass.substituteInferenceVariable(var, substituteType);
       haveSubstitution |= TypeBinding.notEquals(currentSuperclass, this.superclass);
     }
     ReferenceBinding[] currentSuperInterfaces = null;
     if (this.superInterfaces != null) {
       int length = this.superInterfaces.length;
       if (haveSubstitution)
         System.arraycopy(
             this.superInterfaces,
             0,
             currentSuperInterfaces = new ReferenceBinding[length],
             0,
             length);
       for (int i = 0; i < length; i++) {
         ReferenceBinding currentSuperInterface = this.superInterfaces[i];
         if (currentSuperInterface != null) {
           currentSuperInterface =
               (ReferenceBinding)
                   currentSuperInterface.substituteInferenceVariable(var, substituteType);
           if (TypeBinding.notEquals(currentSuperInterface, this.superInterfaces[i])) {
             if (currentSuperInterfaces == null)
               System.arraycopy(
                   this.superInterfaces,
                   0,
                   currentSuperInterfaces = new ReferenceBinding[length],
                   0,
                   length);
             currentSuperInterfaces[i] = currentSuperInterface;
             haveSubstitution = true;
           }
         }
       }
     }
     if (haveSubstitution) {
       TypeVariableBinding newVar =
           new TypeVariableBinding(
               this.sourceName, this.declaringElement, this.rank, this.environment);
       newVar.superclass = currentSuperclass;
       newVar.superInterfaces = currentSuperInterfaces;
       newVar.tagBits = this.tagBits;
       return newVar;
     }
     return this;
   } finally {
     this.inRecursiveFunction = false;
   }
 }
 /* An annotated type variable use differs from its declaration exactly in its annotations and in nothing else.
    Propagate writes to all annotated variants so the clones evolve along.
 */
 public ReferenceBinding setSuperClass(ReferenceBinding superclass) {
   this.superclass = superclass;
   if ((this.tagBits & TagBits.HasAnnotatedVariants) != 0) {
     TypeBinding[] annotatedTypes = getDerivedTypesForDeferredInitialization();
     for (int i = 0, length = annotatedTypes == null ? 0 : annotatedTypes.length;
         i < length;
         i++) {
       TypeVariableBinding annotatedType = (TypeVariableBinding) annotatedTypes[i];
       if (annotatedType.superclass == null) annotatedType.superclass = superclass;
     }
   }
   return superclass;
 }
 /* (non-Javadoc)
  * @see org.eclipse.jdt.core.dom.ITypeBinding#getTypeBounds()
  */
 public ITypeBinding[] getTypeBounds() {
   if (this.bounds != null) {
     return this.bounds;
   }
   if (this.binding instanceof TypeVariableBinding) {
     TypeVariableBinding typeVariableBinding = (TypeVariableBinding) this.binding;
     ReferenceBinding varSuperclass = typeVariableBinding.superclass();
     org.eclipse.jdt.internal.compiler.lookup.TypeBinding firstClassOrArrayBound =
         typeVariableBinding.firstBound;
     int boundsLength = 0;
     if (firstClassOrArrayBound != null) {
       if (firstClassOrArrayBound == varSuperclass) {
         boundsLength++;
       } else if (firstClassOrArrayBound.isArrayType()) { // capture of ? extends/super arrayType
         boundsLength++;
       } else {
         firstClassOrArrayBound = null;
       }
     }
     ReferenceBinding[] superinterfaces = typeVariableBinding.superInterfaces();
     int superinterfacesLength = 0;
     if (superinterfaces != null) {
       superinterfacesLength = superinterfaces.length;
       boundsLength += superinterfacesLength;
     }
     if (boundsLength != 0) {
       ITypeBinding[] typeBounds = new ITypeBinding[boundsLength];
       int boundsIndex = 0;
       if (firstClassOrArrayBound != null) {
         ITypeBinding typeBinding = this.resolver.getTypeBinding(firstClassOrArrayBound);
         if (typeBinding == null) {
           return this.bounds = NO_TYPE_BINDINGS;
         }
         typeBounds[boundsIndex++] = typeBinding;
       }
       if (superinterfaces != null) {
         for (int i = 0; i < superinterfacesLength; i++, boundsIndex++) {
           ITypeBinding typeBinding = this.resolver.getTypeBinding(superinterfaces[i]);
           if (typeBinding == null) {
             return this.bounds = NO_TYPE_BINDINGS;
           }
           typeBounds[boundsIndex] = typeBinding;
         }
       }
       return this.bounds = typeBounds;
     }
   }
   return this.bounds = NO_TYPE_BINDINGS;
 }
  static boolean isEqual(
      org.eclipse.jdt.internal.compiler.lookup.TypeBinding typeBinding,
      org.eclipse.jdt.internal.compiler.lookup.TypeBinding typeBinding2,
      HashSet visitedTypes) {
    if (typeBinding == typeBinding2) return true;
    if (typeBinding == null || typeBinding2 == null) return false;

    switch (typeBinding.kind()) {
      case Binding.BASE_TYPE:
        if (!typeBinding2.isBaseType()) {
          return false;
        }
        return typeBinding.id == typeBinding2.id;

      case Binding.ARRAY_TYPE:
        if (!typeBinding2.isArrayType()) {
          return false;
        }
        return typeBinding.dimensions() == typeBinding2.dimensions()
            && isEqual(
                typeBinding.leafComponentType(), typeBinding2.leafComponentType(), visitedTypes);

      case Binding.PARAMETERIZED_TYPE:
        if (!typeBinding2.isParameterizedType()) {
          return false;
        }
        ParameterizedTypeBinding parameterizedTypeBinding = (ParameterizedTypeBinding) typeBinding;
        ParameterizedTypeBinding parameterizedTypeBinding2 =
            (ParameterizedTypeBinding) typeBinding2;
        return CharOperation.equals(
                parameterizedTypeBinding.compoundName, parameterizedTypeBinding2.compoundName)
            && (parameterizedTypeBinding.modifiers
                    & (ExtraCompilerModifiers.AccJustFlag
                        | ClassFileConstants.AccInterface
                        | ClassFileConstants.AccEnum
                        | ClassFileConstants.AccAnnotation))
                == (parameterizedTypeBinding2.modifiers
                    & (ExtraCompilerModifiers.AccJustFlag
                        | ClassFileConstants.AccInterface
                        | ClassFileConstants.AccEnum
                        | ClassFileConstants.AccAnnotation))
            && isEqual(
                parameterizedTypeBinding.arguments,
                parameterizedTypeBinding2.arguments,
                visitedTypes)
            && isEqual(
                parameterizedTypeBinding.enclosingType(),
                parameterizedTypeBinding2.enclosingType(),
                visitedTypes);

      case Binding.WILDCARD_TYPE:
        if (typeBinding2.kind() != Binding.WILDCARD_TYPE) {
          return false;
        }
        WildcardBinding wildcardBinding = (WildcardBinding) typeBinding;
        WildcardBinding wildcardBinding2 = (WildcardBinding) typeBinding2;
        return isEqual(wildcardBinding.bound, wildcardBinding2.bound, visitedTypes)
            && wildcardBinding.boundKind == wildcardBinding2.boundKind;

      case Binding.INTERSECTION_TYPE:
        if (typeBinding2.kind() != Binding.INTERSECTION_TYPE) {
          return false;
        }
        WildcardBinding intersectionBinding = (WildcardBinding) typeBinding;
        WildcardBinding intersectionBinding2 = (WildcardBinding) typeBinding2;
        return isEqual(intersectionBinding.bound, intersectionBinding2.bound, visitedTypes)
            && isEqual(
                intersectionBinding.otherBounds, intersectionBinding2.otherBounds, visitedTypes);

      case Binding.TYPE_PARAMETER:
        if (!(typeBinding2.isTypeVariable())) {
          return false;
        }
        if (typeBinding.isCapture()) {
          if (!(typeBinding2.isCapture())) {
            return false;
          }
          CaptureBinding captureBinding = (CaptureBinding) typeBinding;
          CaptureBinding captureBinding2 = (CaptureBinding) typeBinding2;
          if (captureBinding.position == captureBinding2.position) {
            if (visitedTypes.contains(typeBinding)) return true;
            visitedTypes.add(typeBinding);

            return isEqual(captureBinding.wildcard, captureBinding2.wildcard, visitedTypes)
                && isEqual(captureBinding.sourceType, captureBinding2.sourceType, visitedTypes);
          }
          return false;
        }
        TypeVariableBinding typeVariableBinding = (TypeVariableBinding) typeBinding;
        TypeVariableBinding typeVariableBinding2 = (TypeVariableBinding) typeBinding2;
        if (CharOperation.equals(typeVariableBinding.sourceName, typeVariableBinding2.sourceName)) {
          if (visitedTypes.contains(typeBinding)) return true;
          visitedTypes.add(typeBinding);

          return isEqual(
                  typeVariableBinding.declaringElement,
                  typeVariableBinding2.declaringElement,
                  visitedTypes)
              && isEqual(
                  typeVariableBinding.superclass(), typeVariableBinding2.superclass(), visitedTypes)
              && isEqual(
                  typeVariableBinding.superInterfaces(),
                  typeVariableBinding2.superInterfaces(),
                  visitedTypes);
        }
        return false;
      case Binding.GENERIC_TYPE:
        if (!typeBinding2.isGenericType()) {
          return false;
        }
        ReferenceBinding referenceBinding = (ReferenceBinding) typeBinding;
        ReferenceBinding referenceBinding2 = (ReferenceBinding) typeBinding2;
        return CharOperation.equals(referenceBinding.compoundName, referenceBinding2.compoundName)
            && (referenceBinding.modifiers
                    & (ExtraCompilerModifiers.AccJustFlag
                        | ClassFileConstants.AccInterface
                        | ClassFileConstants.AccEnum
                        | ClassFileConstants.AccAnnotation))
                == (referenceBinding2.modifiers
                    & (ExtraCompilerModifiers.AccJustFlag
                        | ClassFileConstants.AccInterface
                        | ClassFileConstants.AccEnum
                        | ClassFileConstants.AccAnnotation))
            && isEqual(
                referenceBinding.typeVariables(), referenceBinding2.typeVariables(), visitedTypes)
            && isEqual(
                referenceBinding.enclosingType(), referenceBinding2.enclosingType(), visitedTypes);

      case Binding.RAW_TYPE:
      default:
        if (!(typeBinding2 instanceof ReferenceBinding)) {
          return false;
        }
        referenceBinding = (ReferenceBinding) typeBinding;
        referenceBinding2 = (ReferenceBinding) typeBinding2;
        char[] constantPoolName = referenceBinding.constantPoolName();
        char[] constantPoolName2 = referenceBinding2.constantPoolName();
        // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=116833
        if (constantPoolName == null) {
          if (constantPoolName2 != null) {
            return false;
          }
          if (!CharOperation.equals(
              referenceBinding.computeUniqueKey(), referenceBinding2.computeUniqueKey())) {
            return false;
          }
        } else {
          if (constantPoolName2 == null) {
            return false;
          }
          if (!CharOperation.equals(constantPoolName, constantPoolName2)) {
            return false;
          }
        }
        return CharOperation.equals(referenceBinding.compoundName, referenceBinding2.compoundName)
            && (!referenceBinding2.isGenericType())
            && (referenceBinding.isRawType() == referenceBinding2.isRawType())
            && ((referenceBinding.modifiers & ~ClassFileConstants.AccSuper)
                    & (ExtraCompilerModifiers.AccJustFlag
                        | ClassFileConstants.AccInterface
                        | ClassFileConstants.AccEnum
                        | ClassFileConstants.AccAnnotation))
                == ((referenceBinding2.modifiers & ~ClassFileConstants.AccSuper)
                    & (ExtraCompilerModifiers.AccJustFlag
                        | ClassFileConstants.AccInterface
                        | ClassFileConstants.AccEnum
                        | ClassFileConstants.AccAnnotation))
            && isEqual(
                referenceBinding.enclosingType(), referenceBinding2.enclosingType(), visitedTypes);
    }
  }