Skip to content

Commit 6cb28bd

Browse files
committed
8359145: Implement Primitive Types in Patterns, instanceof, and switch (Fourth Preview)
1 parent e16c510 commit 6cb28bd

17 files changed

+798
-48
lines changed

src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ private static String typeSwitchClassName(Class<?> targetClass) {
770770
return name + "$$TypeSwitch";
771771
}
772772

773-
// this method should be in sync with com.sun.tools.javac.code.Types.checkUnconditionallyExactPrimitives
773+
// this method should be in sync with com.sun.tools.javac.code.Types.isUnconditionallyExactPrimitives
774774
private static boolean unconditionalExactnessCheck(Class<?> selectorType, Class<?> targetType) {
775775
Wrapper selectorWrapper = Wrapper.forBasicType(selectorType);
776776
Wrapper targetWrapper = Wrapper.forBasicType(targetType);

src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeTag.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
package com.sun.tools.javac.code;
2727

2828
import com.sun.source.tree.Tree.Kind;
29-
29+
import java.lang.runtime.ExactConversionsSupport;
3030
import javax.lang.model.type.TypeKind;
3131

3232
import static com.sun.tools.javac.code.TypeTag.NumericClasses.*;
@@ -186,6 +186,10 @@ public boolean isInSuperClassesOf(TypeTag tag) {
186186
return (this.numericClass & tag.superClasses) != 0;
187187
}
188188

189+
public boolean isNumeric() {
190+
return this.numericClass != 0;
191+
}
192+
189193
/** Returns the number of type tags.
190194
*/
191195
public static int getTypeTagCount() {
@@ -247,11 +251,11 @@ public boolean checkRange(int value) {
247251
case BOOLEAN:
248252
return 0 <= value && value <= 1;
249253
case BYTE:
250-
return Byte.MIN_VALUE <= value && value <= Byte.MAX_VALUE;
254+
return ExactConversionsSupport.isIntToByteExact(value);
251255
case CHAR:
252-
return Character.MIN_VALUE <= value && value <= Character.MAX_VALUE;
256+
return ExactConversionsSupport.isIntToCharExact(value);
253257
case SHORT:
254-
return Short.MIN_VALUE <= value && value <= Short.MAX_VALUE;
258+
return ExactConversionsSupport.isIntToShortExact(value);
255259
case INT:
256260
return true;
257261
default:

src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java

Lines changed: 91 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
package com.sun.tools.javac.code;
2727

2828
import java.lang.ref.SoftReference;
29+
import java.lang.runtime.ExactConversionsSupport;
2930
import java.util.HashSet;
3031
import java.util.HashMap;
3132
import java.util.Locale;
@@ -5054,14 +5055,25 @@ public Type visitCapturedType(CapturedType t, S s) {
50545055
// </editor-fold>
50555056

50565057
// <editor-fold defaultstate="collapsed" desc="Unconditionality">
5057-
/** Check unconditionality between any combination of reference or primitive types.
5058+
/** Check unconditionality between any combination of reference or primitive
5059+
* types.
50585060
*
5059-
* Rules:
5060-
* an identity conversion
5061-
* a widening reference conversion
5062-
* a widening primitive conversion (delegates to `checkUnconditionallyExactPrimitives`)
5063-
* a boxing conversion
5064-
* a boxing conversion followed by a widening reference conversion
5061+
* The following are unconditionally exact regardless of the input
5062+
* expression:
5063+
*
5064+
* - an identity conversion
5065+
* - a widening reference conversion
5066+
* - an exact widening primitive conversion
5067+
* - a boxing conversion
5068+
* - a boxing conversion followed by a widening reference conversion
5069+
*
5070+
* Additionally, the following can be unconditionally exact if the source
5071+
* primitive is a constant expression and the conversions is exact for that
5072+
* constant expression:
5073+
*
5074+
* - a narrowing primitive conversion
5075+
* - a widening and narrowing primitive conversion
5076+
* - a widening primitive conversion that is not exact
50655077
*
50665078
* @param source Source primitive or reference type
50675079
* @param target Target primitive or reference type
@@ -5071,28 +5083,80 @@ public boolean isUnconditionallyExact(Type source, Type target) {
50715083
return true;
50725084
}
50735085

5074-
return target.isPrimitive()
5075-
? isUnconditionallyExactPrimitives(source, target)
5076-
: isSubtype(boxedTypeOrType(erasure(source)), target);
5086+
if (target.isPrimitive()) {
5087+
if (source.isPrimitive() &&
5088+
((source.getTag().isStrictSubRangeOf(target.getTag())) &&
5089+
!((source.hasTag(BYTE) && target.hasTag(CHAR)) ||
5090+
(source.hasTag(INT) && target.hasTag(FLOAT)) ||
5091+
(source.hasTag(LONG) && (target.hasTag(DOUBLE) || target.hasTag(FLOAT)))))) return true;
5092+
else {
5093+
return false;
5094+
}
5095+
} else {
5096+
return isSubtype(boxedTypeOrType(erasure(source)), target);
5097+
}
50775098
}
5099+
// where
5100+
public boolean isUnconditionallyExactConstantPrimitives(Type source, Type target) {
5101+
if (!(source.constValue() instanceof Number value) || !target.getTag().isNumeric()) return false;
50785102

5079-
/** Check unconditionality between primitive types.
5080-
*
5081-
* - widening from one integral type to another,
5082-
* - widening from one floating point type to another,
5083-
* - widening from byte, short, or char to a floating point type,
5084-
* - widening from int to double.
5085-
*
5086-
* @param selectorType Type of selector
5087-
* @param targetType Target type
5088-
*/
5089-
public boolean isUnconditionallyExactPrimitives(Type selectorType, Type targetType) {
5090-
return isSameType(selectorType, targetType) ||
5091-
(selectorType.isPrimitive() && targetType.isPrimitive()) &&
5092-
((selectorType.getTag().isStrictSubRangeOf(targetType.getTag())) &&
5093-
!((selectorType.hasTag(BYTE) && targetType.hasTag(CHAR)) ||
5094-
(selectorType.hasTag(INT) && targetType.hasTag(FLOAT)) ||
5095-
(selectorType.hasTag(LONG) && (targetType.hasTag(DOUBLE) || targetType.hasTag(FLOAT)))));
5103+
switch (source.getTag()) {
5104+
case BYTE:
5105+
switch (target.getTag()) {
5106+
case CHAR: return ExactConversionsSupport.isIntToCharExact(value.intValue());
5107+
}
5108+
break;
5109+
case CHAR:
5110+
switch (target.getTag()) {
5111+
case BYTE: return ExactConversionsSupport.isIntToByteExact(value.intValue());
5112+
case SHORT: return ExactConversionsSupport.isIntToShortExact(value.intValue());
5113+
}
5114+
break;
5115+
case SHORT:
5116+
switch (target.getTag()) {
5117+
case BYTE: return ExactConversionsSupport.isIntToByteExact(value.intValue());
5118+
case CHAR: return ExactConversionsSupport.isIntToCharExact(value.intValue());
5119+
}
5120+
break;
5121+
case INT:
5122+
switch (target.getTag()) {
5123+
case BYTE: return ExactConversionsSupport.isIntToByteExact(value.intValue());
5124+
case CHAR: return ExactConversionsSupport.isIntToCharExact(value.intValue());
5125+
case SHORT: return ExactConversionsSupport.isIntToShortExact(value.intValue());
5126+
case FLOAT: return ExactConversionsSupport.isIntToFloatExact(value.intValue());
5127+
}
5128+
break;
5129+
case FLOAT:
5130+
switch (target.getTag()) {
5131+
case BYTE: return ExactConversionsSupport.isFloatToByteExact(value.floatValue());
5132+
case CHAR: return ExactConversionsSupport.isFloatToCharExact(value.floatValue());
5133+
case SHORT: return ExactConversionsSupport.isFloatToShortExact(value.floatValue());
5134+
case INT: return ExactConversionsSupport.isFloatToIntExact(value.floatValue());
5135+
case LONG: return ExactConversionsSupport.isFloatToLongExact(value.floatValue());
5136+
}
5137+
break;
5138+
case LONG:
5139+
switch (target.getTag()) {
5140+
case BYTE: return ExactConversionsSupport.isLongToByteExact(value.longValue());
5141+
case CHAR: return ExactConversionsSupport.isLongToCharExact(value.longValue());
5142+
case SHORT: return ExactConversionsSupport.isLongToShortExact(value.longValue());
5143+
case INT: return ExactConversionsSupport.isLongToIntExact(value.longValue());
5144+
case FLOAT: return ExactConversionsSupport.isLongToFloatExact(value.longValue());
5145+
case DOUBLE: return ExactConversionsSupport.isLongToDoubleExact(value.longValue());
5146+
}
5147+
break;
5148+
case DOUBLE:
5149+
switch (target.getTag()) {
5150+
case BYTE: return ExactConversionsSupport.isDoubleToByteExact(value.doubleValue());
5151+
case CHAR: return ExactConversionsSupport.isDoubleToCharExact(value.doubleValue());
5152+
case SHORT: return ExactConversionsSupport.isDoubleToShortExact(value.doubleValue());
5153+
case INT: return ExactConversionsSupport.isDoubleToIntExact(value.doubleValue());
5154+
case FLOAT: return ExactConversionsSupport.isDoubleToFloatExact(value.doubleValue());
5155+
case LONG: return ExactConversionsSupport.isDoubleToLongExact(value.doubleValue());
5156+
}
5157+
break;
5158+
}
5159+
return true;
50965160
}
50975161
// </editor-fold>
50985162

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ protected Check(Context context) {
167167
allowModules = Feature.MODULES.allowedInSource(source);
168168
allowRecords = Feature.RECORDS.allowedInSource(source);
169169
allowSealed = Feature.SEALED_CLASSES.allowedInSource(source);
170+
allowPrimitivePatterns = preview.isEnabled() && Feature.PRIMITIVE_PATTERNS.allowedInSource(source);
170171
}
171172

172173
/** Character for synthetic names
@@ -190,6 +191,10 @@ protected Check(Context context) {
190191
*/
191192
private final boolean allowSealed;
192193

194+
/** Are primitive patterns allowed
195+
*/
196+
private final boolean allowPrimitivePatterns;
197+
193198
/** Whether to force suppression of deprecation and preview warnings.
194199
* This happens when attributing import statements for JDK 9+.
195200
* @see Feature#DEPRECATION_ON_IMPORT
@@ -4734,20 +4739,30 @@ void checkSwitchCaseLabelDominated(JCCaseLabel unconditionalCaseLabel, List<JCCa
47344739
JCCaseLabel testCaseLabel = caseAndLabel.snd;
47354740
Type testType = labelType(testCaseLabel);
47364741
boolean dominated = false;
4737-
if (types.isUnconditionallyExact(currentType, testType) &&
4738-
!currentType.hasTag(ERROR) && !testType.hasTag(ERROR)) {
4742+
4743+
if (unconditionalCaseLabel == testCaseLabel) unconditionalFound = true;
4744+
boolean dominatedCandidate = false;
4745+
if (!currentType.hasTag(ERROR) && !testType.hasTag(ERROR)) {
4746+
if (types.isUnconditionallyExact(currentType, testType)) {
4747+
dominatedCandidate = true;
4748+
} else if (currentType.constValue() instanceof Number) {
4749+
dominatedCandidate = types.isUnconditionallyExactConstantPrimitives(currentType, testType);
4750+
}
47394751
//the current label is potentially dominated by the existing (test) label, check:
47404752
if (label instanceof JCConstantCaseLabel) {
4741-
dominated |= !(testCaseLabel instanceof JCConstantCaseLabel) &&
4753+
dominated = dominatedCandidate &&
4754+
!(testCaseLabel instanceof JCConstantCaseLabel) &&
47424755
TreeInfo.unguardedCase(testCase);
47434756
} else if (label instanceof JCPatternCaseLabel patternCL &&
47444757
testCaseLabel instanceof JCPatternCaseLabel testPatternCaseLabel &&
47454758
(testCase.equals(c) || TreeInfo.unguardedCase(testCase))) {
4746-
dominated = patternDominated(testPatternCaseLabel.pat,
4747-
patternCL.pat);
4759+
dominated = dominatedCandidate &&
4760+
patternDominated(testPatternCaseLabel.pat, patternCL.pat);
47484761
}
47494762
}
4750-
4763+
if (allowPrimitivePatterns && unconditionalFound && unconditionalCaseLabel != label) {
4764+
dominated = true;
4765+
}
47514766
if (dominated) {
47524767
log.error(label.pos(), Errors.PatternDominated);
47534768
}

test/langtools/tools/javac/patterns/Domination.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
* @bug 8262891 8290709
2727
* @summary Check the pattern domination error are reported correctly.
2828
* @compile/fail/ref=Domination.out -XDrawDiagnostics Domination.java
29+
* @compile/fail/ref=DominationWithPP.out --enable-preview --source ${jdk.version} -XDrawDiagnostics Domination.java
2930
*/
30-
3131
public class Domination {
3232
int testDominatesError1(Object o) {
3333
switch (o) {
@@ -218,4 +218,14 @@ int testNotDominates2(Integer x) {
218218
case null : return -1;
219219
}
220220
}
221+
222+
int testCasePatternDominatedbyPreceedingUnconditionalCasePattern () {
223+
interface A {}
224+
interface B {}
225+
A aa = new A() {};
226+
switch (aa) {
227+
case A a : return 1;
228+
case B b : return -1;
229+
}
230+
}
221231
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Domination.java:35:18: compiler.err.pattern.dominated
2+
Domination.java:43:18: compiler.err.pattern.dominated
3+
Domination.java:51:18: compiler.err.pattern.dominated
4+
Domination.java:67:18: compiler.err.pattern.dominated
5+
Domination.java:88:18: compiler.err.pattern.dominated
6+
Domination.java:113:18: compiler.err.pattern.dominated
7+
Domination.java:144:18: compiler.err.pattern.dominated
8+
Domination.java:153:18: compiler.err.pattern.dominated
9+
Domination.java:184:18: compiler.err.pattern.dominated
10+
Domination.java:193:18: compiler.err.pattern.dominated
11+
Domination.java:202:18: compiler.err.pattern.dominated
12+
Domination.java:211:18: compiler.err.pattern.dominated
13+
Domination.java:228:18: compiler.err.pattern.dominated
14+
13 errors
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* @test /nodynamiccopyright/
3+
* @summary Retain exhaustiveness properties of switches with a constant selector
4+
* @enablePreview
5+
* @compile/fail/ref=PrimitivePatternsSwitchConstants.out -XDrawDiagnostics -XDshould-stop.at=FLOW PrimitivePatternsSwitchConstants.java
6+
*/
7+
public class PrimitivePatternsSwitchConstants {
8+
void testConstExpressions() {
9+
switch (42) { // error: not exhaustive
10+
case byte _ :
11+
}
12+
13+
switch (42l) { // error: not exhaustive
14+
case byte _ :
15+
}
16+
17+
switch (123456) { // error: not exhaustive
18+
case byte _ :
19+
}
20+
21+
switch (16_777_216) { // error: not exhaustive
22+
case float _ :
23+
}
24+
25+
switch (16_777_217) { // error: not exhaustive
26+
case float _ :
27+
}
28+
29+
switch (42d) { // error: not exhaustive
30+
case float _ :
31+
}
32+
33+
switch (1) { // OK
34+
case long _ :
35+
}
36+
37+
final int i = 42;
38+
switch (i) { // OK
39+
case long _ :
40+
}
41+
42+
switch (1) { // error: non-exhaustive
43+
case Long _ : // error: widening primitive conversion and boxing is not supported
44+
}
45+
46+
switch (42) {
47+
case byte bb -> {}
48+
case int ii -> {} // OK
49+
};
50+
51+
switch (42) {
52+
case 42 -> {}
53+
case int ii -> {} // OK
54+
};
55+
56+
switch (42) {
57+
case (byte) 42 -> {}
58+
case int ii -> {} // OK
59+
};
60+
61+
switch (42) {
62+
case 42 -> {}
63+
default -> {} // OK
64+
};
65+
66+
switch (42) {
67+
default -> {} // OK
68+
case 42 -> {}
69+
};
70+
}
71+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
PrimitivePatternsSwitchConstants.java:43:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.Long)
2+
PrimitivePatternsSwitchConstants.java:9:9: compiler.err.not.exhaustive.statement
3+
PrimitivePatternsSwitchConstants.java:13:9: compiler.err.not.exhaustive.statement
4+
PrimitivePatternsSwitchConstants.java:17:9: compiler.err.not.exhaustive.statement
5+
PrimitivePatternsSwitchConstants.java:21:9: compiler.err.not.exhaustive.statement
6+
PrimitivePatternsSwitchConstants.java:25:9: compiler.err.not.exhaustive.statement
7+
PrimitivePatternsSwitchConstants.java:29:9: compiler.err.not.exhaustive.statement
8+
PrimitivePatternsSwitchConstants.java:42:9: compiler.err.not.exhaustive.statement
9+
- compiler.note.preview.filename: PrimitivePatternsSwitchConstants.java, DEFAULT
10+
- compiler.note.preview.recompile
11+
8 errors

0 commit comments

Comments
 (0)