Makefile fixup
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / ConditionalExpression.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  *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
12
13 import org.eclipse.jdt.internal.compiler.ASTVisitor;
14 import org.eclipse.jdt.internal.compiler.impl.*;
15 import org.eclipse.jdt.internal.compiler.codegen.*;
16 import org.eclipse.jdt.internal.compiler.flow.*;
17 import org.eclipse.jdt.internal.compiler.lookup.*;
18
19 public class ConditionalExpression extends OperatorExpression {
20
21         public Expression condition, valueIfTrue, valueIfFalse;
22         public Constant optimizedBooleanConstant;
23         public Constant optimizedIfTrueConstant;
24         public Constant optimizedIfFalseConstant;
25         
26         // for local variables table attributes
27         int trueInitStateIndex = -1;
28         int falseInitStateIndex = -1;
29         int mergedInitStateIndex = -1;
30         
31         public ConditionalExpression(
32                 Expression condition,
33                 Expression valueIfTrue,
34                 Expression valueIfFalse) {
35                 this.condition = condition;
36                 this.valueIfTrue = valueIfTrue;
37                 this.valueIfFalse = valueIfFalse;
38                 sourceStart = condition.sourceStart;
39                 sourceEnd = valueIfFalse.sourceEnd;
40         }
41
42         public FlowInfo analyseCode(
43                 BlockScope currentScope,
44                 FlowContext flowContext,
45                 FlowInfo flowInfo) {
46
47                 Constant cst = this.condition.optimizedBooleanConstant();
48                 boolean isConditionOptimizedTrue = cst != NotAConstant && cst.booleanValue() == true;
49                 boolean isConditionOptimizedFalse = cst != NotAConstant && cst.booleanValue() == false;
50
51                 int mode = flowInfo.reachMode();
52                 flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo, cst == NotAConstant);
53                 
54                 // process the if-true part
55                 FlowInfo trueFlowInfo = flowInfo.initsWhenTrue().copy();
56                 if (isConditionOptimizedFalse) {
57                         trueFlowInfo.setReachMode(FlowInfo.UNREACHABLE); 
58                 }
59                 trueInitStateIndex = currentScope.methodScope().recordInitializationStates(trueFlowInfo);
60                 trueFlowInfo = valueIfTrue.analyseCode(currentScope, flowContext, trueFlowInfo);
61
62                 // process the if-false part
63                 FlowInfo falseFlowInfo = flowInfo.initsWhenFalse().copy();
64                 if (isConditionOptimizedTrue) {
65                         falseFlowInfo.setReachMode(FlowInfo.UNREACHABLE); 
66                 }
67                 falseInitStateIndex = currentScope.methodScope().recordInitializationStates(falseFlowInfo);
68                 falseFlowInfo = valueIfFalse.analyseCode(currentScope, flowContext, falseFlowInfo);
69
70                 // merge if-true & if-false initializations
71                 FlowInfo mergedInfo;
72                 if (isConditionOptimizedTrue){
73                         mergedInfo = trueFlowInfo.addPotentialInitializationsFrom(falseFlowInfo);
74                 } else if (isConditionOptimizedFalse) {
75                         mergedInfo = falseFlowInfo.addPotentialInitializationsFrom(trueFlowInfo);
76                 } else {
77                         // if ((t && (v = t)) ? t : t && (v = f)) r = v;  -- ok
78                         cst = this.optimizedIfTrueConstant;
79                         boolean isValueIfTrueOptimizedTrue = cst != null && cst != NotAConstant && cst.booleanValue() == true;
80                         boolean isValueIfTrueOptimizedFalse = cst != null && cst != NotAConstant && cst.booleanValue() == false;
81                         
82                         cst = this.optimizedIfFalseConstant;
83                         boolean isValueIfFalseOptimizedTrue = cst != null && cst != NotAConstant && cst.booleanValue() == true;
84                         boolean isValueIfFalseOptimizedFalse = cst != null && cst != NotAConstant && cst.booleanValue() == false;
85
86                         UnconditionalFlowInfo trueInfoWhenTrue = trueFlowInfo.initsWhenTrue().copy().unconditionalInits();
87                         if (isValueIfTrueOptimizedFalse) trueInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); 
88
89                         UnconditionalFlowInfo falseInfoWhenTrue = falseFlowInfo.initsWhenTrue().copy().unconditionalInits();
90                         if (isValueIfFalseOptimizedFalse) falseInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); 
91                         
92                         UnconditionalFlowInfo trueInfoWhenFalse = trueFlowInfo.initsWhenFalse().copy().unconditionalInits();
93                         if (isValueIfTrueOptimizedTrue) trueInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); 
94
95                         UnconditionalFlowInfo falseInfoWhenFalse = falseFlowInfo.initsWhenFalse().copy().unconditionalInits();
96                         if (isValueIfFalseOptimizedTrue) falseInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); 
97
98                         mergedInfo =
99                                 FlowInfo.conditional(
100                                         trueInfoWhenTrue.mergedWith(falseInfoWhenTrue),
101                                         trueInfoWhenFalse.mergedWith(falseInfoWhenFalse));
102                 }
103                 mergedInitStateIndex =
104                         currentScope.methodScope().recordInitializationStates(mergedInfo);
105                 mergedInfo.setReachMode(mode);
106                 return mergedInfo;
107         }
108
109         /**
110          * Code generation for the conditional operator ?:
111          *
112          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
113          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
114          * @param valueRequired boolean
115         */
116         public void generateCode(
117                 BlockScope currentScope,
118                 CodeStream codeStream,
119                 boolean valueRequired) {
120
121                 int pc = codeStream.position;
122                 Label endifLabel, falseLabel;
123                 if (constant != NotAConstant) {
124                         if (valueRequired)
125                                 codeStream.generateConstant(constant, implicitConversion);
126                         codeStream.recordPositionsFrom(pc, this.sourceStart);
127                         return;
128                 }
129                 Constant cst = condition.constant;
130                 Constant condCst = condition.optimizedBooleanConstant();
131                 boolean needTruePart =
132                         !(((cst != NotAConstant) && (cst.booleanValue() == false))
133                                 || ((condCst != NotAConstant) && (condCst.booleanValue() == false)));
134                 boolean needFalsePart =
135                         !(((cst != NotAConstant) && (cst.booleanValue() == true))
136                                 || ((condCst != NotAConstant) && (condCst.booleanValue() == true)));
137                 endifLabel = new Label(codeStream);
138
139                 // Generate code for the condition
140                 boolean needConditionValue = (cst == NotAConstant) && (condCst == NotAConstant);
141                 condition.generateOptimizedBoolean(
142                         currentScope,
143                         codeStream,
144                         null,
145                         (falseLabel = new Label(codeStream)),
146                         needConditionValue);
147
148                 if (trueInitStateIndex != -1) {
149                         codeStream.removeNotDefinitelyAssignedVariables(
150                                 currentScope,
151                                 trueInitStateIndex);
152                         codeStream.addDefinitelyAssignedVariables(currentScope, trueInitStateIndex);
153                 }
154                 // Then code generation
155                 if (needTruePart) {
156                         valueIfTrue.generateCode(currentScope, codeStream, valueRequired);
157                         if (needFalsePart) {
158                                 // Jump over the else part
159                                 int position = codeStream.position;
160                                 codeStream.goto_(endifLabel);
161                                 codeStream.updateLastRecordedEndPC(position);
162                                 // Tune codestream stack size
163                                 if (valueRequired) {
164                                         codeStream.decrStackSize(this.resolvedType == LongBinding || this.resolvedType == DoubleBinding ? 2 : 1);
165                                 }
166                         }
167                 }
168                 if (needFalsePart) {
169                         falseLabel.place();
170                         if (falseInitStateIndex != -1) {
171                                 codeStream.removeNotDefinitelyAssignedVariables(
172                                         currentScope,
173                                         falseInitStateIndex);
174                                 codeStream.addDefinitelyAssignedVariables(currentScope, falseInitStateIndex);
175                         }
176                         valueIfFalse.generateCode(currentScope, codeStream, valueRequired);
177                         // End of if statement
178                         endifLabel.place();
179                 }
180                 // May loose some local variable initializations : affecting the local variable attributes
181                 if (mergedInitStateIndex != -1) {
182                         codeStream.removeNotDefinitelyAssignedVariables(
183                                 currentScope,
184                                 mergedInitStateIndex);
185                 }
186                 // implicit conversion
187                 if (valueRequired)
188                         codeStream.generateImplicitConversion(implicitConversion);
189                 codeStream.recordPositionsFrom(pc, this.sourceStart);
190         }
191
192         /**
193          * Optimized boolean code generation for the conditional operator ?:
194         */
195         public void generateOptimizedBoolean(
196                 BlockScope currentScope,
197                 CodeStream codeStream,
198                 Label trueLabel,
199                 Label falseLabel,
200                 boolean valueRequired) {
201
202                 if ((constant != Constant.NotAConstant) && (constant.typeID() == T_boolean) // constant
203                         || (valueIfTrue.implicitConversion >> 4) != T_boolean) { // non boolean values
204                         super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
205                         return;
206                 }
207                 Constant cst = condition.constant;
208                 Constant condCst = condition.optimizedBooleanConstant();
209                 boolean needTruePart =
210                         !(((cst != NotAConstant) && (cst.booleanValue() == false))
211                                 || ((condCst != NotAConstant) && (condCst.booleanValue() == false)));
212                 boolean needFalsePart =
213                         !(((cst != NotAConstant) && (cst.booleanValue() == true))
214                                 || ((condCst != NotAConstant) && (condCst.booleanValue() == true)));
215
216                 Label internalFalseLabel, endifLabel = new Label(codeStream);
217
218                 // Generate code for the condition
219                 boolean needConditionValue = (cst == NotAConstant) && (condCst == NotAConstant);
220                 condition.generateOptimizedBoolean(
221                                 currentScope,
222                                 codeStream,
223                                 null,
224                                 internalFalseLabel = new Label(codeStream),
225                                 needConditionValue);
226
227                 if (trueInitStateIndex != -1) {
228                         codeStream.removeNotDefinitelyAssignedVariables(
229                                 currentScope,
230                                 trueInitStateIndex);
231                         codeStream.addDefinitelyAssignedVariables(currentScope, trueInitStateIndex);
232                 }
233                 // Then code generation
234                 if (needTruePart) {
235                         valueIfTrue.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
236                         
237                         if (needFalsePart) {
238                                 // Jump over the else part
239                                 int position = codeStream.position;
240                                 codeStream.goto_(endifLabel);
241                                 codeStream.updateLastRecordedEndPC(position);
242                                 // No need to decrement codestream stack size
243                                 // since valueIfTrue was already consumed by branch bytecode
244                         }
245                 }
246                 if (needFalsePart) {
247                         internalFalseLabel.place();
248                         if (falseInitStateIndex != -1) {
249                                 codeStream.removeNotDefinitelyAssignedVariables(
250                                         currentScope,
251                                         falseInitStateIndex);
252                                 codeStream.addDefinitelyAssignedVariables(currentScope, falseInitStateIndex);
253                         }
254                         valueIfFalse.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
255
256                         // End of if statement
257                         endifLabel.place();
258                 }
259                 // May loose some local variable initializations : affecting the local variable attributes
260                 if (mergedInitStateIndex != -1) {
261                         codeStream.removeNotDefinitelyAssignedVariables(
262                                 currentScope,
263                                 mergedInitStateIndex);
264                 }
265                 // no implicit conversion for boolean values
266                 codeStream.updateLastRecordedEndPC(codeStream.position);
267         }
268
269         public Constant optimizedBooleanConstant() {
270
271                 return this.optimizedBooleanConstant == null ? this.constant : this.optimizedBooleanConstant;
272         }
273         
274         public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
275                 
276                 condition.printExpression(indent, output).append(" ? "); //$NON-NLS-1$
277                 valueIfTrue.printExpression(0, output).append(" : "); //$NON-NLS-1$
278                 return valueIfFalse.printExpression(0, output);
279         }
280
281         public TypeBinding resolveType(BlockScope scope) {
282                 // specs p.368
283                 constant = NotAConstant;
284                 TypeBinding conditionType = condition.resolveTypeExpecting(scope, BooleanBinding);
285                 
286                 if (valueIfTrue instanceof CastExpression) valueIfTrue.bits |= IgnoreNeedForCastCheckMASK; // will check later on
287                 TypeBinding valueIfTrueType = valueIfTrue.resolveType(scope);
288
289                 if (valueIfFalse instanceof CastExpression) valueIfFalse.bits |= IgnoreNeedForCastCheckMASK; // will check later on
290                 TypeBinding valueIfFalseType = valueIfFalse.resolveType(scope);
291
292                 if (conditionType == null || valueIfTrueType == null || valueIfFalseType == null)
293                         return null;
294
295                 // Propagate the constant value from the valueIfTrue and valueIFFalse expression if it is possible
296                 Constant condConstant, trueConstant, falseConstant;
297                 if ((condConstant = condition.constant) != NotAConstant
298                         && (trueConstant = valueIfTrue.constant) != NotAConstant
299                         && (falseConstant = valueIfFalse.constant) != NotAConstant) {
300                         // all terms are constant expression so we can propagate the constant
301                         // from valueIFTrue or valueIfFalse to teh receiver constant
302                         constant = condConstant.booleanValue() ? trueConstant : falseConstant;
303                 }
304                 if (valueIfTrueType == valueIfFalseType) { // harmed the implicit conversion 
305                         valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
306                         valueIfFalse.implicitConversion = valueIfTrue.implicitConversion;
307                         if (valueIfTrueType == BooleanBinding) {
308                                 this.optimizedIfTrueConstant = valueIfTrue.optimizedBooleanConstant();
309                                 this.optimizedIfFalseConstant = valueIfFalse.optimizedBooleanConstant();
310                                 if (this.optimizedIfTrueConstant != NotAConstant 
311                                                 && this.optimizedIfFalseConstant != NotAConstant
312                                                 && this.optimizedIfTrueConstant.booleanValue() == this.optimizedIfFalseConstant.booleanValue()) {
313                                         // a ? true : true  /   a ? false : false
314                                         this.optimizedBooleanConstant = optimizedIfTrueConstant;
315                                 } else if ((condConstant = condition.optimizedBooleanConstant()) != NotAConstant) { // Propagate the optimized boolean constant if possible
316                                         this.optimizedBooleanConstant = condConstant.booleanValue()
317                                                 ? this.optimizedIfTrueConstant
318                                                 : this.optimizedIfFalseConstant;
319                                 }
320                         }
321                         return this.resolvedType = valueIfTrueType;
322                 }
323                 // Determine the return type depending on argument types
324                 // Numeric types
325                 if (valueIfTrueType.isNumericType() && valueIfFalseType.isNumericType()) {
326                         // (Short x Byte) or (Byte x Short)"
327                         if ((valueIfTrueType == ByteBinding && valueIfFalseType == ShortBinding)
328                                 || (valueIfTrueType == ShortBinding && valueIfFalseType == ByteBinding)) {
329                                 valueIfTrue.implicitWidening(ShortBinding, valueIfTrueType);
330                                 valueIfFalse.implicitWidening(ShortBinding, valueIfFalseType);
331                                 return this.resolvedType = ShortBinding;
332                         }
333                         // <Byte|Short|Char> x constant(Int)  ---> <Byte|Short|Char>   and reciprocally
334                         if ((valueIfTrueType == ByteBinding || valueIfTrueType == ShortBinding || valueIfTrueType == CharBinding)
335                                         && (valueIfFalseType == IntBinding
336                                                 && valueIfFalse.isConstantValueOfTypeAssignableToType(valueIfFalseType, valueIfTrueType))) {
337                                 valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
338                                 valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType);
339                                 return this.resolvedType = valueIfTrueType;
340                         }
341                         if ((valueIfFalseType == ByteBinding
342                                         || valueIfFalseType == ShortBinding
343                                         || valueIfFalseType == CharBinding)
344                                         && (valueIfTrueType == IntBinding
345                                                 && valueIfTrue.isConstantValueOfTypeAssignableToType(valueIfTrueType, valueIfFalseType))) {
346                                 valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType);
347                                 valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType);
348                                 return this.resolvedType = valueIfFalseType;
349                         }
350                         // Manual binary numeric promotion
351                         // int
352                         if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_int)
353                                         && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_int)) {
354                                 valueIfTrue.implicitWidening(IntBinding, valueIfTrueType);
355                                 valueIfFalse.implicitWidening(IntBinding, valueIfFalseType);
356                                 return this.resolvedType = IntBinding;
357                         }
358                         // long
359                         if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_long)
360                                         && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_long)) {
361                                 valueIfTrue.implicitWidening(LongBinding, valueIfTrueType);
362                                 valueIfFalse.implicitWidening(LongBinding, valueIfFalseType);
363                                 return this.resolvedType = LongBinding;
364                         }
365                         // float
366                         if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_float)
367                                         && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_float)) {
368                                 valueIfTrue.implicitWidening(FloatBinding, valueIfTrueType);
369                                 valueIfFalse.implicitWidening(FloatBinding, valueIfFalseType);
370                                 return this.resolvedType = FloatBinding;
371                         }
372                         // double
373                         valueIfTrue.implicitWidening(DoubleBinding, valueIfTrueType);
374                         valueIfFalse.implicitWidening(DoubleBinding, valueIfFalseType);
375                         return this.resolvedType = DoubleBinding;
376                 }
377                 // Type references (null null is already tested)
378                 if ((valueIfTrueType.isBaseType() && valueIfTrueType != NullBinding)
379                                 || (valueIfFalseType.isBaseType() && valueIfFalseType != NullBinding)) {
380                         scope.problemReporter().conditionalArgumentsIncompatibleTypes(
381                                 this,
382                                 valueIfTrueType,
383                                 valueIfFalseType);
384                         return null;
385                 }
386                 if (valueIfFalseType.isCompatibleWith(valueIfTrueType)) {
387                         valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
388                         valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType);
389                         return this.resolvedType = valueIfTrueType;
390                 }
391                 if (valueIfTrueType.isCompatibleWith(valueIfFalseType)) {
392                         valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType);
393                         valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType);
394                         return this.resolvedType = valueIfFalseType;
395                 }
396                 scope.problemReporter().conditionalArgumentsIncompatibleTypes(
397                         this,
398                         valueIfTrueType,
399                         valueIfFalseType);
400                 return null;
401         }
402         
403         public void traverse(ASTVisitor visitor, BlockScope scope) {
404                 if (visitor.visit(this, scope)) {
405                         condition.traverse(visitor, scope);
406                         valueIfTrue.traverse(visitor, scope);
407                         valueIfFalse.traverse(visitor, scope);
408                 }
409                 visitor.endVisit(this, scope);
410         }
411 }