Example #1
0
  /**
   * This method is used by {@link
   * org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.CallinMethodMappingsAttribute
   * CallinMethodMappingsAttribute} for decoding a mapping from its bytecode attribute.
   *
   * @param method
   * @return the bytecode level signature of the given method (yet retrenched)
   */
  public static char[] signature(MethodBinding method, WeavingScheme weavingScheme) {

    StringBuffer buffer = new StringBuffer();
    buffer.append('(');
    // synthetic args for static role method?
    MethodBinding orig =
        method.copyInheritanceSrc != null
            ? method.copyInheritanceSrc
            : method; // normalize to copyInhSrc so reading a callin-attr. from bytes can more
    // easily find the method
    if (weavingScheme == WeavingScheme.OTDRE && orig.declaringClass.isRole() && orig.isStatic()) {
      buffer.append('I');
      buffer.append(String.valueOf(orig.declaringClass.enclosingType().signature()));
    }
    // manual retrenching?
    boolean shouldRetrench = weavingScheme == WeavingScheme.OTRE && method.isCallin();
    int offset = shouldRetrench ? MethodSignatureEnhancer.getEnhancingArgLen(weavingScheme) : 0;
    int paramLen = method.parameters.length;
    for (int i = offset; i < paramLen; i++) {
      // 'weaken' to that erasure that was used in the tsuper version:
      TypeBinding targetParameter = method.getCodeGenType(i);
      buffer.append(targetParameter.signature());
    }
    buffer.append(')');
    TypeBinding sourceReturnType =
        shouldRetrench ? MethodModel.getReturnType(method) : method.returnType;
    // 'weaken' to that erasure that was used in the tsuper version:
    MethodBinding tsuperOriginal =
        (method.tagBits & TagBits.IsCopyOfParameterized) != 0
            ? method.copyInheritanceSrc.original()
            : null;
    TypeBinding returnType =
        (tsuperOriginal != null
                && tsuperOriginal.returnType.isTypeVariable()
                && !sourceReturnType.isTypeVariable())
            ? tsuperOriginal.returnType
            : sourceReturnType;
    if (returnType.isTypeVariable() && method instanceof ParameterizedGenericMethodBinding)
      returnType =
          ((ParameterizedGenericMethodBinding) method)
              .reverseSubstitute((TypeVariableBinding) returnType);
    buffer.append(returnType.erasure().signature());
    int nameLength = buffer.length();
    char[] signature = new char[nameLength];
    buffer.getChars(0, nameLength, signature, 0);

    return signature;
  }
Example #2
0
  /**
   * If 'hasSignature' check the return type of the bound method against the declared return type.
   */
  public boolean checkRoleReturnType(CallinCalloutScope scope, boolean isCallout) {
    TypeBinding methodReturn = boundMethodReturnType();
    TypeBinding resolvedReturnType = this.returnType.resolvedType;
    TypeBinding firstBound = null;
    if (resolvedReturnType.isTypeVariable()) {
      firstBound = ((TypeVariableBinding) resolvedReturnType).firstBound;

      // if declared return type is a type variable, so must the return type of the resolved method:
      if (!isMethodReturnTypeVariable(this.resolvedMethod)) {
        scope.problemReporter().differentReturnInMethodSpec(this, /*isCallout*/ false);
        return false;
      }
    }
    // the role side of a callout may indeed refine the return type
    // of its inherited role method:
    if (isCallout) if (this.returnType.resolvedType.isCompatibleWith(methodReturn)) return true;
    // in other cases types have to be identical:
    if (!MethodModel.hasUnboundedReturnType(
            this.resolvedMethod) // unbounded type variable always matches
        && !TypeAnalyzer.isSameType(scope.enclosingSourceType(), resolvedReturnType, methodReturn)
        && !TypeAnalyzer.isSameType(scope.enclosingSourceType(), firstBound, methodReturn)) {
      scope
          .problemReporter()
          .differentReturnInMethodSpec(
              this, ((AbstractMethodMappingDeclaration) scope.referenceContext).isCallout());
      return false;
    }
    return true;
  }
 private boolean typeUsesTypeVariable(TypeBinding type, TypeBinding variable) {
   if (TypeBinding.equalsEquals(type.leafComponentType(), variable)) return true;
   for (TypeVariableBinding t : type.typeVariables())
     if (typeUsesTypeVariable(t, variable)) return true;
   if (type.isTypeVariable()) {
     if (typeUsesTypeVariable(((ReferenceBinding) type).superclass(), variable)) return true;
     for (TypeBinding superIfc : ((ReferenceBinding) type).superInterfaces())
       if (typeUsesTypeVariable(superIfc, variable)) return true;
   }
   return false;
 }
  protected boolean isBoxingCompatible(
      TypeBinding expressionType, TypeBinding targetType, Expression expression, Scope scope) {
    if (scope.isBoxingCompatibleWith(expressionType, targetType)) return true;

    return expressionType
            .isBaseType() // narrowing then boxing ? Only allowed for some target types see 362279
        && !targetType.isBaseType()
        && !targetType.isTypeVariable()
        && scope.compilerOptions().sourceLevel
            >= org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.JDK1_5 // autoboxing
        && (targetType.id == TypeIds.T_JavaLangByte
            || targetType.id == TypeIds.T_JavaLangShort
            || targetType.id == TypeIds.T_JavaLangCharacter)
        && expression.isConstantValueOfTypeAssignableToType(
            expressionType, scope.environment().computeBoxingType(targetType));
  }
  // 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;
  }
