Skip to content

Commit

Permalink
[23] JEP 455: Primitive Types in Patterns, instanceof, and switch
Browse files Browse the repository at this point in the history
(Preview)

+ systematically test switch with numerical narrowing
+ switch on long,float,double,boolean (+boxed)
  + check constant type (must be same or unboxed of)
  + implement code gen (must use indy #typeSwitch)
    - except Boolean: here we optimize towards unboxing
    + support more signatures for indy typeSwitch
+ fix duplicate detection for boolean constants
  - while observing that null and -1 share the same IntConstant(-1)!
  • Loading branch information
stephan-herrmann committed Aug 29, 2024
1 parent a62bba7 commit 2bc7b0e
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2683,4 +2683,11 @@ public interface IProblem {
* @noreference preview feature
*/
int ConstructorCallNotAllowedHere = PreviewRelated + 2031;

/**
* @since 3.39
* @noreference preview feature
*/
int WrongCaseType = PreviewRelated + 2100;

}
Original file line number Diff line number Diff line change
Expand Up @@ -4048,8 +4048,10 @@ private int addBootStrapTypeSwitchEntry(int localContentsOffset, SwitchStatement
this.constantPool.literalIndexForDynamic(c.primitivesBootstrapIdx,
c.c.booleanValue() ? BooleanConstant.TRUE_STRING : BooleanConstant.FALSE_STRING,
ConstantPool.JavaLangBooleanSignature);
case TypeIds.T_byte, TypeIds.T_char, TypeIds.T_short, TypeIds.T_int, TypeIds.T_long ->
case TypeIds.T_byte, TypeIds.T_char, TypeIds.T_short, TypeIds.T_int ->
this.constantPool.literalIndex(c.intValue());
case TypeIds.T_long ->
this.constantPool.literalIndex(c.c.longValue());
case TypeIds.T_float ->
this.constantPool.literalIndex(c.c.floatValue());
case TypeIds.T_double ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,25 @@ public ResolvedCase[] resolveCase(BlockScope scope, TypeBinding switchExpression
}
}
} else {
// check from §14.11.1 (JEP 455):
// For each case constant associated with the switch block that is a constant expression, one of the following is true:
// - [...]
// - if T is one of long, float, double, or boolean, the type of the case constant is T.
// - if T is one of Long, Float, Double, or Boolean, the type of the case constant is, respectively, long, float, double, or boolean.
TypeBinding expectedCaseType = switchExpressionType;
if (switchExpressionType.isBoxedPrimitiveType()) {
expectedCaseType = scope.environment().computeBoxingType(switchExpressionType); // in this case it's actually 'computeUnboxingType()'
}
switch (expectedCaseType.id) {
case TypeIds.T_long, TypeIds.T_float, TypeIds.T_double, TypeIds.T_boolean -> {
if (caseType.id != expectedCaseType.id) {
scope.problemReporter().caseExpressionWrongType(e, switchExpressionType, expectedCaseType);
continue;
}
switchExpressionType = expectedCaseType;
}
}
//
Constant con = resolveConstantExpression(scope, caseType, switchExpressionType, switchStatement, e, cases);
if (con != Constant.NotAConstant) {
int index = this == switchStatement.nullCase && e instanceof NullLiteral ?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.function.Function;
import java.util.function.IntPredicate;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ast.CaseStatement.ResolvedCase;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
Expand Down Expand Up @@ -808,6 +809,9 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream) {
valueRequired = this.expression.constant == Constant.NotAConstant || hasCases;
// generate expression
this.expression.generateCode(currentScope, codeStream, valueRequired);
if (resolvedType1.id == TypeIds.T_JavaLangBoolean) {
codeStream.generateUnboxingConversion(TypeIds.T_boolean); // optimize by avoiding indy typeSwitch
}
}
// generate the appropriate switch table/lookup bytecode
if (hasCases) {
Expand Down Expand Up @@ -1025,9 +1029,7 @@ private void generateCodeSwitchPatternPrologue(BlockScope currentScope, CodeStre
}
private void generateTypeSwitchPatternPrologue(CodeStream codeStream, int invokeDynamicNumber) {
TypeBinding exprType = this.expression.resolvedType;
char[] signature = (this.switchBits & Primitive) != 0
? "(XI)I".replace("X", String.valueOf(exprType.signature())).toCharArray() //$NON-NLS-1$ //$NON-NLS-2$
: "(Ljava/lang/Object;I)I".toCharArray(); //$NON-NLS-1$
char[] signature =typeSwitchSignature(exprType);
int argsSize = TypeIds.getCategory(exprType.id) + 1; // Object | PRIM, restartIndex (PRIM = Z|S|I..)
codeStream.invokeDynamic(invokeDynamicNumber,
argsSize,
Expand All @@ -1036,6 +1038,17 @@ private void generateTypeSwitchPatternPrologue(CodeStream codeStream, int invoke
signature,
TypeBinding.INT);
}
char[] typeSwitchSignature(TypeBinding exprType) {
char[] arg1 = switch (exprType.id) {
case TypeIds.T_JavaLangLong, TypeIds.T_JavaLangFloat, TypeIds.T_JavaLangDouble, TypeIds.T_JavaLangBoolean ->
exprType.signature();
default ->
(this.switchBits & Primitive) != 0
? exprType.signature()
: "Ljava/lang/Object;".toCharArray(); //$NON-NLS-1$
};
return CharOperation.concat("(".toCharArray(), arg1, "I)I".toCharArray()); //$NON-NLS-1$ //$NON-NLS-2$
}
private void generateEnumSwitchPatternPrologue(CodeStream codeStream, int invokeDynamicNumber) {
String genericTypeSignature = new String(this.expression.resolvedType.genericTypeSignature());
String callingParams = "(" + genericTypeSignature + "I)I"; //$NON-NLS-1$ //$NON-NLS-2$
Expand Down Expand Up @@ -1207,12 +1220,16 @@ public void resolve(BlockScope upperScope) {
}
for (int j = 0; j < counter; j++) {
IntPredicate check = idx -> {
Constant c2 = this.otherConstants[idx].c;
ResolvedCase otherResolvedCase = this.otherConstants[idx];
Constant c2 = otherResolvedCase.c;
if (con.typeID() == TypeIds.T_JavaLangString) {
return c2.stringValue().equals(con.stringValue());
} else {
if (c2.typeID() == TypeIds.T_JavaLangString)
return false;
int id = c.t.id, otherId = otherResolvedCase.t.id;
if (id == TypeIds.T_null || otherId == TypeIds.T_null)
return id == otherId; // 'null' shares IntConstant(-1)
if (con.equals(c2))
return true;
return this.constants[idx] == c1;
Expand Down Expand Up @@ -1243,9 +1260,7 @@ public void resolve(BlockScope upperScope) {
} else {
if (!c.isPattern() && check.test(j)) {
if (this.isNonTraditional) {
if (c.e instanceof NullLiteral && this.otherConstants[j].e instanceof NullLiteral) {
reportDuplicateCase(c.e, this.otherConstants[j].e, length);
}
reportDuplicateCase(c.e, this.otherConstants[j].e, length);
} else {
reportDuplicateCase(caseStmt, this.cases[caseIndex[j]], length);
}
Expand Down Expand Up @@ -1475,6 +1490,11 @@ private boolean needPatternDispatchCopy() {
TypeBinding eType = this.expression != null ? this.expression.resolvedType : null;
if (eType == null)
return false;
switch (eType.id) {
case TypeIds.T_JavaLangLong, TypeIds.T_JavaLangFloat, TypeIds.T_JavaLangDouble:
return true;
// note: if no patterns are present we optimize Boolean to use unboxing rather than indy typeSwitch
}
return !(eType.isPrimitiveOrBoxedPrimitiveType() || eType.isEnum() || eType.id == TypeIds.T_JavaLangString); // classic selectors
}
private void addSecretPatternSwitchVariables(BlockScope upperScope) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1660,6 +1660,14 @@ public void caseExpressionMustBeConstant(Expression expression) {
expression.sourceStart,
expression.sourceEnd);
}
public void caseExpressionWrongType(Expression expression, TypeBinding switchBinding, TypeBinding selectorBinding) {
this.handle(
IProblem.WrongCaseType,
new String[] {String.valueOf(switchBinding.readableName()), String.valueOf(selectorBinding.readableName())},
new String[] {String.valueOf(switchBinding.shortReadableName()), String.valueOf(selectorBinding.shortReadableName())},
expression.sourceStart,
expression.sourceEnd);
}
public void classExtendFinalClass(SourceTypeBinding type, TypeReference superclass, TypeBinding superTypeBinding) {
String name = new String(type.sourceName());
String superTypeFullName = new String(superTypeBinding.readableName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,9 @@
2030 = Cannot assign field ''{0}'' in an early construction context, because it has an initializer
2031 = Constructor call is not allowed here

# JEP 455 Primitive Types in Patterns, instanceof, and switch (Preview)
2100 = Case constants in a switch on ''{0}'' must have type ''{1}''

### ELABORATIONS
## Access restrictions
78592 = The type ''{1}'' is not API (restriction on classpath entry ''{0}'')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1218,6 +1218,7 @@ class ProblemAttributes {
expectedProblemAttributes.put("WildcardConstructorInvocation", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
expectedProblemAttributes.put("WildcardFieldAssignment", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
expectedProblemAttributes.put("WildcardMethodInvocation", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
expectedProblemAttributes.put("WrongCaseType", new ProblemAttributes(CategorizedProblem.CAT_PREVIEW_RELATED));
expectedProblemAttributes.put("illFormedParameterizationOfFunctionalInterface", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
expectedProblemAttributes.put("lambdaParameterTypeMismatched", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
expectedProblemAttributes.put("lambdaSignatureMismatched", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
Expand Down Expand Up @@ -2333,6 +2334,7 @@ class ProblemAttributes {
expectedProblemAttributes.put("WildcardConstructorInvocation", SKIP);
expectedProblemAttributes.put("WildcardFieldAssignment", SKIP);
expectedProblemAttributes.put("WildcardMethodInvocation", SKIP);
expectedProblemAttributes.put("WrongCaseType", SKIP);
expectedProblemAttributes.put("illFormedParameterizationOfFunctionalInterface", SKIP);
expectedProblemAttributes.put("lambdaParameterTypeMismatched", SKIP);
expectedProblemAttributes.put("lambdaSignatureMismatched", SKIP);
Expand Down
Loading

0 comments on commit 2bc7b0e

Please sign in to comment.