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[] setSuperInterfaces(ReferenceBinding[] superInterfaces) {
   this.superInterfaces = superInterfaces;
   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.superInterfaces == null) annotatedType.superInterfaces = superInterfaces;
     }
   }
   return superInterfaces;
 }
 /* (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);
    }
  }
  /**
   * Initialize capture bounds using substituted supertypes e.g. given X<U, V extends X<U, V>>,
   * capture(X<E,?>) = X<E,capture>, where capture extends X<E,capture>
   */
  public void initializeBounds(Scope scope, ParameterizedTypeBinding capturedParameterizedType) {
    TypeVariableBinding wildcardVariable = this.wildcard.typeVariable();
    if (wildcardVariable == null) {
      // error resilience when capturing Zork<?>
      // no substitution for wildcard bound (only formal bounds from type variables are to be
      // substituted: 104082)
      TypeBinding originalWildcardBound = this.wildcard.bound;
      switch (this.wildcard.boundKind) {
        case Wildcard.EXTENDS:
          // still need to capture bound supertype as well so as not to expose wildcards to the
          // outside (111208)
          TypeBinding capturedWildcardBound = originalWildcardBound.capture(scope, this.position);
          if (originalWildcardBound.isInterface()) {
            this.superclass = scope.getJavaLangObject();
            this.superInterfaces =
                new ReferenceBinding[] {(ReferenceBinding) capturedWildcardBound};
          } else {
            // the wildcard bound should be a subtype of variable superclass
            // it may occur that the bound is less specific, then consider glb (202404)
            if (capturedWildcardBound.isArrayType() || capturedWildcardBound == this) {
              this.superclass = scope.getJavaLangObject();
            } else {
              this.superclass = (ReferenceBinding) capturedWildcardBound;
            }
            this.superInterfaces = Binding.NO_SUPERINTERFACES;
          }
          this.firstBound = capturedWildcardBound;
          if ((capturedWildcardBound.tagBits & TagBits.HasTypeVariable) == 0)
            this.tagBits &= ~TagBits.HasTypeVariable;
          break;
        case Wildcard.UNBOUND:
          this.superclass = scope.getJavaLangObject();
          this.superInterfaces = Binding.NO_SUPERINTERFACES;
          this.tagBits &= ~TagBits.HasTypeVariable;
          break;
        case Wildcard.SUPER:
          this.superclass = scope.getJavaLangObject();
          this.superInterfaces = Binding.NO_SUPERINTERFACES;
          this.lowerBound = this.wildcard.bound;
          if ((originalWildcardBound.tagBits & TagBits.HasTypeVariable) == 0)
            this.tagBits &= ~TagBits.HasTypeVariable;
          break;
      }
      return;
    }
    ReferenceBinding originalVariableSuperclass = wildcardVariable.superclass;
    ReferenceBinding substitutedVariableSuperclass =
        (ReferenceBinding) Scope.substitute(capturedParameterizedType, originalVariableSuperclass);
    // prevent cyclic capture: given X<T>, capture(X<? extends T> could yield a circular type
    if (substitutedVariableSuperclass == this)
      substitutedVariableSuperclass = originalVariableSuperclass;

    ReferenceBinding[] originalVariableInterfaces = wildcardVariable.superInterfaces();
    ReferenceBinding[] substitutedVariableInterfaces =
        Scope.substitute(capturedParameterizedType, originalVariableInterfaces);
    if (substitutedVariableInterfaces != originalVariableInterfaces) {
      // prevent cyclic capture: given X<T>, capture(X<? extends T> could yield a circular type
      for (int i = 0, length = substitutedVariableInterfaces.length; i < length; i++) {
        if (substitutedVariableInterfaces[i] == this)
          substitutedVariableInterfaces[i] = originalVariableInterfaces[i];
      }
    }
    // no substitution for wildcard bound (only formal bounds from type variables are to be
    // substituted: 104082)
    TypeBinding originalWildcardBound = this.wildcard.bound;

    switch (this.wildcard.boundKind) {
      case Wildcard.EXTENDS:
        // still need to capture bound supertype as well so as not to expose wildcards to the
        // outside (111208)
        TypeBinding capturedWildcardBound = originalWildcardBound.capture(scope, this.position);
        if (originalWildcardBound.isInterface()) {
          this.superclass = substitutedVariableSuperclass;
          // merge wildcard bound into variable superinterfaces using glb
          if (substitutedVariableInterfaces == Binding.NO_SUPERINTERFACES) {
            this.superInterfaces =
                new ReferenceBinding[] {(ReferenceBinding) capturedWildcardBound};
          } else {
            int length = substitutedVariableInterfaces.length;
            System.arraycopy(
                substitutedVariableInterfaces,
                0,
                substitutedVariableInterfaces = new ReferenceBinding[length + 1],
                1,
                length);
            substitutedVariableInterfaces[0] = (ReferenceBinding) capturedWildcardBound;
            this.superInterfaces = Scope.greaterLowerBound(substitutedVariableInterfaces);
          }
        } else {
          // the wildcard bound should be a subtype of variable superclass
          // it may occur that the bound is less specific, then consider glb (202404)
          if (capturedWildcardBound.isArrayType() || capturedWildcardBound == this) {
            this.superclass = substitutedVariableSuperclass;
          } else {
            this.superclass = (ReferenceBinding) capturedWildcardBound;
            if (this.superclass.isSuperclassOf(substitutedVariableSuperclass)) {
              this.superclass = substitutedVariableSuperclass;
            }
          }
          this.superInterfaces = substitutedVariableInterfaces;
        }
        this.firstBound = capturedWildcardBound;
        if ((capturedWildcardBound.tagBits & TagBits.HasTypeVariable) == 0)
          this.tagBits &= ~TagBits.HasTypeVariable;
        break;
      case Wildcard.UNBOUND:
        this.superclass = substitutedVariableSuperclass;
        this.superInterfaces = substitutedVariableInterfaces;
        this.tagBits &= ~TagBits.HasTypeVariable;
        break;
      case Wildcard.SUPER:
        this.superclass = substitutedVariableSuperclass;
        if (wildcardVariable.firstBound == substitutedVariableSuperclass
            || originalWildcardBound == substitutedVariableSuperclass) {
          this.firstBound = substitutedVariableSuperclass;
        }
        this.superInterfaces = substitutedVariableInterfaces;
        this.lowerBound = originalWildcardBound;
        if ((originalWildcardBound.tagBits & TagBits.HasTypeVariable) == 0)
          this.tagBits &= ~TagBits.HasTypeVariable;
        break;
    }
  }