import eclipse 3.1 M4 compiler
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / CastExpression.java
index ecfe767..79e4922 100644 (file)
@@ -11,7 +11,6 @@
  *******************************************************************************/
 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.*;
@@ -23,6 +22,7 @@ public class CastExpression extends Expression {
 
        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) {
@@ -54,172 +54,6 @@ public class CastExpression extends Expression {
        }
 
        /**
-        * 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) {
@@ -363,10 +197,10 @@ public class CastExpression extends Expression {
                }
                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
                                }
@@ -377,8 +211,8 @@ public class CastExpression extends Expression {
                        //  <<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));
                        }
                }
        }
@@ -386,6 +220,7 @@ public class CastExpression extends Expression {
        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 */}
@@ -410,6 +245,33 @@ public class CastExpression extends Expression {
                                }
                        }       
        }
+       
+       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
         *
@@ -421,7 +283,7 @@ public class CastExpression extends Expression {
                BlockScope currentScope,
                CodeStream codeStream,
                boolean valueRequired) {
-
+       
                int pc = codeStream.position;
                boolean needRuntimeCheckcast = (this.bits & NeedRuntimeCheckCastMASK) != 0;
                if (constant != NotAConstant) {
@@ -429,8 +291,11 @@ public class CastExpression extends Expression {
                                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);
@@ -442,8 +307,11 @@ public class CastExpression extends Expression {
                        valueRequired || needRuntimeCheckcast);
                if (needRuntimeCheckcast) {
                        codeStream.checkcast(this.resolvedType);
-                       if (!valueRequired)
+                       if (valueRequired) {
+                               codeStream.generateImplicitConversion(implicitConversion);
+                       } else {
                                codeStream.pop();
+                       }
                } else {
                        if (valueRequired)
                                codeStream.generateImplicitConversion(implicitConversion);
@@ -459,6 +327,17 @@ public class CastExpression extends Expression {
                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('(');
@@ -466,6 +345,10 @@ public class CastExpression extends Expression {
                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
 
@@ -480,25 +363,67 @@ public class CastExpression extends Expression {
                                && ((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) {