/** Check OTJLD 4.4(b) "Callin parameter mapping / Restrictions for callin replace bindings" */
 public void checkResultMapping() {
   // for replace callins, a "result" mapping is not allowed,
   // unless an expected result is otherwise missing.
   if (this.mappings == null) return;
   for (MethodSpec baseSpec : this.baseMethodSpecs) {
     for (int i = 0; i < this.mappings.length; i++) {
       if (CharOperation.equals(this.mappings[i].ident.token, IOTConstants.RESULT)) {
         this.isResultMapped = true;
         // OTJLD 4.4(b): "If the base method declares a result, then ...
         if (baseSpec.resolvedType() != TypeBinding.VOID) {
           //                * if the role method also declares a result,
           if (this.roleMethodSpec.resolvedType() != TypeBinding.VOID) {
             Expression resultExpr = this.mappings[i].expression;
             //                  => result must be mapped to itself
             if (!(resultExpr instanceof ResultReference)) {
               this.scope.problemReporter().nonResultExpressionInReplaceResult(resultExpr);
               this.binding.tagBits |= TagBits.HasMappingIncompatibility;
             }
           } // no else because:
           //                * if the role method does not declare a result,
           //                  an arbitrary expression may be mapped to result
         } else {
           this.scope
               .problemReporter()
               .resultMappingForVoidMethod(this, baseSpec, this.mappings[i]);
           this.binding.tagBits |= TagBits.HasMappingIncompatibility;
         }
       }
     }
   }
 }
 public StringBuffer printShort(int indent, StringBuffer output, MethodSpec baseMethodSpec) {
   printIndent(indent, output);
   this.roleMethodSpec.print(0, output);
   output.append(getCallinModifier());
   baseMethodSpec.print(0, output);
   return output;
 }
 /** Get incoming arguments that are not used by the role side. */
 public int[] getUnmappedBasePositions(MethodSpec baseSpec) {
   int baseArgCount =
       baseSpec.resolvedParameters().length; // role-as-base enhancement filtered out
   int[] result = new int[baseArgCount];
   int idx = 0;
   for (int i = 0; i < baseArgCount; i++) {
     if (!isMapped(i)) result[idx++] = i;
   }
   if (idx < baseArgCount) {
     System.arraycopy(result, 0, result = new int[idx], 0, idx);
   }
   return result;
 }
 /**
  * Get the expression that is being mapped to "result" Precondition: parameter mappings are
  * present.
  *
  * @return expression (in this expression "result" should be a legal name).
  */
 public Expression getResultExpression(
     MethodSpec baseMethodSpec, boolean needBoxing, AstGenerator gen) {
   if (baseMethodSpec.resolvedType() == TypeBinding.VOID)
     // binding non-void to void: just return "result" which will be ignored any way.
     return new SingleNameReference(
         IOTConstants.RESULT,
         (((long) this.roleMethodSpec.sourceStart) << 32) + this.roleMethodSpec.sourceEnd);
   Expression resultExpr = null;
   for (int i = 0; i < this.mappings.length; i++) {
     if (this.mappings[i].isUsedFor(baseMethodSpec)) // already used mapping?
     continue;
     if (CharOperation.equals(this.mappings[i].ident.token, IOTConstants.RESULT)) {
       if (resultExpr != null) {
         this.scope
             .problemReporter()
             .duplicateParamMapping(this.mappings[i], IOTConstants.RESULT, /*isCallout*/ false);
         this.binding.tagBits |= TagBits.HasMappingIncompatibility;
       } else {
         resultExpr = this.mappings[i].expression;
       }
     }
   }
   if (resultExpr != null) {
     // check undefined 'result' expression:
     if (this.roleMethodSpec.resolvedType() == TypeBinding.VOID
         && (resultExpr instanceof ResultReference)) {
       this.scope
           .problemReporter()
           .resultNotDefinedForVoidMethod(
               resultExpr, this.roleMethodSpec.selector, false /*callout*/);
       this.binding.tagBits |= TagBits.HasMappingIncompatibility;
     }
   }
   if (resultExpr != null
       && !this.isResultMapped // in param-mapping role-side "result" already has boxed type
       && needBoxing)
     resultExpr = gen.createBoxing(resultExpr, (BaseTypeBinding) baseMethodSpec.resolvedType());
   return resultExpr;
 }
 public boolean hasStaticBaseMethod() {
   for (MethodSpec spec : this.baseMethodSpecs) if (spec.isStatic()) return true;
   return false;
 }
  /** 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;
      }
    }
  }