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.internal.compiler.lookup.TypeBinding#erasure()
  */
 public TypeBinding erasure() {
   if (this.otherBounds == null) {
     if (this.boundKind == Wildcard.EXTENDS) return this.bound.erasure();
     TypeVariableBinding var = typeVariable();
     if (var != null) return var.erasure();
     return this.genericType; // if typeVariable() == null, then its inconsistent & return
     // this.genericType to avoid NPE case
   }
   // intersection type
   return this.bound.id == TypeIds.T_JavaLangObject
       ? this.otherBounds[0].erasure() // use first explicit bound to improve stackmap
       : this.bound.erasure();
 }
 /* (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;
 }
 /* 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 TypeBinding setFirstBound(TypeBinding firstBound) {
   this.firstBound = firstBound;
   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.firstBound == null) annotatedType.firstBound = firstBound;
     }
   }
   if (firstBound != null && firstBound.hasNullTypeAnnotations())
     this.tagBits |= TagBits.HasNullTypeAnnotation;
   return firstBound;
 }
  // interpreting 'type' as a provided type, compute the provide null bits
  // we inspect the main type plus bounds of type variables and wildcards
  static long providedNullTagBits(TypeBinding type) {

    long tagBits = type.tagBits & TagBits.AnnotationNullMASK;
    if (tagBits != 0) return validNullTagBits(tagBits);

    if (type.isWildcard()) { // wildcard can be 'provided' during inheritance checks
      WildcardBinding wildcard = (WildcardBinding) type;
      if (wildcard.boundKind == Wildcard.UNBOUND) return 0;
      tagBits = wildcard.bound.tagBits & TagBits.AnnotationNullMASK;
      if (tagBits == 0) return 0;
      switch (wildcard.boundKind) {
        case Wildcard.EXTENDS:
          if (tagBits == TagBits.AnnotationNonNull) return TagBits.AnnotationNonNull;
          return TagBits.AnnotationNullMASK; // @Nullable or better
        case Wildcard.SUPER:
          if (tagBits == TagBits.AnnotationNullable) return TagBits.AnnotationNullable;
          return TagBits.AnnotationNullMASK; // @NonNull or worse
      }
      return 0;
    }

    if (type.isTypeVariable()) { // incl. captures
      TypeVariableBinding typeVariable = (TypeVariableBinding) type;
      boolean haveNullBits = false;
      if (typeVariable.isCapture()) {
        TypeBinding lowerBound = ((CaptureBinding) typeVariable).lowerBound;
        if (lowerBound != null) {
          tagBits = lowerBound.tagBits & TagBits.AnnotationNullMASK;
          if (tagBits == TagBits.AnnotationNullable)
            return TagBits.AnnotationNullable; // cannot be @NonNull
          haveNullBits |= (tagBits != 0);
        }
      }
      if (typeVariable.firstBound != null) {
        long boundBits = typeVariable.firstBound.tagBits & TagBits.AnnotationNullMASK;
        if (boundBits == TagBits.AnnotationNonNull)
          return TagBits.AnnotationNonNull; // cannot be @Nullable
        haveNullBits |= (boundBits != 0);
      }
      if (haveNullBits)
        return TagBits
            .AnnotationNullMASK; // could be either, can only match to a wildcard accepting both
    }

    return 0;
  }
 @Override
 public void setTypeAnnotations(AnnotationBinding[] annotations, boolean evalNullAnnotations) {
   super.setTypeAnnotations(annotations, evalNullAnnotations);
   if (annotations != Binding.NO_ANNOTATIONS && this.wildcard != null) {
     // keep annotations in sync, propagate from capture to its wildcard:
     this.wildcard =
         (WildcardBinding)
             this.wildcard.environment.createAnnotatedType(this.wildcard, annotations);
   }
 }
 protected void appendNullAnnotation(StringBuffer nameBuffer, CompilerOptions options) {
   int oldSize = nameBuffer.length();
   super.appendNullAnnotation(nameBuffer, options);
   if (oldSize == nameBuffer.length()) { // nothing appended in super.appendNullAnnotation()?
     if (hasNullTypeAnnotations()) {
       // see if the prototype has null type annotations:
       TypeVariableBinding[] typeVariables = null;
       if (this.declaringElement instanceof ReferenceBinding) {
         typeVariables = ((ReferenceBinding) this.declaringElement).typeVariables();
       } else if (this.declaringElement instanceof MethodBinding) {
         typeVariables = ((MethodBinding) this.declaringElement).typeVariables();
       }
       if (typeVariables != null && typeVariables.length > this.rank) {
         TypeVariableBinding prototype = typeVariables[this.rank];
         if (prototype != this) // $IDENTITY-COMPARISON$
         prototype.appendNullAnnotation(nameBuffer, options);
       }
     }
   }
 }
 /*
  * @see ITypeBinding#getDeclaringClass()
  */
 public synchronized ITypeBinding getDeclaringClass() {
   if (isClass() || isInterface() || isEnum()) {
     ReferenceBinding referenceBinding = (ReferenceBinding) this.binding;
     if (referenceBinding.isNestedType()) {
       try {
         return this.resolver.getTypeBinding(referenceBinding.enclosingType());
       } catch (RuntimeException e) {
         /* in case a method cannot be resolvable due to missing jars on the classpath
          * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=57871
          * https://bugs.eclipse.org/bugs/show_bug.cgi?id=63550
          * https://bugs.eclipse.org/bugs/show_bug.cgi?id=64299
          */
         org.eclipse.jdt.internal.core.util.Util.log(
             e, "Could not retrieve declaring class"); // $NON-NLS-1$
       }
     }
   } else if (this.binding.isTypeVariable()) {
     TypeVariableBinding typeVariableBinding = (TypeVariableBinding) this.binding;
     Binding declaringElement =
         typeVariableBinding.isCapture()
             ? ((CaptureBinding) typeVariableBinding).sourceType
             : typeVariableBinding.declaringElement;
     if (declaringElement instanceof ReferenceBinding) {
       try {
         return this.resolver.getTypeBinding((ReferenceBinding) declaringElement);
       } catch (RuntimeException e) {
         /* in case a method cannot be resolvable due to missing jars on the classpath
          * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=57871
          * https://bugs.eclipse.org/bugs/show_bug.cgi?id=63550
          * https://bugs.eclipse.org/bugs/show_bug.cgi?id=64299
          */
         org.eclipse.jdt.internal.core.util.Util.log(
             e, "Could not retrieve declaring class"); // $NON-NLS-1$
       }
     }
   }
   return null;
 }
 public TypeVariableBinding(TypeVariableBinding prototype) {
   super(prototype);
   this.declaringElement = prototype.declaringElement;
   this.rank = prototype.rank;
   this.firstBound = prototype.firstBound;
   this.superclass = prototype.superclass;
   this.superInterfaces = Binding.NO_SUPERINTERFACES;
   if (prototype.superInterfaces != null) {
     int len = prototype.superInterfaces.length;
     if (len > 0)
       System.arraycopy(
           prototype.superInterfaces, 0, this.superInterfaces = new ReferenceBinding[len], 0, len);
   }
   this.genericTypeSignature = prototype.genericTypeSignature;
   this.environment = prototype.environment;
   prototype.tagBits |= TagBits.HasAnnotatedVariants;
   this.tagBits &= ~TagBits.HasAnnotatedVariants;
 }
 public CaptureBinding(
     WildcardBinding wildcard,
     ReferenceBinding sourceType,
     int start,
     int end,
     ASTNode cud,
     int captureID) {
   super(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX, wildcard.environment);
   this.wildcard = wildcard;
   this.modifiers =
       ClassFileConstants.AccPublic
           | ExtraCompilerModifiers.AccGenericSignature; // treat capture as public
   this.fPackage = wildcard.fPackage;
   this.sourceType = sourceType;
   this.start = start;
   this.end = end;
   this.captureID = captureID;
   this.tagBits |= TagBits.HasCapturedWildcard;
   this.cud = cud;
   if (wildcard.hasTypeAnnotations()) {
     // register an unannoted version before adding the annotated wildcard:
     CaptureBinding unannotated = (CaptureBinding) clone(null);
     unannotated.wildcard = (WildcardBinding) this.wildcard.unannotated();
     this.environment.getUnannotatedType(unannotated);
     this.id = unannotated.id; // transfer fresh id
     // now register this annotated type:
     this.environment.typeSystem.cacheDerivedType(this, unannotated, this);
     // propagate from wildcard to capture - use super version, because our own method propagates
     // type annotations in the opposite direction:
     super.setTypeAnnotations(
         wildcard.getTypeAnnotations(),
         wildcard.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled);
     if (wildcard.hasNullTypeAnnotations()) this.tagBits |= TagBits.HasNullTypeAnnotation;
   } else {
     computeId(this.environment);
   }
 }
  /** Perform inference of generic method type parameters and/or expected type */
  public static MethodBinding computeCompatibleMethod(
      MethodBinding originalMethod,
      TypeBinding[] arguments,
      Scope scope,
      InvocationSite invocationSite) {
    ParameterizedGenericMethodBinding methodSubstitute;
    TypeVariableBinding[] typeVariables = originalMethod.typeVariables;
    TypeBinding[] substitutes = invocationSite.genericTypeArguments();
    TypeBinding[] uncheckedArguments = null;
    computeSubstitutes:
    {
      if (substitutes != null) {
        // explicit type arguments got supplied
        if (substitutes.length != typeVariables.length) {
          // incompatible due to wrong arity
          return new ProblemMethodBinding(
              originalMethod,
              originalMethod.selector,
              substitutes,
              ProblemReasons.TypeParameterArityMismatch);
        }
        methodSubstitute =
            scope.environment().createParameterizedGenericMethod(originalMethod, substitutes);
        break computeSubstitutes;
      }
      // perform type argument inference (15.12.2.7)
      // initializes the map of substitutes (var --> type[][]{ equal, extends, super}
      TypeBinding[] parameters = originalMethod.parameters;
      InferenceContext inferenceContext = new InferenceContext(originalMethod);
      methodSubstitute =
          inferFromArgumentTypes(scope, originalMethod, arguments, parameters, inferenceContext);
      if (methodSubstitute == null) return null;

      // substitutes may hold null to denote unresolved vars, but null arguments got replaced with
      // respective original variable in param method
      // 15.12.2.8 - inferring unresolved type arguments
      if (inferenceContext.hasUnresolvedTypeArgument()) {
        if (inferenceContext.isUnchecked) { // only remember unchecked status post 15.12.2.7
          int length = inferenceContext.substitutes.length;
          System.arraycopy(
              inferenceContext.substitutes,
              0,
              uncheckedArguments = new TypeBinding[length],
              0,
              length);
        }
        if (methodSubstitute.returnType != TypeBinding.VOID) {
          TypeBinding expectedType = null;
          // if message invocation has expected type
          if (invocationSite instanceof MessageSend) {
            MessageSend message = (MessageSend) invocationSite;
            expectedType = message.expectedType;
          }
          if (expectedType != null) {
            // record it was explicit from context, as opposed to assumed by default (see below)
            inferenceContext.hasExplicitExpectedType = true;
          } else {
            expectedType = scope.getJavaLangObject(); // assume Object by default
          }
          inferenceContext.expectedType = expectedType;
        }
        methodSubstitute = methodSubstitute.inferFromExpectedType(scope, inferenceContext);
        if (methodSubstitute == null) return null;
      }
    }

    // bounds check
    for (int i = 0, length = typeVariables.length; i < length; i++) {
      TypeVariableBinding typeVariable = typeVariables[i];
      TypeBinding substitute = methodSubstitute.typeArguments[i];
      if (uncheckedArguments != null && uncheckedArguments[i] == null)
        continue; // only bound check if inferred through 15.12.2.6
      switch (typeVariable.boundCheck(methodSubstitute, substitute)) {
        case TypeConstants.MISMATCH:
          // incompatible due to bound check
          int argLength = arguments.length;
          TypeBinding[] augmentedArguments =
              new TypeBinding[argLength + 2]; // append offending substitute and typeVariable
          System.arraycopy(arguments, 0, augmentedArguments, 0, argLength);
          augmentedArguments[argLength] = substitute;
          augmentedArguments[argLength + 1] = typeVariable;
          return new ProblemMethodBinding(
              methodSubstitute,
              originalMethod.selector,
              augmentedArguments,
              ProblemReasons.ParameterBoundMismatch);
        case TypeConstants.UNCHECKED:
          // tolerate unchecked bounds
          methodSubstitute.tagBits |= TagBits.HasUncheckedTypeArgumentForBoundCheck;
          break;
      }
    }
    // check presence of unchecked argument conversion a posteriori (15.12.2.6)
    return methodSubstitute;
  }
  private Object reduceSubType(Scope scope, TypeBinding subCandidate, TypeBinding superCandidate) {
    // 18.2.3 Subtyping Constraints
    if (subCandidate.isProperType(true) && superCandidate.isProperType(true)) {
      if (subCandidate.isCompatibleWith(superCandidate, scope)) return TRUE;
      return FALSE;
    }
    if (subCandidate.id == TypeIds.T_null) return TRUE;
    if (superCandidate.id == TypeIds.T_null) return FALSE;
    if (subCandidate instanceof InferenceVariable)
      return new TypeBound((InferenceVariable) subCandidate, superCandidate, SUBTYPE, this.isSoft);
    if (superCandidate instanceof InferenceVariable)
      return new TypeBound(
          (InferenceVariable) superCandidate,
          subCandidate,
          SUPERTYPE,
          this.isSoft); // normalize to have variable on LHS
    switch (superCandidate.kind()) {
      case Binding.GENERIC_TYPE:
      case Binding.TYPE:
      case Binding.RAW_TYPE:
        {
          if (subCandidate.isSubtypeOf(superCandidate)) return TRUE;
          return FALSE;
        }
      case Binding.PARAMETERIZED_TYPE:
        {
          List<ConstraintFormula> constraints = new ArrayList<>();
          while (superCandidate != null
              && superCandidate.kind() == Binding.PARAMETERIZED_TYPE
              && subCandidate != null) {
            if (!addConstraintsFromTypeParameters(
                subCandidate, (ParameterizedTypeBinding) superCandidate, constraints)) return FALSE;
            // travel to enclosing types to check if they have type parameters, too:
            superCandidate = superCandidate.enclosingType();
            subCandidate = subCandidate.enclosingType();
          }
          switch (constraints.size()) {
            case 0:
              return TRUE;
            case 1:
              return constraints.get(0);
            default:
              return constraints.toArray(new ConstraintFormula[constraints.size()]);
          }
        }
      case Binding.ARRAY_TYPE:
        TypeBinding tPrime = ((ArrayBinding) superCandidate).elementsType();
        // let S'[] be the most specific array type that is a supertype of S (or S itself)
        ArrayBinding sPrimeArray = null;
        switch (subCandidate.kind()) {
          case Binding.INTERSECTION_TYPE:
            {
              WildcardBinding intersection = (WildcardBinding) subCandidate;
              sPrimeArray =
                  findMostSpecificSuperArray(
                      intersection.bound, intersection.otherBounds, intersection);
              break;
            }
          case Binding.ARRAY_TYPE:
            sPrimeArray = (ArrayBinding) subCandidate;
            break;
          case Binding.TYPE_PARAMETER:
            {
              TypeVariableBinding subTVB = (TypeVariableBinding) subCandidate;
              sPrimeArray =
                  findMostSpecificSuperArray(subTVB.firstBound, subTVB.otherUpperBounds(), subTVB);
              break;
            }
          default:
            return FALSE;
        }
        if (sPrimeArray == null) return FALSE;
        TypeBinding sPrime = sPrimeArray.elementsType();
        if (!tPrime.isPrimitiveType() && !sPrime.isPrimitiveType()) {
          return ConstraintTypeFormula.create(sPrime, tPrime, SUBTYPE, this.isSoft);
        }
        return TypeBinding.equalsEquals(tPrime, sPrime) ? TRUE : FALSE; // same primitive type?

        // "type variable" has two implementations in JDT:
      case Binding.WILDCARD_TYPE:
        if (subCandidate.kind() == Binding.INTERSECTION_TYPE) {
          ReferenceBinding[] intersectingTypes = subCandidate.getIntersectingTypes();
          if (intersectingTypes != null)
            for (int i = 0; i < intersectingTypes.length; i++)
              if (TypeBinding.equalsEquals(intersectingTypes[i], superCandidate)) return true;
        }
        WildcardBinding variable = (WildcardBinding) superCandidate;
        if (variable.boundKind == Wildcard.SUPER)
          return ConstraintTypeFormula.create(subCandidate, variable.bound, SUBTYPE, this.isSoft);
        return FALSE;
      case Binding.TYPE_PARAMETER:
        // similar to wildcard, but different queries for lower bound
        if (subCandidate.kind() == Binding.INTERSECTION_TYPE) {
          ReferenceBinding[] intersectingTypes = subCandidate.getIntersectingTypes();
          if (intersectingTypes != null)
            for (int i = 0; i < intersectingTypes.length; i++)
              if (TypeBinding.equalsEquals(intersectingTypes[i], superCandidate)) return true;
        }
        if (superCandidate instanceof CaptureBinding) {
          CaptureBinding capture = (CaptureBinding) superCandidate;
          if (capture.lowerBound != null
              && (capture.firstBound == null || capture.firstBound.id == TypeIds.T_JavaLangObject))
            return ConstraintTypeFormula.create(
                subCandidate, capture.lowerBound, SUBTYPE, this.isSoft);
        }
        return FALSE;
      case Binding.INTERSECTION_TYPE:
        superCandidate = ((WildcardBinding) superCandidate).allBounds();
        // $FALL-THROUGH$
      case Binding.INTERSECTION_TYPE18:
        TypeBinding[] intersectingTypes =
            ((IntersectionTypeBinding18) superCandidate).intersectingTypes;
        ConstraintFormula[] result = new ConstraintFormula[intersectingTypes.length];
        for (int i = 0; i < intersectingTypes.length; i++) {
          result[i] =
              ConstraintTypeFormula.create(
                  subCandidate, intersectingTypes[i], SUBTYPE, this.isSoft);
        }
        return result;
      case Binding.POLY_TYPE:
        PolyTypeBinding poly = (PolyTypeBinding) superCandidate;
        Invocation invocation = (Invocation) poly.expression;
        MethodBinding binding = invocation.binding();
        if (binding == null || !binding.isValidBinding()) return FALSE;
        TypeBinding returnType =
            binding.isConstructor() ? binding.declaringClass : binding.returnType;
        return reduceSubType(
            scope,
            subCandidate,
            returnType.capture(scope, invocation.sourceStart(), invocation.sourceEnd()));
    }
    throw new IllegalStateException("Unexpected RHS " + superCandidate); // $NON-NLS-1$
  }
  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;
    }
  }
  /** Perform inference of generic method type parameters and/or expected type */
  public static MethodBinding computeCompatibleMethod(
      MethodBinding originalMethod,
      TypeBinding[] arguments,
      Scope scope,
      InvocationSite invocationSite) {
    ParameterizedGenericMethodBinding methodSubstitute;
    TypeVariableBinding[] typeVariables = originalMethod.typeVariables;
    TypeBinding[] substitutes = invocationSite.genericTypeArguments();
    InferenceContext inferenceContext = null;
    TypeBinding[] uncheckedArguments = null;
    computeSubstitutes:
    {
      if (substitutes != null) {
        // explicit type arguments got supplied
        if (substitutes.length != typeVariables.length) {
          // incompatible due to wrong arity
          return new ProblemMethodBinding(
              originalMethod,
              originalMethod.selector,
              substitutes,
              ProblemReasons.TypeParameterArityMismatch);
        }
        methodSubstitute =
            scope.environment().createParameterizedGenericMethod(originalMethod, substitutes);
        break computeSubstitutes;
      }
      // perform type argument inference (15.12.2.7)
      // initializes the map of substitutes (var --> type[][]{ equal, extends, super}
      TypeBinding[] parameters = originalMethod.parameters;
      inferenceContext = new InferenceContext(originalMethod);
      methodSubstitute =
          inferFromArgumentTypes(scope, originalMethod, arguments, parameters, inferenceContext);
      if (methodSubstitute == null) return null;

      // substitutes may hold null to denote unresolved vars, but null arguments got replaced with
      // respective original variable in param method
      // 15.12.2.8 - inferring unresolved type arguments
      if (inferenceContext.hasUnresolvedTypeArgument()) {
        if (inferenceContext.isUnchecked) { // only remember unchecked status post 15.12.2.7
          int length = inferenceContext.substitutes.length;
          System.arraycopy(
              inferenceContext.substitutes,
              0,
              uncheckedArguments = new TypeBinding[length],
              0,
              length);
        }
        if (methodSubstitute.returnType != TypeBinding.VOID) {
          TypeBinding expectedType = invocationSite.expectedType();
          if (expectedType != null) {
            // record it was explicit from context, as opposed to assumed by default (see below)
            inferenceContext.hasExplicitExpectedType = true;
          } else {
            expectedType = scope.getJavaLangObject(); // assume Object by default
          }
          inferenceContext.expectedType = expectedType;
        }
        methodSubstitute = methodSubstitute.inferFromExpectedType(scope, inferenceContext);
        if (methodSubstitute == null) return null;
      }
    }

    /* bounds check: https://bugs.eclipse.org/bugs/show_bug.cgi?id=242159, Inferred types may contain self reference
       in formal bounds. If "T extends I<T>" is a original type variable and T was inferred to be I<T> due possibly
       to under constraints and resultant glb application per 15.12.2.8, using this.typeArguments to drive the bounds
       check against itself is doomed to fail. For, the variable T would after substitution be I<I<T>> and would fail
       bounds check against I<T>. Use the inferred types from the context directly - see that there is one round of
       extra substitution that has taken place to properly substitute a remaining unresolved variable which also appears
       in a formal bound  (So we really have a bounds mismatch between I<I<T>> and I<I<I<T>>>, in the absence of a fix.)
    */
    Substitution substitution = null;
    if (inferenceContext != null) {
      substitution =
          new LingeringTypeVariableEliminator(typeVariables, inferenceContext.substitutes, scope);
    } else {
      substitution = methodSubstitute;
    }
    for (int i = 0, length = typeVariables.length; i < length; i++) {
      TypeVariableBinding typeVariable = typeVariables[i];
      TypeBinding substitute = methodSubstitute.typeArguments[i]; // retain for diagnostics
      /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=375394, To avoid spurious bounds check failures due to circularity in formal bounds,
        we should eliminate only the lingering embedded type variable references after substitution, not alien type variable references
        that constitute the inference per se.
      */
      TypeBinding substituteForChecks;
      if (substitute instanceof TypeVariableBinding) {
        substituteForChecks = substitute;
      } else {
        substituteForChecks =
            Scope.substitute(
                new LingeringTypeVariableEliminator(typeVariables, null, scope),
                substitute); // while using this for bounds check
      }

      if (uncheckedArguments != null && uncheckedArguments[i] == null)
        continue; // only bound check if inferred through 15.12.2.6
      switch (typeVariable.boundCheck(substitution, substituteForChecks, scope)) {
        case TypeConstants.MISMATCH:
          // incompatible due to bound check
          int argLength = arguments.length;
          TypeBinding[] augmentedArguments =
              new TypeBinding[argLength + 2]; // append offending substitute and typeVariable
          System.arraycopy(arguments, 0, augmentedArguments, 0, argLength);
          augmentedArguments[argLength] = substitute;
          augmentedArguments[argLength + 1] = typeVariable;
          return new ProblemMethodBinding(
              methodSubstitute,
              originalMethod.selector,
              augmentedArguments,
              ProblemReasons.ParameterBoundMismatch);
        case TypeConstants.UNCHECKED:
          // tolerate unchecked bounds
          methodSubstitute.tagBits |= TagBits.HasUncheckedTypeArgumentForBoundCheck;
          break;
      }
    }
    // check presence of unchecked argument conversion a posteriori (15.12.2.6)
    return methodSubstitute;
  }