Example #6
0
  public TypeBinding resolveSuperType(ClassScope scope) {
    // assumes the implementation of resolveType(ClassScope) will call back to detect cycles
    TypeBinding superType = resolveType(scope);
    if (superType == null) return null;

    if (superType.isTypeVariable()) {
      if (this.resolvedType.isValidBinding()) {
        this.resolvedType =
            new ProblemReferenceBinding(
                getTypeName(),
                (ReferenceBinding) this.resolvedType,
                ProblemReasons.IllegalSuperTypeVariable);
        reportInvalidType(scope);
      }
      return null;
    }
    return superType;
  }
  // interpreting 'type' as a required type, compute the required null bits
  // we inspect the main type plus bounds of type variables and wildcards
  static long requiredNullTagBits(TypeBinding type) {

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

    if (type.isWildcard()) {
      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; // wildcard accepts @Nullable or better
        case Wildcard.SUPER:
          if (tagBits == TagBits.AnnotationNullable) return TagBits.AnnotationNullable;
          return TagBits.AnnotationNullMASK; // wildcard accepts @NonNull or worse
      }
      return 0;
    }

    if (type.isTypeVariable()) {
      // assume we must require @NonNull, unless: (1) lower @Nullable bound, or (2) no nullness
      // specified
      TypeVariableBinding typeVariable = (TypeVariableBinding) type;
      boolean haveNullBits = false;
      if (type.isCapture()) {
        TypeBinding lowerBound = ((CaptureBinding) type).lowerBound;
        if (lowerBound != null) {
          tagBits = lowerBound.tagBits & TagBits.AnnotationNullMASK;
          if (tagBits == TagBits.AnnotationNullable)
            return TagBits.AnnotationNullable; // (1) type cannot require @NonNull
          haveNullBits = tagBits != 0;
        }
      }
      if (typeVariable.firstBound != null)
        haveNullBits |= (typeVariable.firstBound.tagBits & TagBits.AnnotationNullMASK) != 0;
      if (haveNullBits)
        return TagBits
            .AnnotationNonNull; // could require @NonNull (unless (2) unspecified nullness)
    }

    return 0;
  }
