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 }