1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 * Nick Teryaev - fix for bug (https://bugs.eclipse.org/bugs/show_bug.cgi?id=40752)
11 *******************************************************************************/
12 package org.eclipse.jdt.internal.compiler.ast;
14 import org.eclipse.jdt.core.compiler.CharOperation;
15 import org.eclipse.jdt.internal.compiler.ASTVisitor;
16 import org.eclipse.jdt.internal.compiler.impl.*;
17 import org.eclipse.jdt.internal.compiler.codegen.*;
18 import org.eclipse.jdt.internal.compiler.flow.*;
19 import org.eclipse.jdt.internal.compiler.lookup.*;
20 import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
22 public class CastExpression extends Expression {
24 public Expression expression;
25 public Expression type;
27 //expression.implicitConversion holds the cast for baseType casting
28 public CastExpression(Expression expression, Expression type) {
29 this.expression = expression;
32 //due to the fact an expression may start with ( and that a cast also start with (
33 //the field is an expression....it can be a TypeReference OR a NameReference Or
34 //an expression <--this last one is invalid.......
36 //if (type instanceof TypeReference )
37 // flag = IsTypeReference ;
39 // if (type instanceof NameReference)
40 // flag = IsNameReference ;
42 // flag = IsExpression ;
46 public FlowInfo analyseCode(
47 BlockScope currentScope,
48 FlowContext flowContext,
52 .analyseCode(currentScope, flowContext, flowInfo)
53 .unconditionalInits();
57 * Returns false if the cast is unnecessary
59 public final boolean checkCastTypesCompatibility(
62 TypeBinding expressionType) {
64 // see specifications 5.5
65 // handle errors and process constant when needed
67 // if either one of the type is null ==>
68 // some error has been already reported some where ==>
69 // we then do not report an obvious-cascade-error.
71 if (castType == null || expressionType == null) return true;
73 // identity conversion cannot be performed upfront, due to side-effects
74 // like constant propagation
76 if (castType.isBaseType()) {
77 if (expressionType.isBaseType()) {
78 if (expressionType == castType) {
79 expression.implicitWidening(castType, expressionType);
80 constant = expression.constant; //use the same constant
83 boolean necessary = false;
84 if (expressionType.isCompatibleWith(castType)
85 || (necessary = BaseTypeBinding.isNarrowing(castType.id, expressionType.id))) {
86 expression.implicitConversion = (castType.id << 4) + expressionType.id;
87 if (expression.constant != Constant.NotAConstant) {
88 constant = expression.constant.castTo(expression.implicitConversion);
94 scope.problemReporter().typeCastError(this, castType, expressionType);
98 //-----------cast to something which is NOT a base type--------------------------
99 if (expressionType == NullBinding) {
100 // if (castType.isArrayType()){ // 26903 - need checkcast when casting null to array type
101 // needRuntimeCheckcast = true;
103 return false; //null is compatible with every thing
105 if (expressionType.isBaseType()) {
106 scope.problemReporter().typeCastError(this, castType, expressionType);
110 if (expressionType.isArrayType()) {
111 if (castType == expressionType) return false; // identity conversion
113 if (castType.isArrayType()) {
114 //------- (castType.isArray) expressionType.isArray -----------
115 TypeBinding exprElementType = ((ArrayBinding) expressionType).elementsType(scope);
116 if (exprElementType.isBaseType()) {
117 // <---stop the recursion-------
118 if (((ArrayBinding) castType).elementsType(scope) == exprElementType) {
119 this.bits |= NeedRuntimeCheckCastMASK;
121 scope.problemReporter().typeCastError(this, castType, expressionType);
125 // recursively on the elements...
126 return checkCastTypesCompatibility(
128 ((ArrayBinding) castType).elementsType(scope),
131 castType.isClass()) {
132 //------(castType.isClass) expressionType.isArray ---------------
133 if (castType.id == T_Object) {
136 } else { //------- (castType.isInterface) expressionType.isArray -----------
137 if (castType.id == T_JavaLangCloneable || castType.id == T_JavaIoSerializable) {
138 this.bits |= NeedRuntimeCheckCastMASK;
142 scope.problemReporter().typeCastError(this, castType, expressionType);
146 if (expressionType.isClass()) {
147 if (castType.isArrayType()) {
148 // ---- (castType.isArray) expressionType.isClass -------
149 if (expressionType.id == T_Object) { // potential runtime error
150 this.bits |= NeedRuntimeCheckCastMASK;
153 } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isClass ------
154 if (expressionType.isCompatibleWith(castType)){ // no runtime error
155 if (castType.id == T_String) constant = expression.constant; // (String) cst is still a constant
158 if (castType.isCompatibleWith(expressionType)) {
159 // potential runtime error
160 this.bits |= NeedRuntimeCheckCastMASK;
163 } else { // ----- (castType.isInterface) expressionType.isClass -------
164 if (expressionType.isCompatibleWith(castType))
166 if (!((ReferenceBinding) expressionType).isFinal()) {
167 // a subclass may implement the interface ==> no check at compile time
168 this.bits |= NeedRuntimeCheckCastMASK;
171 // no subclass for expressionType, thus compile-time check is valid
173 scope.problemReporter().typeCastError(this, castType, expressionType);
177 // if (expressionType.isInterface()) { cannot be anything else
178 if (castType.isArrayType()) {
179 // ----- (castType.isArray) expressionType.isInterface ------
180 if (expressionType.id == T_JavaLangCloneable
181 || expressionType.id == T_JavaIoSerializable) {// potential runtime error
182 this.bits |= NeedRuntimeCheckCastMASK;
184 scope.problemReporter().typeCastError(this, castType, expressionType);
187 } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isInterface --------
188 if (castType.id == T_Object) { // no runtime error
191 if (((ReferenceBinding) castType).isFinal()) {
192 // no subclass for castType, thus compile-time check is valid
193 if (!castType.isCompatibleWith(expressionType)) {
194 // potential runtime error
195 scope.problemReporter().typeCastError(this, castType, expressionType);
199 } else { // ----- (castType.isInterface) expressionType.isInterface -------
200 if (expressionType.isCompatibleWith(castType)) {
203 if (!castType.isCompatibleWith(expressionType)) {
204 MethodBinding[] castTypeMethods = ((ReferenceBinding) castType).methods();
205 MethodBinding[] expressionTypeMethods =
206 ((ReferenceBinding) expressionType).methods();
207 int exprMethodsLength = expressionTypeMethods.length;
208 for (int i = 0, castMethodsLength = castTypeMethods.length; i < castMethodsLength; i++)
209 for (int j = 0; j < exprMethodsLength; j++) {
210 if ((castTypeMethods[i].returnType != expressionTypeMethods[j].returnType)
211 && (CharOperation.equals(castTypeMethods[i].selector, expressionTypeMethods[j].selector))
212 && castTypeMethods[i].areParametersEqual(expressionTypeMethods[j])) {
213 scope.problemReporter().typeCastError(this, castType, expressionType);
218 this.bits |= NeedRuntimeCheckCastMASK;
223 * Casting an enclosing instance will considered as useful if removing it would actually bind to a different type
225 public static void checkNeedForEnclosingInstanceCast(BlockScope scope, Expression enclosingInstance, TypeBinding enclosingInstanceType, TypeBinding memberType) {
227 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
229 TypeBinding castedExpressionType = ((CastExpression)enclosingInstance).expression.resolvedType;
230 if (castedExpressionType == null) return; // cannot do better
231 // obvious identity cast
232 if (castedExpressionType == enclosingInstanceType) {
233 scope.problemReporter().unnecessaryCast((CastExpression)enclosingInstance);
234 } else if (castedExpressionType == NullBinding){
235 return; // tolerate null enclosing instance cast
237 TypeBinding alternateEnclosingInstanceType = castedExpressionType;
238 if (castedExpressionType.isBaseType() || castedExpressionType.isArrayType()) return; // error case
239 if (memberType == scope.getMemberType(memberType.sourceName(), (ReferenceBinding) alternateEnclosingInstanceType)) {
240 scope.problemReporter().unnecessaryCast((CastExpression)enclosingInstance);
246 * Only complain for identity cast, since other type of casts may be useful: e.g. ~((~(long) 0) << 32) is different from: ~((~0) << 32)
248 public static void checkNeedForArgumentCast(BlockScope scope, int operator, int operatorSignature, Expression expression, int expressionTypeId) {
250 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
252 // check need for left operand cast
253 int alternateLeftTypeId = expressionTypeId;
254 if ((expression.bits & UnnecessaryCastMask) == 0 && expression.resolvedType.isBaseType()) {
255 // narrowing conversion on base type may change value, thus necessary
258 TypeBinding alternateLeftType = ((CastExpression)expression).expression.resolvedType;
259 if (alternateLeftType == null) return; // cannot do better
260 if ((alternateLeftTypeId = alternateLeftType.id) == expressionTypeId) { // obvious identity cast
261 scope.problemReporter().unnecessaryCast((CastExpression)expression);
263 } else if (alternateLeftTypeId == T_null) {
264 alternateLeftTypeId = expressionTypeId; // tolerate null argument cast
268 /* tolerate widening cast in unary expressions, as may be used when combined in binary expressions (41680)
269 int alternateOperatorSignature = OperatorExpression.OperatorSignatures[operator][(alternateLeftTypeId << 4) + alternateLeftTypeId];
270 // (cast) left Op (cast) right --> result
271 // 1111 0000 1111 0000 1111
272 // <<16 <<12 <<8 <<4 <<0
273 final int CompareMASK = (0xF<<16) + (0xF<<8) + 0xF; // mask hiding compile-time types
274 if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result
275 scope.problemReporter().unnecessaryCastForArgument((CastExpression)expression, TypeBinding.wellKnownType(scope, expression.implicitConversion >> 4));
281 * Cast expressions will considered as useful if removing them all would actually bind to a different method
282 * (no fine grain analysis on per casted argument basis, simply separate widening cast from narrowing ones)
284 public static void checkNeedForArgumentCasts(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] argumentTypes, final InvocationSite invocationSite) {
286 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
288 int length = argumentTypes.length;
290 // iterate over arguments, and retrieve original argument types (before cast)
291 TypeBinding[] rawArgumentTypes = argumentTypes;
292 for (int i = 0; i < length; i++) {
293 Expression argument = arguments[i];
294 if (argument instanceof CastExpression) {
295 // narrowing conversion on base type may change value, thus necessary
296 if ((argument.bits & UnnecessaryCastMask) == 0 && argument.resolvedType.isBaseType()) {
299 TypeBinding castedExpressionType = ((CastExpression)argument).expression.resolvedType;
300 if (castedExpressionType == null) return; // cannot do better
301 // obvious identity cast
302 if (castedExpressionType == argumentTypes[i]) {
303 scope.problemReporter().unnecessaryCast((CastExpression)argument);
304 } else if (castedExpressionType == NullBinding){
305 continue; // tolerate null argument cast
307 if (rawArgumentTypes == argumentTypes) {
308 System.arraycopy(rawArgumentTypes, 0, rawArgumentTypes = new TypeBinding[length], 0, length);
310 // retain original argument type
311 rawArgumentTypes[i] = castedExpressionType;
315 // perform alternate lookup with original types
316 if (rawArgumentTypes != argumentTypes) {
317 checkAlternateBinding(scope, receiver, receiverType, binding, arguments, argumentTypes, rawArgumentTypes, invocationSite);
322 * Check binary operator casted arguments
324 public static void checkNeedForArgumentCasts(BlockScope scope, int operator, int operatorSignature, Expression left, int leftTypeId, boolean leftIsCast, Expression right, int rightTypeId, boolean rightIsCast) {
326 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
328 // check need for left operand cast
329 int alternateLeftTypeId = leftTypeId;
331 if ((left.bits & UnnecessaryCastMask) == 0 && left.resolvedType.isBaseType()) {
332 // narrowing conversion on base type may change value, thus necessary
335 TypeBinding alternateLeftType = ((CastExpression)left).expression.resolvedType;
336 if (alternateLeftType == null) return; // cannot do better
337 if ((alternateLeftTypeId = alternateLeftType.id) == leftTypeId) { // obvious identity cast
338 scope.problemReporter().unnecessaryCast((CastExpression)left);
340 } else if (alternateLeftTypeId == T_null) {
341 alternateLeftTypeId = leftTypeId; // tolerate null argument cast
346 // check need for right operand cast
347 int alternateRightTypeId = rightTypeId;
349 if ((right.bits & UnnecessaryCastMask) == 0 && right.resolvedType.isBaseType()) {
350 // narrowing conversion on base type may change value, thus necessary
353 TypeBinding alternateRightType = ((CastExpression)right).expression.resolvedType;
354 if (alternateRightType == null) return; // cannot do better
355 if ((alternateRightTypeId = alternateRightType.id) == rightTypeId) { // obvious identity cast
356 scope.problemReporter().unnecessaryCast((CastExpression)right);
358 } else if (alternateRightTypeId == T_null) {
359 alternateRightTypeId = rightTypeId; // tolerate null argument cast
364 if (leftIsCast || rightIsCast) {
365 if (alternateLeftTypeId > 15 || alternateRightTypeId > 15) { // must convert String + Object || Object + String
366 if (alternateLeftTypeId == T_String) {
367 alternateRightTypeId = T_Object;
368 } else if (alternateRightTypeId == T_String) {
369 alternateLeftTypeId = T_Object;
371 return; // invalid operator
374 int alternateOperatorSignature = OperatorExpression.OperatorSignatures[operator][(alternateLeftTypeId << 4) + alternateRightTypeId];
375 // (cast) left Op (cast) right --> result
376 // 1111 0000 1111 0000 1111
377 // <<16 <<12 <<8 <<4 <<0
378 final int CompareMASK = (0xF<<16) + (0xF<<8) + 0xF; // mask hiding compile-time types
379 if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result
380 if (leftIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)left, TypeBinding.wellKnownType(scope, left.implicitConversion >> 4));
381 if (rightIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)right, TypeBinding.wellKnownType(scope, right.implicitConversion >> 4));
386 private static void checkAlternateBinding(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] originalArgumentTypes, TypeBinding[] alternateArgumentTypes, final InvocationSite invocationSite) {
388 InvocationSite fakeInvocationSite = new InvocationSite(){
389 public boolean isSuperAccess(){ return invocationSite.isSuperAccess(); }
390 public boolean isTypeAccess() { return invocationSite.isTypeAccess(); }
391 public void setActualReceiverType(ReferenceBinding actualReceiverType) { /* ignore */}
392 public void setDepth(int depth) { /* ignore */}
393 public void setFieldIndex(int depth){ /* ignore */}
394 public int sourceStart() { return 0; }
395 public int sourceEnd() { return 0; }
397 MethodBinding bindingIfNoCast;
398 if (binding.isConstructor()) {
399 bindingIfNoCast = scope.getConstructor((ReferenceBinding)receiverType, alternateArgumentTypes, fakeInvocationSite);
401 bindingIfNoCast = receiver.isImplicitThis()
402 ? scope.getImplicitMethod(binding.selector, alternateArgumentTypes, fakeInvocationSite)
403 : scope.getMethod(receiverType, binding.selector, alternateArgumentTypes, fakeInvocationSite);
405 if (bindingIfNoCast == binding) {
406 for (int i = 0, length = originalArgumentTypes.length; i < length; i++) {
407 if (originalArgumentTypes[i] != alternateArgumentTypes[i]) {
408 scope.problemReporter().unnecessaryCastForArgument((CastExpression)arguments[i], binding.parameters[i]);
414 * Cast expression code generation
416 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
417 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
418 * @param valueRequired boolean
420 public void generateCode(
421 BlockScope currentScope,
422 CodeStream codeStream,
423 boolean valueRequired) {
425 int pc = codeStream.position;
426 boolean needRuntimeCheckcast = (this.bits & NeedRuntimeCheckCastMASK) != 0;
427 if (constant != NotAConstant) {
428 if (valueRequired || needRuntimeCheckcast) { // Added for: 1F1W9IG: IVJCOM:WINNT - Compiler omits casting check
429 codeStream.generateConstant(constant, implicitConversion);
430 if (needRuntimeCheckcast) {
431 codeStream.checkcast(this.resolvedType);
436 codeStream.recordPositionsFrom(pc, this.sourceStart);
439 expression.generateCode(
442 valueRequired || needRuntimeCheckcast);
443 if (needRuntimeCheckcast) {
444 codeStream.checkcast(this.resolvedType);
449 codeStream.generateImplicitConversion(implicitConversion);
451 codeStream.recordPositionsFrom(pc, this.sourceStart);
454 public Expression innermostCastedExpression(){
455 Expression current = this.expression;
456 while (current instanceof CastExpression) {
457 current = ((CastExpression) current).expression;
462 public StringBuffer printExpression(int indent, StringBuffer output) {
465 type.print(0, output).append(") "); //$NON-NLS-1$
466 return expression.printExpression(0, output);
469 public TypeBinding resolveType(BlockScope scope) {
470 // compute a new constant if the cast is effective
472 // due to the fact an expression may start with ( and that a cast can also start with (
473 // the field is an expression....it can be a TypeReference OR a NameReference Or
474 // any kind of Expression <-- this last one is invalid.......
476 constant = Constant.NotAConstant;
477 implicitConversion = T_undefined;
479 if ((type instanceof TypeReference) || (type instanceof NameReference)
480 && ((type.bits & ASTNode.ParenthesizedMASK) >> ASTNode.ParenthesizedSHIFT) == 0) { // no extra parenthesis around type: ((A))exp
482 this.resolvedType = type.resolveType(scope);
483 TypeBinding expressionType = expression.resolveType(scope);
484 if (this.resolvedType != null && expressionType != null) {
485 boolean necessary = checkCastTypesCompatibility(scope, this.resolvedType, expressionType);
486 if (!necessary && this.expression.resolvedType != null) { // cannot do better if expression is not bound
487 this.bits |= UnnecessaryCastMask;
488 if ((this.bits & IgnoreNeedForCastCheckMASK) == 0) {
489 scope.problemReporter().unnecessaryCast(this);
493 return this.resolvedType;
494 } else { // expression as a cast !!!!!!!!
495 TypeBinding expressionType = expression.resolveType(scope);
496 if (expressionType == null) return null;
497 scope.problemReporter().invalidTypeReference(type);
502 public void traverse(
504 BlockScope blockScope) {
506 if (visitor.visit(this, blockScope)) {
507 type.traverse(visitor, blockScope);
508 expression.traverse(visitor, blockScope);
510 visitor.endVisit(this, blockScope);