| /******************************************************************************* |
| * Copyright (c) 2018, 2019 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.ast; |
| |
| import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.ASSIGNMENT_CONTEXT; |
| import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.INVOCATION_CONTEXT; |
| import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.VANILLA_CONTEXT; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| |
| import org.eclipse.jdt.internal.compiler.ASTVisitor; |
| import org.eclipse.jdt.internal.compiler.codegen.CodeStream; |
| import org.eclipse.jdt.internal.compiler.flow.FlowContext; |
| import org.eclipse.jdt.internal.compiler.flow.FlowInfo; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.impl.Constant; |
| import org.eclipse.jdt.internal.compiler.lookup.Binding; |
| import org.eclipse.jdt.internal.compiler.lookup.BlockScope; |
| import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; |
| import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.PolyTypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.Scope; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeIds; |
| |
| public class SwitchExpression extends SwitchStatement implements IPolyExpression { |
| |
| /* package */ TypeBinding expectedType; |
| private ExpressionContext expressionContext = VANILLA_CONTEXT; |
| private boolean isPolyExpression = false; |
| private TypeBinding[] originalValueResultExpressionTypes; |
| private TypeBinding[] finalValueResultExpressionTypes; |
| |
| private int nullStatus = FlowInfo.UNKNOWN; |
| public List<Expression> resultExpressions; |
| public boolean resolveAll; |
| /* package */ List<Integer> resultExpressionNullStatus; |
| private static Map<TypeBinding, TypeBinding[]> type_map; |
| |
| static { |
| type_map = new HashMap<TypeBinding, TypeBinding[]>(); |
| type_map.put(TypeBinding.CHAR, new TypeBinding[] {TypeBinding.CHAR, TypeBinding.BYTE, TypeBinding.INT}); |
| type_map.put(TypeBinding.SHORT, new TypeBinding[] {TypeBinding.SHORT, TypeBinding.BYTE, TypeBinding.INT}); |
| type_map.put(TypeBinding.BYTE, new TypeBinding[] {TypeBinding.BYTE, TypeBinding.INT}); |
| } |
| |
| @Override |
| public void setExpressionContext(ExpressionContext context) { |
| this.expressionContext = context; |
| } |
| |
| @Override |
| public void setExpectedType(TypeBinding expectedType) { |
| this.expectedType = expectedType; |
| } |
| |
| @Override |
| public ExpressionContext getExpressionContext() { |
| return this.expressionContext; |
| } |
| @Override |
| protected boolean ignoreMissingDefaultCase(CompilerOptions compilerOptions, boolean isEnumSwitch) { |
| return isEnumSwitch; // mandatory error if not enum in switch expressions |
| } |
| @Override |
| protected void reportMissingEnumConstantCase(BlockScope upperScope, FieldBinding enumConstant) { |
| upperScope.problemReporter().missingEnumConstantCase(this, enumConstant); |
| } |
| @Override |
| protected int getFallThroughState(Statement stmt, BlockScope blockScope) { |
| if ((stmt instanceof Expression && ((Expression) stmt).isTrulyExpression())|| stmt instanceof ThrowStatement) |
| return BREAKING; |
| if (this.switchLabeledRules // do this check for every block if '->' (Switch Labeled Rules) |
| && stmt instanceof Block) { |
| Block block = (Block) stmt; |
| if (block.doesNotCompleteNormally()) { |
| return BREAKING; |
| } |
| //JLS 12 15.28.1 Given a switch expression, if the switch block consists of switch labeled rules, |
| //then it is a compile-time error if any switch labeled block can complete normally. |
| blockScope.problemReporter().switchExpressionSwitchLabeledBlockCompletesNormally(block); |
| } |
| return FALLTHROUGH; |
| } |
| @Override |
| public boolean checkNPE(BlockScope skope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) { |
| if ((this.nullStatus & FlowInfo.NULL) != 0) |
| skope.problemReporter().expressionNullReference(this); |
| else if ((this.nullStatus & FlowInfo.POTENTIALLY_NULL) != 0) |
| skope.problemReporter().expressionPotentialNullReference(this); |
| return true; // all checking done |
| } |
| |
| private void computeNullStatus(FlowInfo flowInfo, FlowContext flowContext) { |
| boolean precomputed = this.resultExpressionNullStatus.size() > 0; |
| if (!precomputed) |
| this.resultExpressionNullStatus.add(this.resultExpressions.get(0).nullStatus(flowInfo, flowContext)); int status = this.resultExpressions.get(0).nullStatus(flowInfo, flowContext); |
| int combinedStatus = status; |
| boolean identicalStatus = true; |
| for (int i = 1, l = this.resultExpressions.size(); i < l; ++i) { |
| if (!precomputed) |
| this.resultExpressionNullStatus.add(this.resultExpressions.get(i).nullStatus(flowInfo, flowContext)); |
| int tmp = this.resultExpressions.get(i).nullStatus(flowInfo, flowContext); |
| identicalStatus &= status == tmp; |
| combinedStatus |= tmp; |
| } |
| if (identicalStatus) { |
| this.nullStatus = status; |
| return; |
| } |
| status = Expression.computeNullStatus(0, combinedStatus); |
| if (status > 0) |
| this.nullStatus = status; |
| } |
| |
| @Override |
| protected void completeNormallyCheck(BlockScope blockScope) { |
| if (this.switchLabeledRules) return; // already taken care in getFallThroughState() |
| int sz = this.statements != null ? this.statements.length : 0; |
| if (sz == 0) return; |
| /* JLS 12 15.28.1 |
| * If, on the other hand, the switch block consists of switch labeled statement groups, then it is a |
| * compile-time error if either the last statement in the switch block can complete normally, or the |
| * switch block includes one or more switch labels at the end. |
| */ |
| Statement lastNonCaseStmt = null; |
| Statement firstTrailingCaseStmt = null; |
| for (int i = sz - 1; i >= 0; i--) { |
| Statement stmt = this.statements[sz - 1]; |
| if (stmt instanceof CaseStatement) |
| firstTrailingCaseStmt = stmt; |
| else { |
| lastNonCaseStmt = stmt; |
| break; |
| } |
| } |
| if (lastNonCaseStmt != null) { |
| if (!lastNonCaseStmt.doesNotCompleteNormally()) |
| blockScope.problemReporter().switchExpressionLastStatementCompletesNormally(lastNonCaseStmt); |
| else if (lastNonCaseStmt instanceof ContinueStatement || lastNonCaseStmt instanceof ReturnStatement) { |
| blockScope.problemReporter().switchExpressionIllegalLastStatement(lastNonCaseStmt); |
| } |
| } |
| if (firstTrailingCaseStmt != null) { |
| blockScope.problemReporter().switchExpressionTrailingSwitchLabels(firstTrailingCaseStmt); |
| } |
| } |
| @Override |
| protected boolean needToCheckFlowInAbsenceOfDefaultBranch() { // JLS 12 16.1.8 |
| return !this.switchLabeledRules; |
| } |
| @Override |
| public Expression[] getPolyExpressions() { |
| List<Expression> polys = new ArrayList<>(); |
| for (Expression e : this.resultExpressions) { |
| Expression[] ea = e.getPolyExpressions(); |
| if (ea == null || ea.length ==0) continue; |
| polys.addAll(Arrays.asList(ea)); |
| } |
| return polys.toArray(new Expression[0]); |
| } |
| @Override |
| public boolean isPertinentToApplicability(TypeBinding targetType, MethodBinding method) { |
| for (Expression e : this.resultExpressions) { |
| if (!e.isPertinentToApplicability(targetType, method)) |
| return false; |
| } |
| return true; |
| } |
| @Override |
| public boolean isPotentiallyCompatibleWith(TypeBinding targetType, Scope scope1) { |
| for (Expression e : this.resultExpressions) { |
| if (!e.isPotentiallyCompatibleWith(targetType, scope1)) |
| return false; |
| } |
| return true; |
| } |
| @Override |
| public boolean isFunctionalType() { |
| for (Expression e : this.resultExpressions) { |
| if (e.isFunctionalType()) // return true even for one functional type |
| return true; |
| } |
| return false; |
| } |
| @Override |
| public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) { |
| if ((this.implicitConversion & TypeIds.BOXING) != 0) |
| return FlowInfo.NON_NULL; |
| return this.nullStatus; |
| } |
| @Override |
| protected void statementGenerateCode(BlockScope currentScope, CodeStream codeStream, Statement statement) { |
| if (!(statement instanceof Expression && ((Expression) statement).isTrulyExpression()) |
| || statement instanceof Assignment |
| || statement instanceof MessageSend |
| || (statement instanceof SwitchStatement && !(statement instanceof SwitchExpression))) { |
| super.statementGenerateCode(currentScope, codeStream, statement); |
| return; |
| } |
| Expression expression1 = (Expression) statement; |
| expression1.generateCode(currentScope, codeStream, true /* valueRequired */); |
| } |
| @Override |
| public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { |
| super.generateCode(currentScope, codeStream); |
| if (!valueRequired) { |
| // switch expression is saved to a variable that is not used. We need to pop the generated value from the stack |
| switch(postConversionType(currentScope).id) { |
| case TypeIds.T_long : |
| case TypeIds.T_double : |
| codeStream.pop2(); |
| break; |
| case TypeIds.T_void : |
| break; |
| default : |
| codeStream.pop(); |
| break; |
| } |
| } |
| } |
| protected boolean computeConversions(BlockScope blockScope, TypeBinding targetType) { |
| boolean ok = true; |
| for (int i = 0, l = this.resultExpressions.size(); i < l; ++i) { |
| ok &= computeConversionsResultExpressions(blockScope, targetType, this.originalValueResultExpressionTypes[i], this.resultExpressions.get(i)); |
| } |
| return ok; |
| } |
| private boolean computeConversionsResultExpressions(BlockScope blockScope, TypeBinding targetType, TypeBinding resultExpressionType, |
| Expression resultExpression) { |
| if (resultExpressionType != null && resultExpressionType.isValidBinding()) { |
| if (resultExpression.isConstantValueOfTypeAssignableToType(resultExpressionType, targetType) |
| || resultExpressionType.isCompatibleWith(targetType)) { |
| |
| resultExpression.computeConversion(blockScope, targetType, resultExpressionType); |
| if (resultExpressionType.needsUncheckedConversion(targetType)) { |
| blockScope.problemReporter().unsafeTypeConversion(resultExpression, resultExpressionType, targetType); |
| } |
| if (resultExpression instanceof CastExpression |
| && (resultExpression.bits & (ASTNode.UnnecessaryCast|ASTNode.DisableUnnecessaryCastCheck)) == 0) { |
| CastExpression.checkNeedForAssignedCast(blockScope, targetType, (CastExpression) resultExpression); |
| } |
| } else if (isBoxingCompatible(resultExpressionType, targetType, resultExpression, blockScope)) { |
| resultExpression.computeConversion(blockScope, targetType, resultExpressionType); |
| if (resultExpression instanceof CastExpression |
| && (resultExpression.bits & (ASTNode.UnnecessaryCast|ASTNode.DisableUnnecessaryCastCheck)) == 0) { |
| CastExpression.checkNeedForAssignedCast(blockScope, targetType, (CastExpression) resultExpression); |
| } |
| } else { |
| blockScope.problemReporter().typeMismatchError(resultExpressionType, targetType, resultExpression, null); |
| return false; |
| } |
| } |
| return true; |
| } |
| @Override |
| public TypeBinding resolveType(BlockScope upperScope) { |
| try { |
| int resultExpressionsCount; |
| if (this.constant != Constant.NotAConstant) { |
| this.constant = Constant.NotAConstant; |
| |
| // A switch expression is a poly expression if it appears in an assignment context or an invocation context (5.2, 5.3). |
| // Otherwise, it is a standalone expression. |
| if (this.expressionContext == ASSIGNMENT_CONTEXT || this.expressionContext == INVOCATION_CONTEXT) { |
| for (Expression e : this.resultExpressions) { |
| //Where a poly switch expression appears in a context of a particular kind with target type T, |
| //its result expressions similarly appear in a context of the same kind with target type T. |
| e.setExpressionContext(this.expressionContext); |
| e.setExpectedType(this.expectedType); |
| } |
| } |
| |
| resolve(upperScope); |
| |
| if (this.statements == null || this.statements.length == 0) { |
| // Report Error JLS 12 15.28.1 The switch block must not be empty. |
| upperScope.problemReporter().switchExpressionEmptySwitchBlock(this); |
| return null; |
| } |
| |
| resultExpressionsCount = this.resultExpressions != null ? this.resultExpressions.size() : 0; |
| if (resultExpressionsCount == 0) { |
| // Report Error JLS 12 15.28.1 |
| // It is a compile-time error if a switch expression has no result expressions. |
| upperScope.problemReporter().switchExpressionNoResultExpressions(this); |
| return null; |
| } |
| |
| if (this.originalValueResultExpressionTypes == null) { |
| this.originalValueResultExpressionTypes = new TypeBinding[resultExpressionsCount]; |
| this.finalValueResultExpressionTypes = new TypeBinding[resultExpressionsCount]; |
| for (int i = 0; i < resultExpressionsCount; ++i) { |
| this.finalValueResultExpressionTypes[i] = this.originalValueResultExpressionTypes[i] = |
| this.resultExpressions.get(i).resolvedType; |
| } |
| } |
| if (isPolyExpression()) { //The type of a poly switch expression is the same as its target type. |
| if (this.expectedType == null || !this.expectedType.isProperType(true)) { |
| return new PolyTypeBinding(this); |
| } |
| return this.resolvedType = computeConversions(this.scope, this.expectedType) ? this.expectedType : null; |
| } |
| // fall through |
| } else { |
| // re-resolving of poly expression: |
| resultExpressionsCount = this.resultExpressions != null ? this.resultExpressions.size() : 0; |
| for (int i = 0; i < resultExpressionsCount; i++) { |
| Expression resultExpr = this.resultExpressions.get(i); |
| if (resultExpr.resolvedType == null || resultExpr.resolvedType.kind() == Binding.POLY_TYPE) { |
| this.finalValueResultExpressionTypes[i] = this.originalValueResultExpressionTypes[i] = |
| resultExpr.resolveTypeExpecting(upperScope, this.expectedType); |
| } |
| // This is a kludge and only way completion can tell this node to resolve all |
| // resultExpressions. Ideal solution is to remove all other expressions except |
| // the one that contain the completion node. |
| if (this.resolveAll) continue; |
| if (resultExpr.resolvedType == null || !resultExpr.resolvedType.isValidBinding()) |
| return this.resolvedType = null; |
| } |
| this.resolvedType = computeConversions(this.scope, this.expectedType) ? this.expectedType : null; |
| // fall through |
| } |
| |
| if (resultExpressionsCount == 1) |
| return this.resolvedType = this.originalValueResultExpressionTypes[0]; |
| |
| boolean typeUniformAcrossAllArms = true; |
| TypeBinding tmp = this.originalValueResultExpressionTypes[0]; |
| for (int i = 1, l = this.originalValueResultExpressionTypes.length; i < l; ++i) { |
| TypeBinding originalType = this.originalValueResultExpressionTypes[i]; |
| if (originalType != null && TypeBinding.notEquals(tmp, originalType)) { |
| typeUniformAcrossAllArms = false; |
| break; |
| } |
| } |
| // If the result expressions all have the same type (which may be the null type), |
| // then that is the type of the switch expression. |
| if (typeUniformAcrossAllArms) { |
| tmp = this.originalValueResultExpressionTypes[0]; |
| for (int i = 1; i < resultExpressionsCount; ++i) { |
| if (this.originalValueResultExpressionTypes[i] != null) |
| tmp = NullAnnotationMatching.moreDangerousType(tmp, this.originalValueResultExpressionTypes[i]); |
| } |
| return this.resolvedType = tmp; |
| } |
| |
| boolean typeBbolean = true; |
| for (TypeBinding t : this.originalValueResultExpressionTypes) { |
| if (t != null) |
| typeBbolean &= t.id == T_boolean || t.id == T_JavaLangBoolean; |
| } |
| LookupEnvironment env = this.scope.environment(); |
| /* |
| * Otherwise, if the type of each result expression is boolean or Boolean, |
| * an unboxing conversion (5.1.8) is applied to each result expression of type Boolean, |
| * and the switch expression has type boolean. |
| */ |
| if (typeBbolean) { |
| for (int i = 0; i < resultExpressionsCount; ++i) { |
| if (this.originalValueResultExpressionTypes[i].id == T_boolean) continue; |
| this.finalValueResultExpressionTypes[i] = env.computeBoxingType(this.originalValueResultExpressionTypes[i]); |
| this.resultExpressions.get(i).computeConversion(this.scope, this.finalValueResultExpressionTypes[i], this.originalValueResultExpressionTypes[i]); |
| } |
| return this.resolvedType = TypeBinding.BOOLEAN; |
| } |
| |
| /* |
| * Otherwise, if the type of each result expression is convertible to a numeric type (5.1.8), the type |
| * of the switch expression is given by numeric promotion (5.6.3) applied to the result expressions. |
| */ |
| boolean typeNumeric = true; |
| TypeBinding resultNumeric = null; |
| HashSet<TypeBinding> typeSet = new HashSet<>(); |
| /* JLS 12 5.6.3 Switch Numeric Promotion |
| * When a switch expression applies numeric promotion to a set of result expressions, each of which |
| * must denote a value that is convertible to a numeric type, the following rules apply, in order: |
| * If any result expression is of a reference type, it is subjected to unboxing conversion (5.1.8). |
| */ |
| for (int i = 0; i < resultExpressionsCount; ++i) { |
| TypeBinding originalType = this.originalValueResultExpressionTypes[i]; |
| if (originalType == null) continue; |
| tmp = originalType.isNumericType() ? originalType : env.computeBoxingType(originalType); |
| if (!tmp.isNumericType()) { |
| typeNumeric = false; |
| break; |
| } |
| typeSet.add(TypeBinding.wellKnownType(this.scope, tmp.id)); |
| } |
| if (typeNumeric) { |
| /* If any result expression is of type double, then other result expressions that are not of type double |
| * are widened to double. |
| * Otherwise, if any result expression is of type float, then other result expressions that are not of |
| * type float are widened to float. |
| * Otherwise, if any result expression is of type long, then other result expressions that are not of |
| * type long are widened to long. |
| */ |
| TypeBinding[] dfl = new TypeBinding[]{// do not change the order JLS 12 5.6.3 |
| TypeBinding.DOUBLE, |
| TypeBinding.FLOAT, |
| TypeBinding.LONG}; |
| for (TypeBinding binding : dfl) { |
| if (typeSet.contains(binding)) { |
| resultNumeric = binding; |
| break; |
| } |
| } |
| |
| /* Otherwise, if any result expression is of type int and is not a constant expression, the other |
| * result expressions that are not of type int are widened to int. |
| */ |
| resultNumeric = resultNumeric != null ? resultNumeric : check_nonconstant_int(); |
| |
| resultNumeric = resultNumeric != null ? resultNumeric : // one among the first few rules applied. |
| getResultNumeric(typeSet, this.originalValueResultExpressionTypes); // check the rest |
| typeSet = null; // hey gc! |
| for (int i = 0; i < resultExpressionsCount; ++i) { |
| // auto-unboxing and/or widening/narrrowing JLS 12 5.6.3 |
| this.resultExpressions.get(i).computeConversion(this.scope, |
| resultNumeric, this.originalValueResultExpressionTypes[i]); |
| this.finalValueResultExpressionTypes[i] = resultNumeric; |
| } |
| // After the conversion(s), if any, value set conversion (5.1.13) is then applied to each result expression. |
| return this.resolvedType = resultNumeric; |
| } |
| |
| /* Otherwise, boxing conversion (5.1.7) is applied to each result expression that has a primitive type, |
| * after which the type of the switch expression is the result of applying capture conversion (5.1.10) |
| * to the least upper bound (4.10.4) of the types of the result expressions. |
| */ |
| for (int i = 0; i < resultExpressionsCount; ++i) { |
| TypeBinding finalType = this.finalValueResultExpressionTypes[i]; |
| if (finalType != null && finalType.isBaseType()) |
| this.finalValueResultExpressionTypes[i] = env.computeBoxingType(finalType); |
| } |
| TypeBinding commonType = this.scope.lowerUpperBound(this.finalValueResultExpressionTypes); |
| if (commonType != null) { |
| for (int i = 0, l = this.resultExpressions.size(); i < l; ++i) { |
| if (this.originalValueResultExpressionTypes[i] == null) continue; |
| this.resultExpressions.get(i).computeConversion(this.scope, commonType, this.originalValueResultExpressionTypes[i]); |
| this.finalValueResultExpressionTypes[i] = commonType; |
| } |
| return this.resolvedType = commonType.capture(this.scope, this.sourceStart, this.sourceEnd); |
| } |
| this.scope.problemReporter().switchExpressionIncompatibleResultExpressions(this); |
| return null; |
| } finally { |
| if (this.scope != null) this.scope.enclosingCase = null; // no longer inside switch case block |
| } |
| } |
| private TypeBinding check_nonconstant_int() { |
| for (int i = 0, l = this.resultExpressions.size(); i < l; ++i) { |
| Expression e = this.resultExpressions.get(i); |
| TypeBinding type = this.originalValueResultExpressionTypes[i]; |
| if (type != null && type.id == T_int && e.constant == Constant.NotAConstant) |
| return TypeBinding.INT; |
| } |
| return null; |
| } |
| private boolean areAllIntegerResultExpressionsConvertibleToTargetType(TypeBinding targetType) { |
| for (int i = 0, l = this.resultExpressions.size(); i < l; ++i) { |
| Expression e = this.resultExpressions.get(i); |
| TypeBinding t = this.originalValueResultExpressionTypes[i]; |
| if (!TypeBinding.equalsEquals(t, TypeBinding.INT)) continue; |
| if (!e.isConstantValueOfTypeAssignableToType(t, targetType)) |
| return false; |
| } |
| return true; |
| } |
| @Override |
| public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { |
| flowInfo = super.analyseCode(currentScope, flowContext, flowInfo); |
| this.resultExpressionNullStatus = new ArrayList<>(0); |
| final CompilerOptions compilerOptions = currentScope.compilerOptions(); |
| if (compilerOptions.enableSyntacticNullAnalysisForFields) { |
| for (Expression re : this.resultExpressions) { |
| this.resultExpressionNullStatus.add(re.nullStatus(flowInfo, flowContext)); |
| // wipe information that was meant only for this result expression: |
| flowContext.expireNullCheckedFieldInfo(); |
| } |
| } |
| computeNullStatus(flowInfo, flowContext); |
| return flowInfo; |
| } |
| private TypeBinding check_csb(Set<TypeBinding> typeSet, TypeBinding candidate) { |
| if (!typeSet.contains(candidate)) |
| return null; |
| |
| TypeBinding[] allowedTypes = SwitchExpression.type_map.get(candidate); |
| Set<TypeBinding> allowedSet = Arrays.stream(allowedTypes).collect(Collectors.toSet()); |
| |
| if (!allowedSet.containsAll(typeSet)) |
| return null; |
| |
| return areAllIntegerResultExpressionsConvertibleToTargetType(candidate) ? |
| candidate : null; |
| } |
| private TypeBinding getResultNumeric(Set<TypeBinding> typeSet, TypeBinding[] armTypes) { |
| // note: if an expression has a type integer, then it will be a constant |
| // since non-constant integers are already processed before reaching here. |
| |
| /* |
| * Otherwise, if any result expression is of type char, and every other result expression is either of |
| * type char, or of type byte, or a constant expression of type int with a value that is representable |
| * in the type char, then the byte results are widened to char and the int results are narrowed to char. |
| */ |
| |
| /* Otherwise, if any result expression is of type short, and every other result expression is either of |
| * type short, or of type byte, or a constant expression of type int with a value that is representable |
| * in the type short, then the byte results are widened to short and the int results are narrowed to |
| * short. |
| */ |
| /* Otherwise, if any result expression is of type byte, and every other result expression is either of |
| * type byte or a constant expression of type int with a value that is representable in the type byte, |
| * then the int results are narrowed to byte. |
| */ |
| |
| // DO NOT Change the order below [as per JLS 12 5.6.3 item 2, sub-items 5,6 and 7]. |
| TypeBinding[] csb = new TypeBinding[] {TypeBinding.CHAR, TypeBinding.SHORT, TypeBinding.BYTE}; |
| for (TypeBinding c : csb) { |
| TypeBinding result = check_csb(typeSet, c); |
| if (result != null) |
| return result; |
| } |
| /* Otherwise, all the result expressions that are not of type int are widened to int. */ |
| return TypeBinding.INT; |
| } |
| @Override |
| public boolean isPolyExpression() { |
| if (this.isPolyExpression) |
| return true; |
| // JLS 12 15.28.1 A switch expression is a poly expression if it appears in an assignment context or |
| // an invocation context (5.2, 5.3). Otherwise, it is a standalone expression. |
| return this.isPolyExpression = this.expressionContext == ASSIGNMENT_CONTEXT || |
| this.expressionContext == INVOCATION_CONTEXT; |
| } |
| @Override |
| public boolean isTrulyExpression() { |
| return true; |
| } |
| @Override |
| public boolean isCompatibleWith(TypeBinding left, Scope skope) { |
| if (!isPolyExpression()) |
| return super.isCompatibleWith(left, skope); |
| |
| for (Expression e : this.resultExpressions) { |
| if (!e.isCompatibleWith(left, skope)) |
| return false; |
| } |
| return true; |
| } |
| @Override |
| public boolean isBoxingCompatibleWith(TypeBinding targetType, Scope skope) { |
| if (!isPolyExpression()) |
| return super.isBoxingCompatibleWith(targetType, skope); |
| |
| for (Expression e : this.resultExpressions) { |
| if (!(e.isCompatibleWith(targetType, skope) || e.isBoxingCompatibleWith(targetType, skope))) |
| return false; |
| } |
| return true; |
| } |
| @Override |
| public boolean sIsMoreSpecific(TypeBinding s, TypeBinding t, Scope skope) { |
| if (super.sIsMoreSpecific(s, t, skope)) |
| return true; |
| if (!isPolyExpression()) |
| return false; |
| for (Expression e : this.resultExpressions) { |
| if (!e.sIsMoreSpecific(s, t, skope)) |
| return false; |
| } |
| return true; |
| } |
| @Override |
| public TypeBinding expectedType() { |
| return this.expectedType; |
| } |
| @Override |
| public void traverse( |
| ASTVisitor visitor, |
| BlockScope blockScope) { |
| |
| if (visitor.visit(this, blockScope)) { |
| this.expression.traverse(visitor, blockScope); |
| if (this.statements != null) { |
| int statementsLength = this.statements.length; |
| for (int i = 0; i < statementsLength; i++) |
| this.statements[i].traverse(visitor, this.scope); |
| } |
| } |
| visitor.endVisit(this, blockScope); |
| } |
| } |