Example #8
0
 public boolean checkUnsafeCast(
     Scope scope,
     TypeBinding castType,
     TypeBinding expressionType,
     TypeBinding match,
     boolean isNarrowing) {
   if (match == castType) {
     if (!isNarrowing
         && match
             == this.resolvedType
                 .leafComponentType()) { // do not tag as unnecessary when recursing through upper
       // bounds
       tagAsUnnecessaryCast(scope, castType);
     }
     return true;
   }
   if (match != null) {
     if (isNarrowing
         ? match.isProvablyDistinct(expressionType)
         : castType.isProvablyDistinct(match)) {
       return false;
     }
   }
   switch (castType.kind()) {
     case Binding.PARAMETERIZED_TYPE:
       if (castType.isBoundParameterizedType()) {
         if (match == null) { // unrelated types
           this.bits |= ASTNode.UnsafeCast;
           return true;
         }
         switch (match.kind()) {
           case Binding.PARAMETERIZED_TYPE:
             if (isNarrowing) {
               // [JLS 5.5] T <: S
               if (expressionType.isRawType() || !expressionType.isEquivalentTo(match)) {
                 this.bits |= ASTNode.UnsafeCast;
                 return true;
               }
               // [JLS 5.5] S has no subtype X != T, such that |X| == |T|
               // if I2<T,U> extends I1<T>, then cast from I1<T> to I2<T,U> is unchecked
               ParameterizedTypeBinding paramCastType = (ParameterizedTypeBinding) castType;
               ParameterizedTypeBinding paramMatch = (ParameterizedTypeBinding) match;
               // easy case if less parameters on match
               TypeBinding[] castArguments = paramCastType.arguments;
               int length = castArguments.length;
               if (paramMatch.arguments == null || length > paramMatch.arguments.length) {
                 this.bits |= ASTNode.UnsafeCast;
               } else if ((paramCastType.tagBits
                       & (TagBits.HasDirectWildcard | TagBits.HasTypeVariable))
                   != 0) {
                 // verify alternate cast type, substituting different type arguments
                 nextAlternateArgument:
                 for (int i = 0; i < length; i++) {
                   switch (castArguments[i].kind()) {
                     case Binding.WILDCARD_TYPE:
                     case Binding.TYPE_PARAMETER:
                       break; // check substituting with other
                     default:
                       continue nextAlternateArgument; // no alternative possible
                   }
                   TypeBinding[] alternateArguments;
                   // need to clone for each iteration to avoid env paramtype cache interference
                   System.arraycopy(
                       paramCastType.arguments,
                       0,
                       alternateArguments = new TypeBinding[length],
                       0,
                       length);
                   alternateArguments[i] = scope.getJavaLangObject();
                   LookupEnvironment environment = scope.environment();
                   ParameterizedTypeBinding alternateCastType =
                       environment.createParameterizedType(
                           (ReferenceBinding) castType.erasure(),
                           alternateArguments,
                           castType.enclosingType());
                   if (alternateCastType.findSuperTypeOriginatingFrom(expressionType) == match) {
                     this.bits |= ASTNode.UnsafeCast;
                     break;
                   }
                 }
               }
               return true;
             } else {
               // [JLS 5.5] T >: S
               if (!match.isEquivalentTo(castType)) {
                 this.bits |= ASTNode.UnsafeCast;
                 return true;
               }
             }
             break;
           case Binding.RAW_TYPE:
             this.bits |=
                 ASTNode.UnsafeCast; // upcast since castType is known to be bound paramType
             return true;
           default:
             if (isNarrowing) {
               // match is not parameterized or raw, then any other subtype of match will erase  to
               // |T|
               this.bits |= ASTNode.UnsafeCast;
               return true;
             }
             break;
         }
       }
       break;
     case Binding.ARRAY_TYPE:
       TypeBinding leafType = castType.leafComponentType();
       if (isNarrowing && (leafType.isBoundParameterizedType() || leafType.isTypeVariable())) {
         this.bits |= ASTNode.UnsafeCast;
         return true;
       }
       break;
     case Binding.TYPE_PARAMETER:
       this.bits |= ASTNode.UnsafeCast;
       return true;
   }
   if (!isNarrowing
       && match
           == this.resolvedType
               .leafComponentType()) { // do not tag as unnecessary when recursing through upper
     // bounds
     tagAsUnnecessaryCast(scope, castType);
   }
   return true;
 }
  /** Check whether the baseSpec has a result compatible via replace. */
  public void checkResultForReplace(MethodSpec baseSpec) {
    boolean typeIdentityRequired = true; // default unless return is type variable
    // covariant return requires a fresh type parameter for the role's return type:
    if (baseSpec.covariantReturn && this.roleMethodSpec.returnType != null) {
      TypeBinding resolvedRoleReturn = this.roleMethodSpec.returnType.resolvedType;
      if (resolvedRoleReturn != null) {
        if (!resolvedRoleReturn.isTypeVariable()) {
          this.scope
              .problemReporter()
              .covariantReturnRequiresTypeParameter(this.roleMethodSpec.returnType);
          this.binding.tagBits |= TagBits.HasMappingIncompatibility;
        } else {
          // is the type parameter "fresh"?
          for (Argument arg : this.roleMethodSpec.arguments) {
            if (typeUsesTypeVariable(
                arg.type.resolvedType.leafComponentType(), resolvedRoleReturn)) {
              this.scope
                  .problemReporter()
                  .duplicateUseOfTypeVariableInCallin(
                      this.roleMethodSpec.returnType, resolvedRoleReturn);
              this.binding.tagBits |= TagBits.HasMappingIncompatibility;
              break;
            }
          }
        }
      }
    }
    TypeVariableBinding returnVariable =
        MethodModel.checkedGetReturnTypeVariable(this.roleMethodSpec.resolvedMethod);
    if (returnVariable != null) {
      // unbounded type variable always matches:
      if (returnVariable.firstBound == null) return;
      // in case of type variable only one-way compatibility is needed even for replace:
      typeIdentityRequired = false;
    }

    // now go for the actual type checking:
    TypeBinding baseReturn = baseSpec.resolvedMethod.returnType;
    TypeBinding roleReturn = MethodModel.getReturnType(this.roleMethodSpec.resolvedMethod);
    TypeBinding roleReturnLeaf = roleReturn != null ? roleReturn.leafComponentType() : null;
    if (roleReturnLeaf instanceof ReferenceBinding
        && ((ReferenceBinding) roleReturnLeaf).isRole()) {
      // strengthen:
      roleReturnLeaf =
          TeamModel.strengthenRoleType(this.scope.enclosingSourceType(), roleReturnLeaf);
      if (roleReturnLeaf == null) { // FIXME(SH): testcase and better handling
        String roleReturnName =
            roleReturn != null
                ? new String(roleReturn.readableName())
                : "null return type"; //$NON-NLS-1$
        throw new InternalCompilerError(
            "role strengthening for " + roleReturnName + " -> null"); // $NON-NLS-1$ //$NON-NLS-2$
      }

      // bound roles use their topmost bound super:
      if (((ReferenceBinding) roleReturnLeaf).baseclass() != null)
        roleReturnLeaf =
            RoleModel.getTopmostBoundRole(this.scope, (ReferenceBinding) roleReturnLeaf);

      // need the RTB:
      if (!(roleReturnLeaf instanceof DependentTypeBinding))
        roleReturnLeaf =
            RoleTypeCreator.maybeWrapUnqualifiedRoleType(
                roleReturnLeaf, this.scope.enclosingSourceType());

      // array?
      int dims = roleReturn != null ? roleReturn.dimensions() : 0;
      if (dims == 0) {
        roleReturn = roleReturnLeaf;
        this.realRoleReturn = roleReturnLeaf;
      } else {
        roleReturn = ((DependentTypeBinding) roleReturnLeaf).getArrayType(dims);
        this.realRoleReturn = ((DependentTypeBinding) roleReturnLeaf).getArrayType(dims);
      }
    }
    if (baseReturn == null || baseReturn == TypeBinding.VOID) {
      // OTJLD 4.4(b): "A callin method bound with replace
      //                to a base method returning void
      //                must not declare a non-void result."
      if (!(roleReturn == null || roleReturn == TypeBinding.VOID)) {
        this.scope.problemReporter().callinIllegalRoleReturnReturn(baseSpec, this.roleMethodSpec);
        this.binding.tagBits |= TagBits.HasMappingIncompatibility;
      }
    } else {
      if (roleReturn == null || roleReturn == TypeBinding.VOID) {
        this.baseMethodNeedingResultFromBasecall = baseSpec;
        // will be reported in checkBaseResult().
        return;
      }

      TypeBinding baseLeaf = baseReturn.leafComponentType();
      if (baseLeaf instanceof DependentTypeBinding) {
        // instantiate relative to Role._OT$base:
        ReferenceBinding enclosingRole = this.scope.enclosingSourceType();
        FieldBinding baseField = enclosingRole.getField(IOTConstants._OT_BASE, true);
        if (baseField != null && baseField.isValidBinding())
          baseReturn =
              baseField.getRoleTypeBinding((ReferenceBinding) baseLeaf, baseReturn.dimensions());
      }

      // check auto(un)boxing:
      if (this.scope.isBoxingCompatibleWith(roleReturn, baseReturn)) return;

      Config oldConfig = Config.createOrResetConfig(this);
      try {
        if (!roleReturn.isCompatibleWith(baseReturn)) {
          if (typeIdentityRequired) {
            this.scope
                .problemReporter()
                .callinIncompatibleReturnType(baseSpec, this.roleMethodSpec);
            this.binding.tagBits |= TagBits.HasMappingIncompatibility;
            return;
          }
          // else we still needed the lowering test
        }
        // callin replace requires two way compatibility:
        baseSpec.returnNeedsTranslation = Config.getLoweringRequired();

      } finally {
        Config.removeOrRestore(oldConfig, this);
      }
      // from now on don't bother with arrays any more (dimensions have been checked):
      roleReturn = roleReturn.leafComponentType();
      baseReturn = baseReturn.leafComponentType();
      TypeBinding translatedReturn =
          baseSpec.returnNeedsTranslation
              ? ((ReferenceBinding) roleReturn).baseclass()
              : roleReturn;
      if (translatedReturn.isTypeVariable()) {
        TypeBinding firstBound = ((TypeVariableBinding) translatedReturn).firstBound;
        if (firstBound != null) translatedReturn = firstBound;
      }
      if (!baseReturn.isCompatibleWith(translatedReturn)) {
        this.scope
            .problemReporter()
            .callinIncompatibleReturnTypeBaseCall(baseSpec, this.roleMethodSpec);
        this.binding.tagBits |= TagBits.HasMappingIncompatibility;
      }
    }
  }
  /**
   * Check all parameters in methodSpec against the resolved role method. Also record which
   * parameters (including result) need translation (lifting/lowering).
   *
   * <p>Pre: not called if parameter mappings are present.
   *
   * @param methodSpec
   */
  protected boolean internalCheckParametersCompatibility(
      MethodSpec methodSpec, TypeBinding[] roleParams, TypeBinding[] baseParams) {
    if (baseParams.length < roleParams.length) {
      this.scope
          .problemReporter()
          .tooFewArgumentsInMethodMapping(this.roleMethodSpec, methodSpec, false /*callout*/);
      this.binding.tagBits |= TagBits.HasMappingIncompatibility;
      return false;
    } else {
      // before modifying the parameters array copy it:
      System.arraycopy(
          this.roleMethodSpec.parameters,
          0,
          this.roleMethodSpec.parameters = new TypeBinding[roleParams.length],
          0,
          roleParams.length);
      for (int j = 0; j < roleParams.length; j++) {
        TypeBinding baseParam = baseParams[j];
        TypeBinding roleParam = roleParams[j];
        if (baseParam.dimensions() != roleParam.dimensions()) {
          this.scope
              .problemReporter()
              .incompatibleMappedArgument(
                  baseParam, roleParam, this.roleMethodSpec, j, /*callout*/ false);
          this.binding.tagBits |= TagBits.HasMappingIncompatibility;
          continue; // no real type checking needed.
        }
        TypeBinding baseLeaf = baseParam.leafComponentType();
        TypeBinding roleLeaf = roleParam.leafComponentType();
        ASTNode location =
            (methodSpec.hasSignature) ? (ASTNode) methodSpec.arguments[j] : methodSpec;
        boolean compatibilityViaBaseAnchor = false;
        boolean hasReportedError = false;
        boolean isTypeVariable = false;
        try { // capture continue exits

          // unbound type variable matches everything:
          if (roleParam.isTypeVariable()) {
            TypeVariableBinding typeVariableBinding = (TypeVariableBinding) roleParam;
            if (typeVariableBinding.firstBound == null) continue;
            // use bound for type checking below, yet need not check two-way compatibility:
            isTypeVariable = true;
            roleLeaf = typeVariableBinding.firstBound.leafComponentType();
          }

          int dimensions = roleParam.dimensions();
          if (baseLeaf.isCompatibleWith(roleLeaf)) {
            this.roleMethodSpec.parameters[j] = roleParam;
            continue;
          }
          if (RoleTypeCreator.isCompatibleViaBaseAnchor(
              this.scope, baseLeaf, roleLeaf, TokenNameBINDIN)) {
            this.roleMethodSpec.parameters[j] = roleParam;
            compatibilityViaBaseAnchor = true;
            continue;
          }

          TypeBinding roleToLiftTo = null;
          if (isReplaceCallin()) {
            TypeBinding roleSideType = roleLeaf;
            if (roleSideType.isRole()) {
              ReferenceBinding roleRef = (ReferenceBinding) roleSideType;
              roleRef =
                  (ReferenceBinding)
                      TeamModel.strengthenRoleType(this.scope.enclosingReceiverType(), roleRef);
              if (TypeBinding.equalsEquals(roleRef.baseclass(), baseLeaf)) {
                if (dimensions > 0) {
                  if (roleRef instanceof DependentTypeBinding)
                    roleToLiftTo = ((DependentTypeBinding) roleRef).getArrayType(dimensions);
                  else
                    roleToLiftTo =
                        this.scope.createArrayType(roleRef, dimensions); // FIXME(SH): is this OK?
                } else {
                  roleToLiftTo = roleRef;
                }
              }
            }
          } else {
            // this uses OTJLD 2.3.3(a) adaptation which is not reversible, ie., not usable for
            // replace:
            roleToLiftTo =
                TeamModel.getRoleToLiftTo(this.scope, baseParam, roleParam, false, location);
          }
          if (roleToLiftTo != null) {
            // success by translation
            methodSpec.argNeedsTranslation[j] = true;
            this.roleMethodSpec.argNeedsTranslation[j] = true;
            this.roleMethodSpec.parameters[j] = roleToLiftTo; // this applies to all bindings

            // still need to check for ambiguity/abstract role:
            ReferenceBinding enclosingTeam = this.scope.enclosingSourceType().enclosingType();
            int iProblem =
                enclosingTeam
                    .getTeamModel()
                    .canLiftingFail((ReferenceBinding) roleToLiftTo.leafComponentType());
            if (iProblem > 0)
              addRoleLiftingProblem((ReferenceBinding) roleToLiftTo.leafComponentType(), iProblem);

            continue;
          }
          // check auto(un)boxing:
          if (this.scope.isBoxingCompatibleWith(baseLeaf, roleLeaf)) continue;

          if (roleParam instanceof ReferenceBinding) {
            ReferenceBinding roleRef = (ReferenceBinding) roleParam;
            if (roleRef.isRole() && roleRef.baseclass() != null) {
              this.scope
                  .problemReporter()
                  .typeMismatchErrorPotentialLift(
                      location, baseParam, roleParam, roleRef.baseclass());
              hasReportedError = true;
              continue;
            }
          }
          // no compatibility detected:
          this.scope
              .problemReporter()
              .incompatibleMappedArgument(
                  baseParam, roleParam, this.roleMethodSpec, j, /*callout*/ false);
          hasReportedError = true;
        } finally {
          if (hasReportedError) this.binding.tagBits |= TagBits.HasMappingIncompatibility;
          // regardless of continue, check this last because it is the least precise message:
          if (!hasReportedError && baseLeaf.isCompatibleWith(roleLeaf)) {
            if (isReplaceCallin() && !isTypeVariable) {
              boolean twowayCompatible =
                  compatibilityViaBaseAnchor
                      ? RoleTypeCreator.isCompatibleViaBaseAnchor(
                          this.scope, baseLeaf, roleLeaf, TokenNameBINDOUT)
                      : roleLeaf.isCompatibleWith(baseLeaf);
              if (!twowayCompatible) {
                // requires two-way compatibility (see additional paragraph in 4.5(d))
                this.scope
                    .problemReporter()
                    .typesNotTwowayCompatibleInReplace(baseParam, roleParam, location, j);
              }
            }
          }
        }
      }
    }
    return true; // unused in the callin case
  }
  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);
    }
  }
  public TypeBinding resolveType(BlockScope scope) {

    this.constant = Constant.NotAConstant;
    if ((this.targetType = this.type.resolveType(scope, true /* check bounds*/)) == null)
      return null;

    /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=320463
      https://bugs.eclipse.org/bugs/show_bug.cgi?id=312076
      JLS3 15.8.2 forbids the type named in the class literal expression from being a parameterized type.
      And the grammar in 18.1 disallows (where X and Y are some concrete types) constructs of the form
      Outer<X>.class, Outer<X>.Inner.class, Outer.Inner<X>.class, Outer<X>.Inner<Y>.class etc.
      Corollary wise, we should resolve the type of the class literal expression to be a raw type as
      class literals exist only for the raw underlying type.
    */
    LookupEnvironment environment = scope.environment();
    this.targetType =
        environment.convertToRawType(
            this.targetType, true /* force conversion of enclosing types*/);

    if (this.targetType.isArrayType()) {
      ArrayBinding arrayBinding = (ArrayBinding) this.targetType;
      TypeBinding leafComponentType = arrayBinding.leafComponentType;
      if (leafComponentType == TypeBinding.VOID) {
        scope.problemReporter().cannotAllocateVoidArray(this);
        return null;
      } else if (leafComponentType.isTypeVariable()) {
        scope
            .problemReporter()
            .illegalClassLiteralForTypeVariable((TypeVariableBinding) leafComponentType, this);
      }
    } else if (this.targetType.isTypeVariable()) {
      scope
          .problemReporter()
          .illegalClassLiteralForTypeVariable((TypeVariableBinding) this.targetType, this);
    }
    // {ObjectTeams: do we need a RoleClassLiteralAccess?
    if (this.targetType instanceof ReferenceBinding) {
      ReferenceBinding targetRef = (ReferenceBinding) this.targetType;
      if (targetRef.isRole()) {
        if (this.verbatim) {
          this.targetType =
              RoleTypeCreator.maybeWrapUnqualifiedRoleType(scope, this.targetType, this);
        } else {
          SourceTypeBinding site = scope.enclosingSourceType();
          if (scope.methodScope().isStatic // role class literal needs team instance
              && !site.isRole() // static role method are OK.
              && !RoleTypeBinding.isRoleWithExplicitAnchor(this.targetType)) // t.R.class?
          {
            scope.problemReporter().roleClassLiteralLacksTeamInstance(this, targetRef);
            return null;
          }
          ReferenceBinding teamBinding;
          if (RoleTypeBinding.isRoleWithExplicitAnchor(targetRef))
            teamBinding = targetRef.enclosingType();
          else teamBinding = TeamModel.findEnclosingTeamContainingRole(site, targetRef);
          if (teamBinding == null)
            scope.problemReporter().externalizedRoleClassLiteral(this, targetRef);
          else {
            TypeBinding methodType =
                RoleClassLiteralAccess.ensureGetClassMethod(
                    teamBinding.getTeamModel(),
                    targetRef.roleModel); // not affected by visibility check (for resilience)
            this.roleClassLiteralAccess = new RoleClassLiteralAccess(this, methodType);
            this.resolvedType = this.roleClassLiteralAccess.resolveType(scope);
          }
          return this.resolvedType;
        }
      }
    }
    // SH}
    ReferenceBinding classType = scope.getJavaLangClass();
    // https://bugs.eclipse.org/bugs/show_bug.cgi?id=328689
    if (scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
      // Integer.class --> Class<Integer>, perform boxing of base types (int.class -->
      // Class<Integer>)
      TypeBinding boxedType = null;
      if (this.targetType.id == T_void) {
        boxedType = environment.getResolvedType(JAVA_LANG_VOID, scope);
      } else {
        boxedType = scope.boxing(this.targetType);
      }
      if (environment.usesNullTypeAnnotations())
        boxedType =
            environment.createAnnotatedType(
                boxedType, new AnnotationBinding[] {environment.getNonNullAnnotation()});
      this.resolvedType =
          environment.createParameterizedType(
              classType, new TypeBinding[] {boxedType}, null /*not a member*/);
    } else {
      this.resolvedType = classType;
    }
    return this.resolvedType;
  }