protected void checkReturnCompatibility(MethodSpec methodSpec) {
   Config.requireTypeAdjustment(); // reset flags
   // Note(SH): non-replace mappings have no return-dataflow (except for explicitly mapping
   // 'result' in after)
   if (isReplaceCallin()) // return for after/before is ignored.
   checkResultForReplace(methodSpec);
 }
  /** 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;
      }
    }
  }