1 /******************************************************************************* 2 * Copyright (c) 2018, 2020 IBM Corporation and others. 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * https://www.eclipse.org/legal/epl-2.0/ 7 * 8 * SPDX-License-Identifier: EPL-2.0 9 * 10 * Contributors: 11 * IBM Corporation - initial API and implementation 12 *******************************************************************************/ 13 package org.eclipse.jdt.internal.compiler.ast; 14 15 import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.ASSIGNMENT_CONTEXT; 16 import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.INVOCATION_CONTEXT; 17 import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.VANILLA_CONTEXT; 18 19 import java.util.ArrayList; 20 import java.util.Arrays; 21 import java.util.EmptyStackException; 22 import java.util.HashMap; 23 import java.util.HashSet; 24 import java.util.List; 25 import java.util.Map; 26 import java.util.Set; 27 import java.util.Stack; 28 import java.util.stream.Collectors; 29 30 import org.eclipse.jdt.core.compiler.CharOperation; 31 import org.eclipse.jdt.internal.compiler.ASTVisitor; 32 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; 33 import org.eclipse.jdt.internal.compiler.codegen.CodeStream; 34 import org.eclipse.jdt.internal.compiler.flow.FlowContext; 35 import org.eclipse.jdt.internal.compiler.flow.FlowInfo; 36 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; 37 import org.eclipse.jdt.internal.compiler.impl.Constant; 38 import org.eclipse.jdt.internal.compiler.lookup.Binding; 39 import org.eclipse.jdt.internal.compiler.lookup.BlockScope; 40 import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; 41 import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; 42 import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; 43 import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; 44 import org.eclipse.jdt.internal.compiler.lookup.PolyTypeBinding; 45 import org.eclipse.jdt.internal.compiler.lookup.Scope; 46 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; 47 import org.eclipse.jdt.internal.compiler.lookup.TypeIds; 48 49 public class SwitchExpression extends SwitchStatement implements IPolyExpression { 50 51 /* package */ TypeBinding expectedType; 52 private ExpressionContext expressionContext = VANILLA_CONTEXT; 53 private boolean isPolyExpression = false; 54 private TypeBinding[] originalValueResultExpressionTypes; 55 private TypeBinding[] finalValueResultExpressionTypes; 56 57 private int nullStatus = FlowInfo.UNKNOWN; 58 public List<Expression> resultExpressions; 59 public boolean resolveAll; 60 /* package */ List<Integer> resultExpressionNullStatus; 61 LocalVariableBinding hiddenYield; 62 /* package */ int hiddenYieldResolvedPosition = -1; 63 public boolean containsTry = false; 64 private static Map<TypeBinding, TypeBinding[]> type_map; 65 static final char[] SECRET_YIELD_VALUE_NAME = " yieldValue".toCharArray(); //$NON-NLS-1$ 66 int yieldResolvedPosition = -1; 67 List<LocalVariableBinding> typesOnStack; 68 69 static { 70 type_map = new HashMap<TypeBinding, TypeBinding[]>(); type_map.put(TypeBinding.CHAR, new TypeBinding[] {TypeBinding.CHAR, TypeBinding.INT})71 type_map.put(TypeBinding.CHAR, new TypeBinding[] {TypeBinding.CHAR, TypeBinding.INT}); type_map.put(TypeBinding.SHORT, new TypeBinding[] {TypeBinding.SHORT, TypeBinding.BYTE, TypeBinding.INT})72 type_map.put(TypeBinding.SHORT, new TypeBinding[] {TypeBinding.SHORT, TypeBinding.BYTE, TypeBinding.INT}); type_map.put(TypeBinding.BYTE, new TypeBinding[] {TypeBinding.BYTE, TypeBinding.INT})73 type_map.put(TypeBinding.BYTE, new TypeBinding[] {TypeBinding.BYTE, TypeBinding.INT}); 74 } 75 76 @Override setExpressionContext(ExpressionContext context)77 public void setExpressionContext(ExpressionContext context) { 78 this.expressionContext = context; 79 } 80 81 @Override setExpectedType(TypeBinding expectedType)82 public void setExpectedType(TypeBinding expectedType) { 83 this.expectedType = expectedType; 84 } 85 86 @Override getExpressionContext()87 public ExpressionContext getExpressionContext() { 88 return this.expressionContext; 89 } 90 @Override ignoreMissingDefaultCase(CompilerOptions compilerOptions, boolean isEnumSwitch)91 protected boolean ignoreMissingDefaultCase(CompilerOptions compilerOptions, boolean isEnumSwitch) { 92 return isEnumSwitch; // mandatory error if not enum in switch expressions 93 } 94 @Override reportMissingEnumConstantCase(BlockScope upperScope, FieldBinding enumConstant)95 protected void reportMissingEnumConstantCase(BlockScope upperScope, FieldBinding enumConstant) { 96 upperScope.problemReporter().missingEnumConstantCase(this, enumConstant); 97 } 98 @Override getFallThroughState(Statement stmt, BlockScope blockScope)99 protected int getFallThroughState(Statement stmt, BlockScope blockScope) { 100 if ((stmt instanceof Expression && ((Expression) stmt).isTrulyExpression())|| stmt instanceof ThrowStatement) 101 return BREAKING; 102 if (this.switchLabeledRules // do this check for every block if '->' (Switch Labeled Rules) 103 && stmt instanceof Block) { 104 Block block = (Block) stmt; 105 if (!block.canCompleteNormally()) { 106 return BREAKING; 107 } 108 } 109 return FALLTHROUGH; 110 } 111 @Override checkNPE(BlockScope skope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck)112 public boolean checkNPE(BlockScope skope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) { 113 if ((this.nullStatus & FlowInfo.NULL) != 0) 114 skope.problemReporter().expressionNullReference(this); 115 else if ((this.nullStatus & FlowInfo.POTENTIALLY_NULL) != 0) 116 skope.problemReporter().expressionPotentialNullReference(this); 117 return true; // all checking done 118 } 119 computeNullStatus(FlowInfo flowInfo, FlowContext flowContext)120 private void computeNullStatus(FlowInfo flowInfo, FlowContext flowContext) { 121 boolean precomputed = this.resultExpressionNullStatus.size() > 0; 122 if (!precomputed) 123 this.resultExpressionNullStatus.add(this.resultExpressions.get(0).nullStatus(flowInfo, flowContext)); int status = this.resultExpressions.get(0).nullStatus(flowInfo, flowContext); 124 int combinedStatus = status; 125 boolean identicalStatus = true; 126 for (int i = 1, l = this.resultExpressions.size(); i < l; ++i) { 127 if (!precomputed) 128 this.resultExpressionNullStatus.add(this.resultExpressions.get(i).nullStatus(flowInfo, flowContext)); 129 int tmp = this.resultExpressions.get(i).nullStatus(flowInfo, flowContext); 130 identicalStatus &= status == tmp; 131 combinedStatus |= tmp; 132 } 133 if (identicalStatus) { 134 this.nullStatus = status; 135 return; 136 } 137 status = Expression.computeNullStatus(0, combinedStatus); 138 if (status > 0) 139 this.nullStatus = status; 140 } 141 142 @Override completeNormallyCheck(BlockScope blockScope)143 protected void completeNormallyCheck(BlockScope blockScope) { 144 int sz = this.statements != null ? this.statements.length : 0; 145 if (sz == 0) return; 146 /* JLS 12 15.28.1 Given a switch expression, if the switch block consists of switch labeled rules 147 * then it is a compile-time error if any switch labeled block can complete normally. 148 */ 149 if (this.switchLabeledRules) { 150 for (Statement stmt : this.statements) { 151 if (!(stmt instanceof Block)) 152 continue; 153 if (stmt.canCompleteNormally()) 154 blockScope.problemReporter().switchExpressionLastStatementCompletesNormally(stmt); 155 } 156 return; 157 } 158 /* JLS 12 15.28.1 159 * If, on the other hand, the switch block consists of switch labeled statement groups, then it is a 160 * compile-time error if either the last statement in the switch block can complete normally, or the 161 * switch block includes one or more switch labels at the end. 162 */ 163 Statement lastNonCaseStmt = null; 164 Statement firstTrailingCaseStmt = null; 165 for (int i = sz - 1; i >= 0; i--) { 166 Statement stmt = this.statements[sz - 1]; 167 if (stmt instanceof CaseStatement) 168 firstTrailingCaseStmt = stmt; 169 else { 170 lastNonCaseStmt = stmt; 171 break; 172 } 173 } 174 if (lastNonCaseStmt != null) { 175 if (lastNonCaseStmt.canCompleteNormally()) 176 blockScope.problemReporter().switchExpressionLastStatementCompletesNormally(lastNonCaseStmt); 177 else if (lastNonCaseStmt instanceof ContinueStatement || lastNonCaseStmt instanceof ReturnStatement) { 178 blockScope.problemReporter().switchExpressionIllegalLastStatement(lastNonCaseStmt); 179 } 180 } 181 if (firstTrailingCaseStmt != null) { 182 blockScope.problemReporter().switchExpressionTrailingSwitchLabels(firstTrailingCaseStmt); 183 } 184 } 185 @Override needToCheckFlowInAbsenceOfDefaultBranch()186 protected boolean needToCheckFlowInAbsenceOfDefaultBranch() { // JLS 12 16.1.8 187 return !this.switchLabeledRules; 188 } 189 @Override getPolyExpressions()190 public Expression[] getPolyExpressions() { 191 List<Expression> polys = new ArrayList<>(); 192 for (Expression e : this.resultExpressions) { 193 Expression[] ea = e.getPolyExpressions(); 194 if (ea == null || ea.length ==0) continue; 195 polys.addAll(Arrays.asList(ea)); 196 } 197 return polys.toArray(new Expression[0]); 198 } 199 @Override isPertinentToApplicability(TypeBinding targetType, MethodBinding method)200 public boolean isPertinentToApplicability(TypeBinding targetType, MethodBinding method) { 201 for (Expression e : this.resultExpressions) { 202 if (!e.isPertinentToApplicability(targetType, method)) 203 return false; 204 } 205 return true; 206 } 207 @Override isPotentiallyCompatibleWith(TypeBinding targetType, Scope scope1)208 public boolean isPotentiallyCompatibleWith(TypeBinding targetType, Scope scope1) { 209 for (Expression e : this.resultExpressions) { 210 if (!e.isPotentiallyCompatibleWith(targetType, scope1)) 211 return false; 212 } 213 return true; 214 } 215 @Override isFunctionalType()216 public boolean isFunctionalType() { 217 for (Expression e : this.resultExpressions) { 218 if (e.isFunctionalType()) // return true even for one functional type 219 return true; 220 } 221 return false; 222 } 223 @Override nullStatus(FlowInfo flowInfo, FlowContext flowContext)224 public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) { 225 if ((this.implicitConversion & TypeIds.BOXING) != 0) 226 return FlowInfo.NON_NULL; 227 return this.nullStatus; 228 } 229 @Override statementGenerateCode(BlockScope currentScope, CodeStream codeStream, Statement statement)230 protected void statementGenerateCode(BlockScope currentScope, CodeStream codeStream, Statement statement) { 231 if (!(statement instanceof Expression && ((Expression) statement).isTrulyExpression()) 232 || statement instanceof Assignment 233 || statement instanceof MessageSend 234 || (statement instanceof SwitchStatement && !(statement instanceof SwitchExpression))) { 235 super.statementGenerateCode(currentScope, codeStream, statement); 236 return; 237 } 238 Expression expression1 = (Expression) statement; 239 expression1.generateCode(currentScope, codeStream, true /* valueRequired */); 240 } createType(int typeId)241 private TypeBinding createType(int typeId) { 242 TypeBinding type = TypeBinding.wellKnownType(this.scope, typeId); 243 return type != null ? type : this.scope.getJavaLangObject(); 244 } addTypeStackVariable(CodeStream codeStream, TypeBinding type, int typeId, int index, int resolvedPosition)245 private LocalVariableBinding addTypeStackVariable(CodeStream codeStream, TypeBinding type, int typeId, int index, int resolvedPosition) { 246 char[] name = CharOperation.concat(SECRET_YIELD_VALUE_NAME, String.valueOf(index).toCharArray()); 247 type = type != null ? type : createType(typeId); 248 LocalVariableBinding lvb = 249 new LocalVariableBinding( 250 name, 251 type, 252 ClassFileConstants.AccDefault, 253 false); 254 lvb.setConstant(Constant.NotAConstant); 255 lvb.useFlag = LocalVariableBinding.USED; 256 lvb.resolvedPosition = resolvedPosition; 257 // if (this.offset > 0xFFFF) { // no more than 65535 words of locals // TODO - also the cumulative at MethodScope 258 // problemReporter().noMoreAvailableSpaceForLocal( 259 // local, 260 // local.declaration == null ? (ASTNode)methodScope().referenceContext : local.declaration); 261 // } 262 this.scope.addLocalVariable(lvb); 263 lvb.declaration = new LocalDeclaration(name, 0, 0); 264 return lvb; 265 } getNextOffset(LocalVariableBinding local)266 private int getNextOffset(LocalVariableBinding local) { 267 int delta = ((TypeBinding.equalsEquals(local.type, TypeBinding.LONG)) || (TypeBinding.equalsEquals(local.type, TypeBinding.DOUBLE))) ? 268 2 : 1; 269 return local.resolvedPosition + delta; 270 } processTypesBindingsOnStack(CodeStream codeStream)271 private void processTypesBindingsOnStack(CodeStream codeStream) { 272 int count = 0; 273 int nextResolvedPosition = this.scope.offset; 274 if (!codeStream.switchSaveTypeBindings.empty()) { 275 this.typesOnStack = new ArrayList<>(); 276 int index = 0; 277 Stack<TypeBinding> typeStack = new Stack<>(); 278 int sz = codeStream.switchSaveTypeBindings.size(); 279 for (int i = codeStream.lastSwitchCumulativeSyntheticVars; i < sz; ++i) { 280 typeStack.add(codeStream.switchSaveTypeBindings.get(i)); 281 } 282 while (!typeStack.empty()) { 283 TypeBinding type = typeStack.pop(); 284 LocalVariableBinding lvb = addTypeStackVariable(codeStream, type, TypeIds.T_undefined, index++, nextResolvedPosition); 285 nextResolvedPosition = getNextOffset(lvb); 286 this.typesOnStack.add(lvb); 287 codeStream.store(lvb, false); 288 codeStream.addVariable(lvb); 289 ++count; 290 } 291 } 292 // now keep a position reserved for yield result value 293 this.yieldResolvedPosition = nextResolvedPosition; 294 nextResolvedPosition += ((TypeBinding.equalsEquals(this.resolvedType, TypeBinding.LONG)) || 295 (TypeBinding.equalsEquals(this.resolvedType, TypeBinding.DOUBLE))) ? 296 2 : 1; 297 298 codeStream.lastSwitchCumulativeSyntheticVars += count + 1; // 1 for yield var 299 int delta = nextResolvedPosition - this.scope.offset; 300 this.scope.adjustLocalVariablePositions(delta, false); 301 } loadStoredTypesAndKeep(CodeStream codeStream)302 public void loadStoredTypesAndKeep(CodeStream codeStream) { 303 List<LocalVariableBinding> tos = this.typesOnStack; 304 int sz = tos != null ? tos.size() : 0; 305 int index = sz - 1; 306 while (index >= 0) { 307 LocalVariableBinding lvb = tos.get(index--); 308 codeStream.load(lvb); 309 // lvb.recordInitializationEndPC(codeStream.position); 310 // codeStream.removeVariable(lvb); 311 } 312 } removeStoredTypes(CodeStream codeStream)313 private void removeStoredTypes(CodeStream codeStream) { 314 List<LocalVariableBinding> tos = this.typesOnStack; 315 int sz = tos != null ? tos.size() : 0; 316 int index = sz - 1; 317 while (index >= 0) { 318 LocalVariableBinding lvb = tos.get(index--); 319 codeStream.removeVariable(lvb); 320 } 321 } 322 @Override generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired)323 public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { 324 int tmp = 0; 325 if (this.containsTry) { 326 tmp = codeStream.lastSwitchCumulativeSyntheticVars; 327 processTypesBindingsOnStack(codeStream); 328 } 329 super.generateCode(currentScope, codeStream); 330 if (this.containsTry) { 331 removeStoredTypes(codeStream); 332 codeStream.lastSwitchCumulativeSyntheticVars = tmp; 333 } 334 if (!valueRequired) { 335 // switch expression is saved to a variable that is not used. We need to pop the generated value from the stack 336 switch(postConversionType(currentScope).id) { 337 case TypeIds.T_long : 338 case TypeIds.T_double : 339 codeStream.pop2(); 340 break; 341 case TypeIds.T_void : 342 break; 343 default : 344 codeStream.pop(); 345 break; 346 } 347 } 348 } computeConversions(BlockScope blockScope, TypeBinding targetType)349 protected boolean computeConversions(BlockScope blockScope, TypeBinding targetType) { 350 boolean ok = true; 351 for (int i = 0, l = this.resultExpressions.size(); i < l; ++i) { 352 ok &= computeConversionsResultExpressions(blockScope, targetType, this.originalValueResultExpressionTypes[i], this.resultExpressions.get(i)); 353 } 354 return ok; 355 } computeConversionsResultExpressions(BlockScope blockScope, TypeBinding targetType, TypeBinding resultExpressionType, Expression resultExpression)356 private boolean computeConversionsResultExpressions(BlockScope blockScope, TypeBinding targetType, TypeBinding resultExpressionType, 357 Expression resultExpression) { 358 if (resultExpressionType != null && resultExpressionType.isValidBinding()) { 359 if (resultExpression.isConstantValueOfTypeAssignableToType(resultExpressionType, targetType) 360 || resultExpressionType.isCompatibleWith(targetType)) { 361 362 resultExpression.computeConversion(blockScope, targetType, resultExpressionType); 363 if (resultExpressionType.needsUncheckedConversion(targetType)) { 364 blockScope.problemReporter().unsafeTypeConversion(resultExpression, resultExpressionType, targetType); 365 } 366 if (resultExpression instanceof CastExpression 367 && (resultExpression.bits & (ASTNode.UnnecessaryCast|ASTNode.DisableUnnecessaryCastCheck)) == 0) { 368 CastExpression.checkNeedForAssignedCast(blockScope, targetType, (CastExpression) resultExpression); 369 } 370 } else if (isBoxingCompatible(resultExpressionType, targetType, resultExpression, blockScope)) { 371 resultExpression.computeConversion(blockScope, targetType, resultExpressionType); 372 if (resultExpression instanceof CastExpression 373 && (resultExpression.bits & (ASTNode.UnnecessaryCast|ASTNode.DisableUnnecessaryCastCheck)) == 0) { 374 CastExpression.checkNeedForAssignedCast(blockScope, targetType, (CastExpression) resultExpression); 375 } 376 } else { 377 blockScope.problemReporter().typeMismatchError(resultExpressionType, targetType, resultExpression, null); 378 return false; 379 } 380 } 381 return true; 382 } 383 class OOBLFlagger extends ASTVisitor { 384 Set<String> labelDecls; 385 Set<BreakStatement> referencedBreakLabels; 386 Set<ContinueStatement> referencedContinueLabels; OOBLFlagger(SwitchExpression se)387 public OOBLFlagger(SwitchExpression se) { 388 this.labelDecls = new HashSet<>(); 389 this.referencedBreakLabels = new HashSet<>(); 390 this.referencedContinueLabels = new HashSet<>(); 391 } 392 @Override visit(SwitchExpression switchExpression, BlockScope blockScope)393 public boolean visit(SwitchExpression switchExpression, BlockScope blockScope) { 394 return true; 395 } checkForOutofBoundLabels(BlockScope blockScope)396 private void checkForOutofBoundLabels(BlockScope blockScope) { 397 try { 398 for (BreakStatement bs : this.referencedBreakLabels) { 399 if (bs.label == null || bs.label.length == 0) 400 continue; 401 if (!this.labelDecls.contains(new String(bs.label))) 402 blockScope.problemReporter().switchExpressionsBreakOutOfSwitchExpression(bs); 403 } 404 for (ContinueStatement cs : this.referencedContinueLabels) { 405 if (cs.label == null || cs.label.length == 0) 406 continue; 407 if (!this.labelDecls.contains(new String(cs.label))) 408 blockScope.problemReporter().switchExpressionsContinueOutOfSwitchExpression(cs); 409 } 410 } catch (EmptyStackException e) { 411 // ignore 412 } 413 } 414 415 @Override endVisit(SwitchExpression switchExpression, BlockScope blockScope)416 public void endVisit(SwitchExpression switchExpression, BlockScope blockScope) { 417 checkForOutofBoundLabels(blockScope); 418 } 419 @Override visit(BreakStatement breakStatement, BlockScope blockScope)420 public boolean visit(BreakStatement breakStatement, BlockScope blockScope) { 421 if (breakStatement.label != null && breakStatement.label.length != 0) 422 this.referencedBreakLabels.add(breakStatement); 423 return true; 424 } 425 @Override visit(ContinueStatement continueStatement, BlockScope blockScope)426 public boolean visit(ContinueStatement continueStatement, BlockScope blockScope) { 427 if (continueStatement.label != null && continueStatement.label.length != 0) 428 this.referencedContinueLabels.add(continueStatement); 429 return true; 430 } 431 @Override visit(LambdaExpression lambdaExpression, BlockScope blockScope)432 public boolean visit(LambdaExpression lambdaExpression, BlockScope blockScope) { 433 return false; 434 } 435 @Override visit(LabeledStatement stmt, BlockScope blockScope)436 public boolean visit(LabeledStatement stmt, BlockScope blockScope) { 437 if (stmt.label != null && stmt.label.length != 0) 438 this.labelDecls.add(new String(stmt.label)); 439 return true; 440 } 441 @Override visit(ReturnStatement stmt, BlockScope blockScope)442 public boolean visit(ReturnStatement stmt, BlockScope blockScope) { 443 blockScope.problemReporter().switchExpressionsReturnWithinSwitchExpression(stmt); 444 return false; 445 } 446 @Override visit(TypeDeclaration stmt, BlockScope blockScope)447 public boolean visit(TypeDeclaration stmt, BlockScope blockScope) { 448 return false; 449 } 450 } 451 @Override resolveType(BlockScope upperScope)452 public TypeBinding resolveType(BlockScope upperScope) { 453 return resolveTypeInternal(upperScope); 454 } resolveTypeInternal(BlockScope upperScope)455 public TypeBinding resolveTypeInternal(BlockScope upperScope) { 456 try { 457 int resultExpressionsCount; 458 if (this.constant != Constant.NotAConstant) { 459 this.constant = Constant.NotAConstant; 460 461 // A switch expression is a poly expression if it appears in an assignment context or an invocation context (5.2, 5.3). 462 // Otherwise, it is a standalone expression. 463 if (this.expressionContext == ASSIGNMENT_CONTEXT || this.expressionContext == INVOCATION_CONTEXT) { 464 for (Expression e : this.resultExpressions) { 465 //Where a poly switch expression appears in a context of a particular kind with target type T, 466 //its result expressions similarly appear in a context of the same kind with target type T. 467 e.setExpressionContext(this.expressionContext); 468 e.setExpectedType(this.expectedType); 469 } 470 } 471 472 resolve(upperScope); 473 474 if (this.statements == null || this.statements.length == 0) { 475 // Report Error JLS 13 15.28.1 The switch block must not be empty. 476 upperScope.problemReporter().switchExpressionEmptySwitchBlock(this); 477 return null; 478 } 479 480 resultExpressionsCount = this.resultExpressions != null ? this.resultExpressions.size() : 0; 481 if (resultExpressionsCount == 0) { 482 // Report Error JLS 13 15.28.1 483 // It is a compile-time error if a switch expression has no result expressions. 484 upperScope.problemReporter().switchExpressionNoResultExpressions(this); 485 return null; 486 } 487 this.traverse(new OOBLFlagger(this), upperScope); 488 489 if (this.originalValueResultExpressionTypes == null) { 490 this.originalValueResultExpressionTypes = new TypeBinding[resultExpressionsCount]; 491 this.finalValueResultExpressionTypes = new TypeBinding[resultExpressionsCount]; 492 for (int i = 0; i < resultExpressionsCount; ++i) { 493 this.finalValueResultExpressionTypes[i] = this.originalValueResultExpressionTypes[i] = 494 this.resultExpressions.get(i).resolvedType; 495 } 496 } 497 if (isPolyExpression()) { //The type of a poly switch expression is the same as its target type. 498 if (this.expectedType == null || !this.expectedType.isProperType(true)) { 499 return new PolyTypeBinding(this); 500 } 501 return this.resolvedType = computeConversions(this.scope, this.expectedType) ? this.expectedType : null; 502 } 503 // fall through 504 } else { 505 // re-resolving of poly expression: 506 resultExpressionsCount = this.resultExpressions != null ? this.resultExpressions.size() : 0; 507 if (resultExpressionsCount == 0) 508 return this.resolvedType = null; // error flagging would have been done during the earlier phase. 509 for (int i = 0; i < resultExpressionsCount; i++) { 510 Expression resultExpr = this.resultExpressions.get(i); 511 if (resultExpr.resolvedType == null || resultExpr.resolvedType.kind() == Binding.POLY_TYPE) { 512 this.finalValueResultExpressionTypes[i] = this.originalValueResultExpressionTypes[i] = 513 resultExpr.resolveTypeExpecting(upperScope, this.expectedType); 514 } 515 // This is a kludge and only way completion can tell this node to resolve all 516 // resultExpressions. Ideal solution is to remove all other expressions except 517 // the one that contain the completion node. 518 if (this.resolveAll) continue; 519 if (resultExpr.resolvedType == null || !resultExpr.resolvedType.isValidBinding()) 520 return this.resolvedType = null; 521 } 522 this.resolvedType = computeConversions(this.scope, this.expectedType) ? this.expectedType : null; 523 // fall through 524 } 525 526 if (resultExpressionsCount == 1) 527 return this.resolvedType = this.originalValueResultExpressionTypes[0]; 528 529 boolean typeUniformAcrossAllArms = true; 530 TypeBinding tmp = this.originalValueResultExpressionTypes[0]; 531 for (int i = 1, l = this.originalValueResultExpressionTypes.length; i < l; ++i) { 532 TypeBinding originalType = this.originalValueResultExpressionTypes[i]; 533 if (originalType != null && TypeBinding.notEquals(tmp, originalType)) { 534 typeUniformAcrossAllArms = false; 535 break; 536 } 537 } 538 // If the result expressions all have the same type (which may be the null type), 539 // then that is the type of the switch expression. 540 if (typeUniformAcrossAllArms) { 541 tmp = this.originalValueResultExpressionTypes[0]; 542 for (int i = 1; i < resultExpressionsCount; ++i) { 543 if (this.originalValueResultExpressionTypes[i] != null) 544 tmp = NullAnnotationMatching.moreDangerousType(tmp, this.originalValueResultExpressionTypes[i]); 545 } 546 return this.resolvedType = tmp; 547 } 548 549 boolean typeBbolean = true; 550 for (TypeBinding t : this.originalValueResultExpressionTypes) { 551 if (t != null) 552 typeBbolean &= t.id == T_boolean || t.id == T_JavaLangBoolean; 553 } 554 LookupEnvironment env = this.scope.environment(); 555 /* 556 * Otherwise, if the type of each result expression is boolean or Boolean, 557 * an unboxing conversion (5.1.8) is applied to each result expression of type Boolean, 558 * and the switch expression has type boolean. 559 */ 560 if (typeBbolean) { 561 for (int i = 0; i < resultExpressionsCount; ++i) { 562 if (this.originalValueResultExpressionTypes[i] == null) continue; 563 if (this.originalValueResultExpressionTypes[i].id == T_boolean) continue; 564 this.finalValueResultExpressionTypes[i] = env.computeBoxingType(this.originalValueResultExpressionTypes[i]); 565 this.resultExpressions.get(i).computeConversion(this.scope, this.finalValueResultExpressionTypes[i], this.originalValueResultExpressionTypes[i]); 566 } 567 return this.resolvedType = TypeBinding.BOOLEAN; 568 } 569 570 /* 571 * Otherwise, if the type of each result expression is convertible to a numeric type (5.1.8), the type 572 * of the switch expression is given by numeric promotion (5.6.3) applied to the result expressions. 573 */ 574 boolean typeNumeric = true; 575 TypeBinding resultNumeric = null; 576 HashSet<TypeBinding> typeSet = new HashSet<>(); 577 /* JLS 13 5.6 Numeric Contexts 578 * An expression appears in a numeric context if it is one of:.... 579 * ...8. a result expression of a standalone switch expression (15.28.1), 580 * where all the result expressions are convertible to a numeric type 581 * If any expression is of a reference type, it is subjected to unboxing conversion (5.1.8). 582 */ 583 for (int i = 0; i < resultExpressionsCount; ++i) { 584 TypeBinding originalType = this.originalValueResultExpressionTypes[i]; 585 if (originalType == null) continue; 586 tmp = originalType.isNumericType() ? originalType : env.computeBoxingType(originalType); 587 if (!tmp.isNumericType()) { 588 typeNumeric = false; 589 break; 590 } 591 typeSet.add(TypeBinding.wellKnownType(this.scope, tmp.id)); 592 } 593 if (typeNumeric) { 594 /* If any result expression is of type double, then other result expressions that are not of type double 595 * are widened to double. 596 * Otherwise, if any result expression is of type float, then other result expressions that are not of 597 * type float are widened to float. 598 * Otherwise, if any result expression is of type long, then other result expressions that are not of 599 * type long are widened to long. 600 */ 601 TypeBinding[] dfl = new TypeBinding[]{// do not change the order JLS 13 5.6 602 TypeBinding.DOUBLE, 603 TypeBinding.FLOAT, 604 TypeBinding.LONG}; 605 for (TypeBinding binding : dfl) { 606 if (typeSet.contains(binding)) { 607 resultNumeric = binding; 608 break; 609 } 610 } 611 612 /* Otherwise, if any expression appears in a numeric array context or a numeric arithmetic context, 613 * rather than a numeric choice context, then the promoted type is int and other expressions that are 614 * not of type int undergo widening primitive conversion to int. - not applicable since numeric choice context. 615 * [Note: A numeric choice context is a numeric context that is either a numeric conditional expression or 616 * a standalone switch expression where all the result expressions are convertible to a numeric type.] 617 */ 618 619 /* Otherwise, if any result expression is of type int and is not a constant expression, the other 620 * result expressions that are not of type int are widened to int. 621 */ 622 resultNumeric = resultNumeric != null ? resultNumeric : check_nonconstant_int(); 623 624 resultNumeric = resultNumeric != null ? resultNumeric : // one among the first few rules applied. 625 getResultNumeric(typeSet); // check the rest 626 typeSet = null; // hey gc! 627 for (int i = 0; i < resultExpressionsCount; ++i) { 628 this.resultExpressions.get(i).computeConversion(this.scope, 629 resultNumeric, this.originalValueResultExpressionTypes[i]); 630 this.finalValueResultExpressionTypes[i] = resultNumeric; 631 } 632 // After the conversion(s), if any, value set conversion (5.1.13) is then applied to each result expression. 633 return this.resolvedType = resultNumeric; 634 } 635 636 /* Otherwise, boxing conversion (5.1.7) is applied to each result expression that has a primitive type, 637 * after which the type of the switch expression is the result of applying capture conversion (5.1.10) 638 * to the least upper bound (4.10.4) of the types of the result expressions. 639 */ 640 for (int i = 0; i < resultExpressionsCount; ++i) { 641 TypeBinding finalType = this.finalValueResultExpressionTypes[i]; 642 if (finalType != null && finalType.isBaseType()) 643 this.finalValueResultExpressionTypes[i] = env.computeBoxingType(finalType); 644 } 645 TypeBinding commonType = this.scope.lowerUpperBound(this.finalValueResultExpressionTypes); 646 if (commonType != null) { 647 for (int i = 0, l = this.resultExpressions.size(); i < l; ++i) { 648 if (this.originalValueResultExpressionTypes[i] == null) continue; 649 this.resultExpressions.get(i).computeConversion(this.scope, commonType, this.originalValueResultExpressionTypes[i]); 650 this.finalValueResultExpressionTypes[i] = commonType; 651 } 652 return this.resolvedType = commonType.capture(this.scope, this.sourceStart, this.sourceEnd); 653 } 654 this.scope.problemReporter().switchExpressionIncompatibleResultExpressions(this); 655 return null; 656 } finally { 657 if (this.scope != null) this.scope.enclosingCase = null; // no longer inside switch case block 658 } 659 } check_nonconstant_int()660 private TypeBinding check_nonconstant_int() { 661 for (int i = 0, l = this.resultExpressions.size(); i < l; ++i) { 662 Expression e = this.resultExpressions.get(i); 663 TypeBinding type = this.originalValueResultExpressionTypes[i]; 664 if (type != null && type.id == T_int && e.constant == Constant.NotAConstant) 665 return TypeBinding.INT; 666 } 667 return null; 668 } areAllIntegerResultExpressionsConvertibleToTargetType(TypeBinding targetType)669 private boolean areAllIntegerResultExpressionsConvertibleToTargetType(TypeBinding targetType) { 670 for (int i = 0, l = this.resultExpressions.size(); i < l; ++i) { 671 Expression e = this.resultExpressions.get(i); 672 TypeBinding t = this.originalValueResultExpressionTypes[i]; 673 if (!TypeBinding.equalsEquals(t, TypeBinding.INT)) continue; 674 if (!e.isConstantValueOfTypeAssignableToType(t, targetType)) 675 return false; 676 } 677 return true; 678 } 679 @Override analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo)680 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { 681 flowInfo = super.analyseCode(currentScope, flowContext, flowInfo); 682 this.resultExpressionNullStatus = new ArrayList<>(0); 683 final CompilerOptions compilerOptions = currentScope.compilerOptions(); 684 if (compilerOptions.enableSyntacticNullAnalysisForFields) { 685 for (Expression re : this.resultExpressions) { 686 this.resultExpressionNullStatus.add(re.nullStatus(flowInfo, flowContext)); 687 // wipe information that was meant only for this result expression: 688 flowContext.expireNullCheckedFieldInfo(); 689 } 690 } 691 computeNullStatus(flowInfo, flowContext); 692 return flowInfo; 693 } 694 @Override addSecretTryResultVariable()695 protected void addSecretTryResultVariable() { 696 if (this.containsTry) { 697 this.hiddenYield = 698 new LocalVariableBinding( 699 SwitchExpression.SECRET_YIELD_VALUE_NAME, 700 null, 701 ClassFileConstants.AccDefault, 702 false); 703 this.hiddenYield.setConstant(Constant.NotAConstant); 704 this.hiddenYield.useFlag = LocalVariableBinding.USED; 705 this.scope.addLocalVariable(this.hiddenYield); 706 this.hiddenYield.declaration = new LocalDeclaration(SECRET_YIELD_VALUE_NAME, 0, 0); 707 } 708 } check_csb(Set<TypeBinding> typeSet, TypeBinding candidate)709 private TypeBinding check_csb(Set<TypeBinding> typeSet, TypeBinding candidate) { 710 if (!typeSet.contains(candidate)) 711 return null; 712 713 TypeBinding[] allowedTypes = SwitchExpression.type_map.get(candidate); 714 Set<TypeBinding> allowedSet = Arrays.stream(allowedTypes).collect(Collectors.toSet()); 715 716 if (!allowedSet.containsAll(typeSet)) 717 return null; 718 719 return areAllIntegerResultExpressionsConvertibleToTargetType(candidate) ? 720 candidate : null; 721 } getResultNumeric(Set<TypeBinding> typeSet)722 private TypeBinding getResultNumeric(Set<TypeBinding> typeSet) { 723 // note: if an expression has a type integer, then it will be a constant 724 // since non-constant integers are already processed before reaching here. 725 726 /* Otherwise, if any expression is of type short, and every other expression is either of type short, 727 * or of type byte, or a constant expression of type int with a value that is representable in the 728 * type short, then T is short, the byte expressions undergo widening primitive conversion to short, 729 * and the int expressions undergo narrowing primitive conversion to short.\ 730 * 731 * Otherwise, if any expression is of type byte, and every other expression is either of type byte or a 732 * constant expression of type int with a value that is representable in the type byte, then T is byte 733 * and the int expressions undergo narrowing primitive conversion to byte. 734 * 735 * Otherwise, if any expression is of type char, and every other expression is either of type char or a 736 * constant expression of type int with a value that is representable in the type char, then T is char 737 * and the int expressions undergo narrowing primitive conversion to char. 738 * 739 * Otherwise, T is int and all the expressions that are not of type int undergo widening 740 * primitive conversion to int. 741 */ 742 743 // DO NOT Change the order below [as per JLS 13 5.6 ]. 744 TypeBinding[] csb = new TypeBinding[] {TypeBinding.SHORT, TypeBinding.BYTE, TypeBinding.CHAR}; 745 for (TypeBinding c : csb) { 746 TypeBinding result = check_csb(typeSet, c); 747 if (result != null) 748 return result; 749 } 750 /* Otherwise, all the result expressions that are not of type int are widened to int. */ 751 return TypeBinding.INT; 752 } 753 @Override isPolyExpression()754 public boolean isPolyExpression() { 755 if (this.isPolyExpression) 756 return true; 757 // JLS 13 15.28.1 A switch expression is a poly expression if it appears in an assignment context or 758 // an invocation context (5.2, 5.3). Otherwise, it is a standalone expression. 759 return this.isPolyExpression = this.expressionContext == ASSIGNMENT_CONTEXT || 760 this.expressionContext == INVOCATION_CONTEXT; 761 } 762 @Override isTrulyExpression()763 public boolean isTrulyExpression() { 764 return true; 765 } 766 @Override isCompatibleWith(TypeBinding left, Scope skope)767 public boolean isCompatibleWith(TypeBinding left, Scope skope) { 768 if (!isPolyExpression()) 769 return super.isCompatibleWith(left, skope); 770 771 for (Expression e : this.resultExpressions) { 772 if (!e.isCompatibleWith(left, skope)) 773 return false; 774 } 775 return true; 776 } 777 @Override isBoxingCompatibleWith(TypeBinding targetType, Scope skope)778 public boolean isBoxingCompatibleWith(TypeBinding targetType, Scope skope) { 779 if (!isPolyExpression()) 780 return super.isBoxingCompatibleWith(targetType, skope); 781 782 for (Expression e : this.resultExpressions) { 783 if (!(e.isCompatibleWith(targetType, skope) || e.isBoxingCompatibleWith(targetType, skope))) 784 return false; 785 } 786 return true; 787 } 788 @Override sIsMoreSpecific(TypeBinding s, TypeBinding t, Scope skope)789 public boolean sIsMoreSpecific(TypeBinding s, TypeBinding t, Scope skope) { 790 if (super.sIsMoreSpecific(s, t, skope)) 791 return true; 792 if (!isPolyExpression()) 793 return false; 794 for (Expression e : this.resultExpressions) { 795 if (!e.sIsMoreSpecific(s, t, skope)) 796 return false; 797 } 798 return true; 799 } 800 @Override expectedType()801 public TypeBinding expectedType() { 802 return this.expectedType; 803 } 804 @Override traverse( ASTVisitor visitor, BlockScope blockScope)805 public void traverse( 806 ASTVisitor visitor, 807 BlockScope blockScope) { 808 809 if (visitor.visit(this, blockScope)) { 810 this.expression.traverse(visitor, blockScope); 811 if (this.statements != null) { 812 int statementsLength = this.statements.length; 813 for (int i = 0; i < statementsLength; i++) 814 this.statements[i].traverse(visitor, this.scope); 815 } 816 } 817 visitor.endVisit(this, blockScope); 818 } 819 }