*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
-import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.codegen.*;
public Expression expression;
public Expression type;
+ public TypeBinding expectedType; // when assignment conversion to a given expected type: String s = (String) t;
//expression.implicitConversion holds the cast for baseType casting
public CastExpression(Expression expression, Expression type) {
}
/**
- * Returns false if the cast is unnecessary
- */
- public final boolean checkCastTypesCompatibility(
- BlockScope scope,
- TypeBinding castType,
- TypeBinding expressionType) {
-
- // see specifications 5.5
- // handle errors and process constant when needed
-
- // if either one of the type is null ==>
- // some error has been already reported some where ==>
- // we then do not report an obvious-cascade-error.
-
- if (castType == null || expressionType == null) return true;
-
- // identity conversion cannot be performed upfront, due to side-effects
- // like constant propagation
-
- if (castType.isBaseType()) {
- if (expressionType.isBaseType()) {
- if (expressionType == castType) {
- expression.implicitWidening(castType, expressionType);
- constant = expression.constant; //use the same constant
- return false;
- }
- boolean necessary = false;
- if (expressionType.isCompatibleWith(castType)
- || (necessary = BaseTypeBinding.isNarrowing(castType.id, expressionType.id))) {
- expression.implicitConversion = (castType.id << 4) + expressionType.id;
- if (expression.constant != Constant.NotAConstant) {
- constant = expression.constant.castTo(expression.implicitConversion);
- }
- return necessary;
-
- }
- }
- scope.problemReporter().typeCastError(this, castType, expressionType);
- return true;
- }
-
- //-----------cast to something which is NOT a base type--------------------------
- if (expressionType == NullBinding) {
- // if (castType.isArrayType()){ // 26903 - need checkcast when casting null to array type
- // needRuntimeCheckcast = true;
- // }
- return false; //null is compatible with every thing
- }
- if (expressionType.isBaseType()) {
- scope.problemReporter().typeCastError(this, castType, expressionType);
- return true;
- }
-
- if (expressionType.isArrayType()) {
- if (castType == expressionType) return false; // identity conversion
-
- if (castType.isArrayType()) {
- //------- (castType.isArray) expressionType.isArray -----------
- TypeBinding exprElementType = ((ArrayBinding) expressionType).elementsType(scope);
- if (exprElementType.isBaseType()) {
- // <---stop the recursion-------
- if (((ArrayBinding) castType).elementsType(scope) == exprElementType) {
- this.bits |= NeedRuntimeCheckCastMASK;
- } else {
- scope.problemReporter().typeCastError(this, castType, expressionType);
- }
- return true;
- }
- // recursively on the elements...
- return checkCastTypesCompatibility(
- scope,
- ((ArrayBinding) castType).elementsType(scope),
- exprElementType);
- } else if (
- castType.isClass()) {
- //------(castType.isClass) expressionType.isArray ---------------
- if (castType.id == T_Object) {
- return false;
- }
- } else { //------- (castType.isInterface) expressionType.isArray -----------
- if (castType.id == T_JavaLangCloneable || castType.id == T_JavaIoSerializable) {
- this.bits |= NeedRuntimeCheckCastMASK;
- return true;
- }
- }
- scope.problemReporter().typeCastError(this, castType, expressionType);
- return true;
- }
-
- if (expressionType.isClass()) {
- if (castType.isArrayType()) {
- // ---- (castType.isArray) expressionType.isClass -------
- if (expressionType.id == T_Object) { // potential runtime error
- this.bits |= NeedRuntimeCheckCastMASK;
- return true;
- }
- } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isClass ------
- if (expressionType.isCompatibleWith(castType)){ // no runtime error
- if (castType.id == T_String) constant = expression.constant; // (String) cst is still a constant
- return false;
- }
- if (castType.isCompatibleWith(expressionType)) {
- // potential runtime error
- this.bits |= NeedRuntimeCheckCastMASK;
- return true;
- }
- } else { // ----- (castType.isInterface) expressionType.isClass -------
- if (expressionType.isCompatibleWith(castType))
- return false;
- if (!((ReferenceBinding) expressionType).isFinal()) {
- // a subclass may implement the interface ==> no check at compile time
- this.bits |= NeedRuntimeCheckCastMASK;
- return true;
- }
- // no subclass for expressionType, thus compile-time check is valid
- }
- scope.problemReporter().typeCastError(this, castType, expressionType);
- return true;
- }
-
- // if (expressionType.isInterface()) { cannot be anything else
- if (castType.isArrayType()) {
- // ----- (castType.isArray) expressionType.isInterface ------
- if (expressionType.id == T_JavaLangCloneable
- || expressionType.id == T_JavaIoSerializable) {// potential runtime error
- this.bits |= NeedRuntimeCheckCastMASK;
- } else {
- scope.problemReporter().typeCastError(this, castType, expressionType);
- }
- return true;
- } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isInterface --------
- if (castType.id == T_Object) { // no runtime error
- return false;
- }
- if (((ReferenceBinding) castType).isFinal()) {
- // no subclass for castType, thus compile-time check is valid
- if (!castType.isCompatibleWith(expressionType)) {
- // potential runtime error
- scope.problemReporter().typeCastError(this, castType, expressionType);
- return true;
- }
- }
- } else { // ----- (castType.isInterface) expressionType.isInterface -------
- if (expressionType.isCompatibleWith(castType)) {
- return false;
- }
- if (!castType.isCompatibleWith(expressionType)) {
- MethodBinding[] castTypeMethods = ((ReferenceBinding) castType).methods();
- MethodBinding[] expressionTypeMethods =
- ((ReferenceBinding) expressionType).methods();
- int exprMethodsLength = expressionTypeMethods.length;
- for (int i = 0, castMethodsLength = castTypeMethods.length; i < castMethodsLength; i++)
- for (int j = 0; j < exprMethodsLength; j++) {
- if ((castTypeMethods[i].returnType != expressionTypeMethods[j].returnType)
- && (CharOperation.equals(castTypeMethods[i].selector, expressionTypeMethods[j].selector))
- && castTypeMethods[i].areParametersEqual(expressionTypeMethods[j])) {
- scope.problemReporter().typeCastError(this, castType, expressionType);
- }
- }
- }
- }
- this.bits |= NeedRuntimeCheckCastMASK;
- return true;
- }
-
- /**
* Casting an enclosing instance will considered as useful if removing it would actually bind to a different type
*/
public static void checkNeedForEnclosingInstanceCast(BlockScope scope, Expression enclosingInstance, TypeBinding enclosingInstanceType, TypeBinding memberType) {
}
if (leftIsCast || rightIsCast) {
if (alternateLeftTypeId > 15 || alternateRightTypeId > 15) { // must convert String + Object || Object + String
- if (alternateLeftTypeId == T_String) {
- alternateRightTypeId = T_Object;
- } else if (alternateRightTypeId == T_String) {
- alternateLeftTypeId = T_Object;
+ if (alternateLeftTypeId == T_JavaLangString) {
+ alternateRightTypeId = T_JavaLangObject;
+ } else if (alternateRightTypeId == T_JavaLangString) {
+ alternateLeftTypeId = T_JavaLangObject;
} else {
return; // invalid operator
}
// <<16 <<12 <<8 <<4 <<0
final int CompareMASK = (0xF<<16) + (0xF<<8) + 0xF; // mask hiding compile-time types
if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result
- if (leftIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)left, TypeBinding.wellKnownType(scope, left.implicitConversion >> 4));
- if (rightIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)right, TypeBinding.wellKnownType(scope, right.implicitConversion >> 4));
+ if (leftIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)left, TypeBinding.wellKnownType(scope, (left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4));
+ if (rightIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)right, TypeBinding.wellKnownType(scope, (right.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4));
}
}
}
private static void checkAlternateBinding(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] originalArgumentTypes, TypeBinding[] alternateArgumentTypes, final InvocationSite invocationSite) {
InvocationSite fakeInvocationSite = new InvocationSite(){
+ public TypeBinding[] genericTypeArguments() { return null; }
public boolean isSuperAccess(){ return invocationSite.isSuperAccess(); }
public boolean isTypeAccess() { return invocationSite.isTypeAccess(); }
public void setActualReceiverType(ReferenceBinding actualReceiverType) { /* ignore */}
}
}
}
+
+ public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) {
+ if (match == castType) {
+ if (!isNarrowing) tagAsUnnecessaryCast(scope, castType);
+ return true;
+ }
+ if (castType.isBoundParameterizedType() || castType.isGenericType()) {
+ if (match.isProvablyDistinctFrom(isNarrowing ? expressionType : castType, 0)) {
+ reportIllegalCast(scope, castType, expressionType);
+ return false;
+ }
+ if (isNarrowing ? !expressionType.isEquivalentTo(match) : !match.isEquivalentTo(castType)) {
+ scope.problemReporter().unsafeCast(this);
+ return true;
+ }
+ if ((castType.tagBits & TagBits.HasDirectWildcard) == 0) {
+ if ((!match.isParameterizedType() && !match.isGenericType())
+ || expressionType.isRawType()) {
+ scope.problemReporter().unsafeCast(this);
+ return true;
+ }
+ }
+ }
+ if (!isNarrowing) tagAsUnnecessaryCast(scope, castType);
+ return true;
+ }
+
/**
* Cast expression code generation
*
BlockScope currentScope,
CodeStream codeStream,
boolean valueRequired) {
-
+
int pc = codeStream.position;
boolean needRuntimeCheckcast = (this.bits & NeedRuntimeCheckCastMASK) != 0;
if (constant != NotAConstant) {
codeStream.generateConstant(constant, implicitConversion);
if (needRuntimeCheckcast) {
codeStream.checkcast(this.resolvedType);
- if (!valueRequired)
+ if (valueRequired) {
+ codeStream.generateImplicitConversion(this.implicitConversion);
+ } else {
codeStream.pop();
+ }
}
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
valueRequired || needRuntimeCheckcast);
if (needRuntimeCheckcast) {
codeStream.checkcast(this.resolvedType);
- if (!valueRequired)
+ if (valueRequired) {
+ codeStream.generateImplicitConversion(implicitConversion);
+ } else {
codeStream.pop();
+ }
} else {
if (valueRequired)
codeStream.generateImplicitConversion(implicitConversion);
return current;
}
+ /**
+ * @see org.eclipse.jdt.internal.compiler.ast.Expression#localVariableBinding()
+ */
+ public LocalVariableBinding localVariableBinding() {
+ return this.expression.localVariableBinding();
+ }
+
+ public int nullStatus(FlowInfo flowInfo) {
+ return this.expression.nullStatus(flowInfo);
+ }
+
public StringBuffer printExpression(int indent, StringBuffer output) {
output.append('(');
return expression.printExpression(0, output);
}
+ public void reportIllegalCast(Scope scope, TypeBinding castType, TypeBinding expressionType) {
+ scope.problemReporter().typeCastError(this, castType, expressionType);
+ }
+
public TypeBinding resolveType(BlockScope scope) {
// compute a new constant if the cast is effective
&& ((type.bits & ASTNode.ParenthesizedMASK) >> ASTNode.ParenthesizedSHIFT) == 0) { // no extra parenthesis around type: ((A))exp
this.resolvedType = type.resolveType(scope);
+ expression.setExpectedType(this.resolvedType); // needed in case of generic method invocation
TypeBinding expressionType = expression.resolveType(scope);
if (this.resolvedType != null && expressionType != null) {
- boolean necessary = checkCastTypesCompatibility(scope, this.resolvedType, expressionType);
- if (!necessary && this.expression.resolvedType != null) { // cannot do better if expression is not bound
- this.bits |= UnnecessaryCastMask;
+ checkCastTypesCompatibility(scope, this.resolvedType, expressionType, this.expression);
+ this.expression.computeConversion(scope, this.resolvedType, expressionType);
+ if ((this.bits & UnnecessaryCastMask) != 0) {
if ((this.bits & IgnoreNeedForCastCheckMASK) == 0) {
- scope.problemReporter().unnecessaryCast(this);
+ if (!usedForGenericMethodReturnTypeInference()) // used for generic type inference ?
+ scope.problemReporter().unnecessaryCast(this);
}
}
}
return this.resolvedType;
- } else { // expression as a cast !!!!!!!!
+ } else { // expression as a cast
TypeBinding expressionType = expression.resolveType(scope);
if (expressionType == null) return null;
scope.problemReporter().invalidTypeReference(type);
return null;
}
}
+
+ /**
+ * @see org.eclipse.jdt.internal.compiler.ast.Expression#setExpectedType(org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
+ */
+ public void setExpectedType(TypeBinding expectedType) {
+ this.expectedType = expectedType;
+ }
+ /**
+ * Determines whether apparent unnecessary cast wasn't actually used to
+ * perform return type inference of generic method invocation.
+ */
+ private boolean usedForGenericMethodReturnTypeInference() {
+ if (this.expression instanceof MessageSend) {
+ MethodBinding method = ((MessageSend)this.expression).binding;
+ if (method instanceof ParameterizedGenericMethodBinding
+ && ((ParameterizedGenericMethodBinding)method).inferredReturnType) {
+ if (this.expectedType == null)
+ return true;
+ if (this.resolvedType != this.expectedType)
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @see org.eclipse.jdt.internal.compiler.ast.Expression#tagAsNeedCheckCast()
+ */
+ public void tagAsNeedCheckCast() {
+ this.bits |= NeedRuntimeCheckCastMASK;
+ }
+
+ /**
+ * @see org.eclipse.jdt.internal.compiler.ast.Expression#tagAsUnnecessaryCast(Scope, TypeBinding)
+ */
+ public void tagAsUnnecessaryCast(Scope scope, TypeBinding castType) {
+ if (this.expression.resolvedType == null) return; // cannot do better if expression is not bound
+ this.bits |= UnnecessaryCastMask;
+ }
+
public void traverse(
ASTVisitor visitor,
BlockScope blockScope) {