Makefile fixup
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / CastExpression.java
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
7  * 
8  * Contributors:
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;
13
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;
21
22 public class CastExpression extends Expression {
23
24         public Expression expression;
25         public Expression type;
26         
27         //expression.implicitConversion holds the cast for baseType casting 
28         public CastExpression(Expression expression, Expression type) {
29                 this.expression = expression;
30                 this.type = type;
31
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.......
35
36                 //if (type instanceof TypeReference )
37                 //      flag = IsTypeReference ;
38                 //else
39                 //      if (type instanceof NameReference)
40                 //              flag = IsNameReference ;
41                 //      else
42                 //              flag = IsExpression ;
43
44         }
45
46         public FlowInfo analyseCode(
47                 BlockScope currentScope,
48                 FlowContext flowContext,
49                 FlowInfo flowInfo) {
50
51                 return expression
52                         .analyseCode(currentScope, flowContext, flowInfo)
53                         .unconditionalInits();
54         }
55
56         /**
57          * Returns false if the cast is unnecessary
58          */
59         public final boolean checkCastTypesCompatibility(
60                 BlockScope scope,
61                 TypeBinding castType,
62                 TypeBinding expressionType) {
63         
64                 // see specifications 5.5
65                 // handle errors and process constant when needed
66         
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.
70         
71                 if (castType == null || expressionType == null) return true;
72         
73                 // identity conversion cannot be performed upfront, due to side-effects
74                 // like constant propagation
75                                 
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
81                                         return false;
82                                 }
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);
89                                         }
90                                         return necessary;
91                                         
92                                 }
93                         }
94                         scope.problemReporter().typeCastError(this, castType, expressionType);
95                         return true;
96                 }
97         
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;
102                         //      }
103                         return false; //null is compatible with every thing
104                 }
105                 if (expressionType.isBaseType()) {
106                         scope.problemReporter().typeCastError(this, castType, expressionType);
107                         return true;
108                 }
109         
110                 if (expressionType.isArrayType()) {
111                         if (castType == expressionType) return false; // identity conversion
112         
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;
120                                         } else {
121                                                 scope.problemReporter().typeCastError(this, castType, expressionType);
122                                         }
123                                         return true;
124                                 }
125                                 // recursively on the elements...
126                                 return checkCastTypesCompatibility(
127                                         scope,
128                                         ((ArrayBinding) castType).elementsType(scope),
129                                         exprElementType);
130                         } else if (
131                                 castType.isClass()) {
132                                 //------(castType.isClass) expressionType.isArray ---------------       
133                                 if (castType.id == T_Object) {
134                                         return false;
135                                 }
136                         } else { //------- (castType.isInterface) expressionType.isArray -----------
137                                 if (castType.id == T_JavaLangCloneable || castType.id == T_JavaIoSerializable) {
138                                         this.bits |= NeedRuntimeCheckCastMASK;
139                                         return true;
140                                 }
141                         }
142                         scope.problemReporter().typeCastError(this, castType, expressionType);
143                         return true;
144                 }
145         
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;
151                                         return true;
152                                 }
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
156                                         return false;
157                                 }
158                                 if (castType.isCompatibleWith(expressionType)) {
159                                         // potential runtime  error
160                                         this.bits |= NeedRuntimeCheckCastMASK;
161                                         return true;
162                                 }
163                         } else { // ----- (castType.isInterface) expressionType.isClass -------  
164                                 if (expressionType.isCompatibleWith(castType)) 
165                                         return false;
166                                 if (!((ReferenceBinding) expressionType).isFinal()) {
167                                         // a subclass may implement the interface ==> no check at compile time
168                                         this.bits |= NeedRuntimeCheckCastMASK;
169                                         return true;                                
170                                 }
171                                 // no subclass for expressionType, thus compile-time check is valid
172                         }
173                         scope.problemReporter().typeCastError(this, castType, expressionType);
174                         return true;
175                 }
176         
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;
183                         } else {
184                                 scope.problemReporter().typeCastError(this, castType, expressionType);
185                         }
186                         return true;
187                 } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isInterface --------
188                         if (castType.id == T_Object) { // no runtime error
189                                 return false;
190                         }
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);
196                                         return true;
197                                 }
198                         }
199                 } else { // ----- (castType.isInterface) expressionType.isInterface -------
200                         if (expressionType.isCompatibleWith(castType)) {
201                                 return false; 
202                         }
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);
214                                                 }
215                                         }
216                         }
217                 }
218                 this.bits |= NeedRuntimeCheckCastMASK;
219                 return true;
220         }
221
222         /**
223          * Casting an enclosing instance will considered as useful if removing it would actually bind to a different type
224          */
225         public static void checkNeedForEnclosingInstanceCast(BlockScope scope, Expression enclosingInstance, TypeBinding enclosingInstanceType, TypeBinding memberType) {
226         
227                 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
228                 
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
236                 } else {
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);
241                         }
242                 }
243         }
244
245         /**
246          * Only complain for identity cast, since other type of casts may be useful: e.g.  ~((~(long) 0) << 32)  is different from: ~((~0) << 32) 
247          */
248         public static void checkNeedForArgumentCast(BlockScope scope, int operator, int operatorSignature, Expression expression, int expressionTypeId) {
249         
250                 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
251         
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
256                         return;
257                 } else  {
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); 
262                                 return;
263                         } else if (alternateLeftTypeId == T_null) {
264                                 alternateLeftTypeId = expressionTypeId;  // tolerate null argument cast
265                                 return;
266                         }
267                 }
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)); 
276                 }
277 */              
278         }
279                 
280         /**
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)
283          */
284         public static void checkNeedForArgumentCasts(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] argumentTypes, final InvocationSite invocationSite) {
285         
286                 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
287                 
288                 int length = argumentTypes.length;
289
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()) {
297                                         continue;
298                                 }               
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
306                                 } else {
307                                         if (rawArgumentTypes == argumentTypes) {
308                                                 System.arraycopy(rawArgumentTypes, 0, rawArgumentTypes = new TypeBinding[length], 0, length);
309                                         }
310                                         // retain original argument type
311                                         rawArgumentTypes[i] = castedExpressionType; 
312                                 }
313                         }                               
314                 }
315                 // perform alternate lookup with original types
316                 if (rawArgumentTypes != argumentTypes) {
317                         checkAlternateBinding(scope, receiver, receiverType, binding, arguments, argumentTypes, rawArgumentTypes, invocationSite);
318                 }
319         }
320
321         /**
322          * Check binary operator casted arguments 
323          */
324         public static void checkNeedForArgumentCasts(BlockScope scope, int operator, int operatorSignature, Expression left, int leftTypeId, boolean leftIsCast, Expression right, int rightTypeId, boolean rightIsCast) {
325
326                 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
327
328                 // check need for left operand cast
329                 int alternateLeftTypeId = leftTypeId;
330                 if (leftIsCast) {
331                         if ((left.bits & UnnecessaryCastMask) == 0 && left.resolvedType.isBaseType()) {
332                                 // narrowing conversion on base type may change value, thus necessary
333                                 leftIsCast = false;
334                         } else  {
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); 
339                                         leftIsCast = false;
340                                 } else if (alternateLeftTypeId == T_null) {
341                                         alternateLeftTypeId = leftTypeId;  // tolerate null argument cast
342                                         leftIsCast = false;
343                                 }
344                         }
345                 }
346                 // check need for right operand cast
347                 int alternateRightTypeId = rightTypeId;
348                 if (rightIsCast) {
349                         if ((right.bits & UnnecessaryCastMask) == 0 && right.resolvedType.isBaseType()) {
350                                 // narrowing conversion on base type may change value, thus necessary
351                                 rightIsCast = false;
352                         } else {
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); 
357                                         rightIsCast = false;
358                                 } else if (alternateRightTypeId == T_null) {
359                                         alternateRightTypeId = rightTypeId;  // tolerate null argument cast
360                                         rightIsCast = false;
361                                 }
362                         }       
363                 }
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;
370                                 } else {
371                                         return; // invalid operator
372                                 }
373                         }
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));
382                         }
383                 }
384         }
385
386         private static void checkAlternateBinding(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] originalArgumentTypes, TypeBinding[] alternateArgumentTypes, final InvocationSite invocationSite) {
387
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; }
396                         };      
397                         MethodBinding bindingIfNoCast;
398                         if (binding.isConstructor()) {
399                                 bindingIfNoCast = scope.getConstructor((ReferenceBinding)receiverType, alternateArgumentTypes, fakeInvocationSite);
400                         } else {
401                                 bindingIfNoCast = receiver.isImplicitThis()
402                                         ? scope.getImplicitMethod(binding.selector, alternateArgumentTypes, fakeInvocationSite)
403                                         : scope.getMethod(receiverType, binding.selector, alternateArgumentTypes, fakeInvocationSite);  
404                         }
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]);
409                                         }
410                                 }
411                         }       
412         }
413         /**
414          * Cast expression code generation
415          *
416          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
417          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
418          * @param valueRequired boolean
419          */
420         public void generateCode(
421                 BlockScope currentScope,
422                 CodeStream codeStream,
423                 boolean valueRequired) {
424
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);
432                                         if (!valueRequired)
433                                                 codeStream.pop();
434                                 }
435                         }
436                         codeStream.recordPositionsFrom(pc, this.sourceStart);
437                         return;
438                 }
439                 expression.generateCode(
440                         currentScope,
441                         codeStream,
442                         valueRequired || needRuntimeCheckcast);
443                 if (needRuntimeCheckcast) {
444                         codeStream.checkcast(this.resolvedType);
445                         if (!valueRequired)
446                                 codeStream.pop();
447                 } else {
448                         if (valueRequired)
449                                 codeStream.generateImplicitConversion(implicitConversion);
450                 }
451                 codeStream.recordPositionsFrom(pc, this.sourceStart);
452         }
453
454         public Expression innermostCastedExpression(){ 
455                 Expression current = this.expression;
456                 while (current instanceof CastExpression) {
457                         current = ((CastExpression) current).expression;
458                 }
459                 return current;
460         }
461
462         public StringBuffer printExpression(int indent, StringBuffer output) {
463
464                 output.append('(');
465                 type.print(0, output).append(") "); //$NON-NLS-1$
466                 return expression.printExpression(0, output);
467         }
468
469         public TypeBinding resolveType(BlockScope scope) {
470                 // compute a new constant if the cast is effective
471
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.......
475
476                 constant = Constant.NotAConstant;
477                 implicitConversion = T_undefined;
478
479                 if ((type instanceof TypeReference) || (type instanceof NameReference)
480                                 && ((type.bits & ASTNode.ParenthesizedMASK) >> ASTNode.ParenthesizedSHIFT) == 0) { // no extra parenthesis around type: ((A))exp
481
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);
490                                         }
491                                 }
492                         }
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);
498                         return null;
499                 }
500         }
501
502         public void traverse(
503                 ASTVisitor visitor,
504                 BlockScope blockScope) {
505
506                 if (visitor.visit(this, blockScope)) {
507                         type.traverse(visitor, blockScope);
508                         expression.traverse(visitor, blockScope);
509                 }
510                 visitor.endVisit(this, blockScope);
511         }
512 }