8303374: Implement JEP 455: Primitive Types in Patterns, instanceof, and switch (Preview)

Co-authored-by: Jan Lahoda <jlahoda@openjdk.org>
Co-authored-by: Maurizio Cimadamore <mcimadamore@openjdk.org>
Co-authored-by: Gavin Bierman <gbierman@openjdk.org>
Co-authored-by: Brian Goetz <briangoetz@openjdk.org>
Co-authored-by: Raffaello Giulietti <rgiulietti@openjdk.org>
Co-authored-by: Aggelos Biboudis <abimpoudis@openjdk.org>
Reviewed-by: vromero, jlahoda
This commit is contained in:
Aggelos Biboudis 2024-01-31 14:18:13 +00:00
parent 66971600f7
commit 1733d2ea24
43 changed files with 3564 additions and 391 deletions

View File

@ -0,0 +1,296 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.runtime;
/**
* A testing conversion of a value is exact if it yields a result without loss
* of information or throwing an exception. Otherwise, it is inexact. Some
* conversions are always exact regardless of the value. These conversions are
* said to be unconditionally exact.
* <p>
* For example, a conversion from {@code int} to {@code byte} for the value 10
* is exact because the result, 10, is the same as the original value. In
* contrast, if the {@code int} variable {@code i} stores the value 1000 then a
* narrowing primitive conversion to {@code byte} will yield the result -24.
* Loss of information has occurred: both the magnitude and the sign of the
* result are different than those of the original value. As such, a conversion
* from {@code int} to {@code byte} for the value 1000 is inexact. Finally a
* widening primitive conversion from {@code byte} to {@code int} is
* unconditionally exact because it will always succeed with no loss of
* information about the magnitude of the numeric value.
* <p>
* The methods in this class provide the run-time support for the exactness
* checks of testing conversions from a primitive type to primitive type. These
* methods may be used, for example, by Java compiler implementations to
* implement checks for {@code instanceof} and pattern matching runtime
* implementations. Unconditionally exact testing conversions do not require a
* corresponding action at run time and, for this reason, methods corresponding
* to these exactness checks are omitted here.
* <p>
* The run time conversion checks examine whether loss of information would
* occur if a testing conversion would be to be applied. In those cases where a
* floating-point primitive type is involved, and the value of the testing
* conversion is either signed zero, signed infinity or {@code NaN}, these
* methods comply with the following:
*
* <ul>
* <li>Converting a floating-point negative zero to an integer type is considered
* inexact.</li>
* <li>Converting a floating-point {@code NaN} or infinity to an integer type is
* considered inexact.</li>
* <li>Converting a floating-point {@code NaN} or infinity or signed zero to another
* floating-point type is considered exact.</li>
* </ul>
*
* @jls 5.7.1 Exact Testing Conversions
* @jls 5.7.2 Unconditionally Exact Testing Conversions
* @jls 15.20.2 The instanceof Operator
*
* @implNote Some exactness checks describe a test which can be redirected
* safely through one of the existing methods. Those are omitted too (i.e.,
* {@code byte} to {@code char} can be redirected to
* {@link ExactConversionsSupport#isIntToCharExact(int)}, {@code short} to
* {@code byte} can be redirected to
* {@link ExactConversionsSupport#isIntToByteExact(int)} and similarly for
* {@code short} to {@code char}, {@code char} to {@code byte} and {@code char}
* to {@code short} to the corresponding methods that take an {@code int}).
*
* @since 23
*/
public final class ExactConversionsSupport {
private ExactConversionsSupport() { }
/**
* Exactness method from int to byte
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
*/
public static boolean isIntToByteExact(int n) {return n == (int)(byte)n;}
/**
* Exactness method from int to short
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
*/
public static boolean isIntToShortExact(int n) {return n == (int)(short)n;}
/**
* Exactness method from int to char
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
*/
public static boolean isIntToCharExact(int n) {return n == (int)(char)n;}
/**
* Exactness method from int to float
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
*
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isIntToFloatExact(int n) {
return n == (int)(float)n && n != Integer.MAX_VALUE;
}
/**
* Exactness method from long to byte
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
*/
public static boolean isLongToByteExact(long n) {return n == (long)(byte)n;}
/**
* Exactness method from long to short
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
*/
public static boolean isLongToShortExact(long n) {return n == (long)(short)n;}
/**
* Exactness method from long to char
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
*/
public static boolean isLongToCharExact(long n) {return n == (long)(char)n;}
/**
* Exactness method from long to int
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
*/
public static boolean isLongToIntExact(long n) {return n == (long)(int)n;}
/**
* Exactness method from long to float
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isLongToFloatExact(long n) {
return n == (long)(float)n && n != Long.MAX_VALUE;
}
/**
* Exactness method from long to double
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isLongToDoubleExact(long n) {
return n == (long)(double)n && n != Long.MAX_VALUE;
}
/**
* Exactness method from float to byte
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isFloatToByteExact(float n) {
return n == (float)(byte)n && !isNegativeZero(n);
}
/**
* Exactness method from float to short
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isFloatToShortExact(float n) {
return n == (float)(short)n && !isNegativeZero(n);
}
/**
* Exactness method from float to char
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isFloatToCharExact(float n) {
return n == (float)(char)n && !isNegativeZero(n);
}
/**
* Exactness method from float to int
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isFloatToIntExact(float n) {
return n == (float)(int)n && n != 0x1p31f && !isNegativeZero(n);
}
/**
* Exactness method from float to long
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isFloatToLongExact(float n) {
return n == (float)(long)n && n != 0x1p63f && !isNegativeZero(n);
}
/**
* Exactness method from double to byte
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isDoubleToByteExact(double n) {
return n == (double)(byte)n && !isNegativeZero(n);
}
/**
* Exactness method from double to short
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isDoubleToShortExact(double n){
return n == (double)(short)n && !isNegativeZero(n);
}
/**
* Exactness method from double to char
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isDoubleToCharExact(double n) {
return n == (double)(char)n && !isNegativeZero(n);
}
/**
* Exactness method from double to int
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isDoubleToIntExact(double n) {
return n == (double)(int)n && !isNegativeZero(n);
}
/**
* Exactness method from double to long
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isDoubleToLongExact(double n) {
return n == (double)(long)n && n != 0x1p63 && !isNegativeZero(n);
}
/**
* Exactness method from double to float
* @param n value
* @return true if and only if the passed value can be converted exactly to the target type
* @implSpec relies on the notion of representation equivalence defined in the
* specification of the {@linkplain Double} class.
*/
public static boolean isDoubleToFloatExact(double n) {
return n == (double)(float)n || n != n;
}
private static boolean isNegativeZero(float n) {
return Float.floatToRawIntBits(n) == Integer.MIN_VALUE;
}
private static boolean isNegativeZero(double n) {
return Double.doubleToRawLongBits(n) == Long.MIN_VALUE;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,7 +26,9 @@
package java.lang.runtime;
import java.lang.Enum.EnumDesc;
import java.lang.classfile.CodeBuilder;
import java.lang.constant.ClassDesc;
import java.lang.constant.ConstantDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.MethodTypeDesc;
import java.lang.invoke.CallSite;
@ -40,16 +42,21 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.stream.Stream;
import jdk.internal.access.SharedSecrets;
import java.lang.classfile.ClassFile;
import java.lang.classfile.Label;
import java.lang.classfile.instruction.SwitchCase;
import jdk.internal.misc.PreviewFeatures;
import jdk.internal.vm.annotation.Stable;
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
import java.util.HashMap;
import java.util.Map;
import static java.util.Objects.requireNonNull;
import sun.invoke.util.Wrapper;
/**
* Bootstrap methods for linking {@code invokedynamic} call sites that implement
@ -65,6 +72,7 @@ public class SwitchBootstraps {
private static final Object SENTINEL = new Object();
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static final boolean previewEnabled = PreviewFeatures.isEnabled();
private static final MethodHandle NULL_CHECK;
private static final MethodHandle IS_ZERO;
@ -74,6 +82,8 @@ public class SwitchBootstraps {
private static final MethodTypeDesc TYPES_SWITCH_DESCRIPTOR =
MethodTypeDesc.ofDescriptor("(Ljava/lang/Object;ILjava/util/function/BiPredicate;Ljava/util/List;)I");
private static final Map<TypePairs, String> typePairToName;
static {
try {
NULL_CHECK = LOOKUP.findStatic(Objects.class, "isNull",
@ -89,6 +99,7 @@ public class SwitchBootstraps {
catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
typePairToName = TypePairs.initialize();
}
/**
@ -134,12 +145,15 @@ public class SwitchBootstraps {
* and {@code Class} and {@code EnumDesc} instances, in any combination
* @return a {@code CallSite} returning the first matching element as described above
*
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalArgumentException if any element in the labels array is null, if the
* invocation type is not not a method type of first parameter of a reference type,
* second parameter of type {@code int} and with {@code int} as its return type,
* or if {@code labels} contains an element that is not of type {@code String},
* {@code Integer}, {@code Class} or {@code EnumDesc}.
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalArgumentException if any element in the labels array is null
* @throws IllegalArgumentException if the invocation type is not a method type of first parameter of a reference type,
* second parameter of type {@code int} and with {@code int} as its return type,
* @throws IllegalArgumentException if {@code labels} contains an element that is not of type {@code String},
* {@code Integer}, {@code Long}, {@code Float}, {@code Double}, {@code Boolean},
* {@code Class} or {@code EnumDesc}.
* @throws IllegalArgumentException if {@code labels} contains an element that is not of type {@code Boolean}
* when {@code target} is a {@code Boolean.class}.
* @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
* @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
*/
@ -147,31 +161,39 @@ public class SwitchBootstraps {
String invocationName,
MethodType invocationType,
Object... labels) {
Class<?> selectorType = invocationType.parameterType(0);
if (invocationType.parameterCount() != 2
|| (!invocationType.returnType().equals(int.class))
|| invocationType.parameterType(0).isPrimitive()
|| !invocationType.parameterType(1).equals(int.class))
throw new IllegalArgumentException("Illegal invocation type " + invocationType);
requireNonNull(labels);
labels = labels.clone();
Stream.of(labels).forEach(SwitchBootstraps::verifyLabel);
Stream.of(labels).forEach(l -> verifyLabel(l, selectorType));
MethodHandle target = generateInnerClass(lookup, labels);
MethodHandle target = generateTypeSwitch(lookup, selectorType, labels);
target = withIndexCheck(target, labels.length);
return new ConstantCallSite(target);
}
private static void verifyLabel(Object label) {
private static void verifyLabel(Object label, Class<?> selectorType) {
if (label == null) {
throw new IllegalArgumentException("null label found");
}
Class<?> labelClass = label.getClass();
if (labelClass != Class.class &&
labelClass != String.class &&
labelClass != Integer.class &&
((labelClass != Float.class &&
labelClass != Long.class &&
labelClass != Double.class &&
labelClass != Boolean.class) ||
((selectorType.equals(boolean.class) || selectorType.equals(Boolean.class)) && labelClass != Boolean.class && labelClass != Class.class) ||
!previewEnabled) &&
labelClass != EnumDesc.class) {
throw new IllegalArgumentException("label with illegal type found: " + label.getClass());
}
@ -266,11 +288,11 @@ public class SwitchBootstraps {
MethodHandles.guardWithTest(MethodHandles.dropArguments(NULL_CHECK, 0, int.class),
MethodHandles.dropArguments(MethodHandles.constant(int.class, -1), 0, int.class, Object.class),
MethodHandles.guardWithTest(MethodHandles.dropArguments(IS_ZERO, 1, Object.class),
generateInnerClass(lookup, labels),
generateTypeSwitch(lookup, invocationType.parameterType(0), labels),
MethodHandles.insertArguments(MAPPED_ENUM_LOOKUP, 1, lookup, enumClass, labels, new EnumMap())));
target = MethodHandles.permuteArguments(body, MethodType.methodType(int.class, Object.class, int.class), 1, 0);
} else {
target = generateInnerClass(lookup, labels);
target = generateTypeSwitch(lookup, invocationType.parameterType(0), labels);
}
target = target.asType(invocationType);
@ -381,137 +403,233 @@ public class SwitchBootstraps {
* ...
* }
*/
@SuppressWarnings("removal")
private static MethodHandle generateInnerClass(MethodHandles.Lookup caller, Object[] labels) {
private static Consumer<CodeBuilder> generateTypeSwitchSkeleton(Class<?> selectorType, Object[] labelConstants, List<EnumDesc<?>> enumDescs, List<Class<?>> extraClassLabels) {
int SELECTOR_OBJ = 0;
int RESTART_IDX = 1;
int ENUM_CACHE = 2;
int EXTRA_CLASS_LABELS = 3;
return cb -> {
cb.aload(SELECTOR_OBJ);
Label nonNullLabel = cb.newLabel();
cb.if_nonnull(nonNullLabel);
cb.iconst_m1();
cb.ireturn();
cb.labelBinding(nonNullLabel);
if (labelConstants.length == 0) {
cb.constantInstruction(0)
.ireturn();
return;
}
cb.iload(RESTART_IDX);
Label dflt = cb.newLabel();
record Element(Label target, Label next, Object caseLabel) { }
List<Element> cases = new ArrayList<>();
List<SwitchCase> switchCases = new ArrayList<>();
Object lastLabel = null;
for (int idx = labelConstants.length - 1; idx >= 0; idx--) {
Object currentLabel = labelConstants[idx];
Label target = cb.newLabel();
Label next;
if (lastLabel == null) {
next = dflt;
} else if (lastLabel.equals(currentLabel)) {
next = cases.getLast().next();
} else {
next = cases.getLast().target();
}
lastLabel = currentLabel;
cases.add(new Element(target, next, currentLabel));
switchCases.add(SwitchCase.of(idx, target));
}
cases = cases.reversed();
switchCases = switchCases.reversed();
cb.tableswitch(0, labelConstants.length - 1, dflt, switchCases);
for (int idx = 0; idx < cases.size(); idx++) {
Element element = cases.get(idx);
Label next = element.next();
cb.labelBinding(element.target());
if (element.caseLabel() instanceof Class<?> classLabel) {
if (unconditionalExactnessCheck(selectorType, classLabel)) {
//nothing - unconditionally use this case
} else if (classLabel.isPrimitive()) {
if (!selectorType.isPrimitive() && !Wrapper.isWrapperNumericOrBooleanType(selectorType)) {
// Object o = ...
// o instanceof Wrapped(float)
cb.aload(SELECTOR_OBJ);
cb.instanceof_(Wrapper.forBasicType(classLabel)
.wrapperType()
.describeConstable()
.orElseThrow());
cb.ifeq(next);
} else if (!unconditionalExactnessCheck(Wrapper.asPrimitiveType(selectorType), classLabel)) {
// Integer i = ... or int i = ...
// o instanceof float
Label notNumber = cb.newLabel();
cb.aload(SELECTOR_OBJ);
cb.instanceof_(ConstantDescs.CD_Number);
if (selectorType == long.class || selectorType == float.class || selectorType == double.class) {
cb.ifeq(next);
} else {
cb.ifeq(notNumber);
}
cb.aload(SELECTOR_OBJ);
cb.checkcast(ConstantDescs.CD_Number);
if (selectorType == long.class) {
cb.invokevirtual(ConstantDescs.CD_Number,
"longValue",
MethodTypeDesc.of(ConstantDescs.CD_long));
} else if (selectorType == float.class) {
cb.invokevirtual(ConstantDescs.CD_Number,
"floatValue",
MethodTypeDesc.of(ConstantDescs.CD_float));
} else if (selectorType == double.class) {
cb.invokevirtual(ConstantDescs.CD_Number,
"doubleValue",
MethodTypeDesc.of(ConstantDescs.CD_double));
} else {
Label compare = cb.newLabel();
cb.invokevirtual(ConstantDescs.CD_Number,
"intValue",
MethodTypeDesc.of(ConstantDescs.CD_int));
cb.goto_(compare);
cb.labelBinding(notNumber);
cb.aload(SELECTOR_OBJ);
cb.instanceof_(ConstantDescs.CD_Character);
cb.ifeq(next);
cb.aload(SELECTOR_OBJ);
cb.checkcast(ConstantDescs.CD_Character);
cb.invokevirtual(ConstantDescs.CD_Character,
"charValue",
MethodTypeDesc.of(ConstantDescs.CD_char));
cb.labelBinding(compare);
}
TypePairs typePair = TypePairs.of(Wrapper.asPrimitiveType(selectorType), classLabel);
String methodName = typePairToName.get(typePair);
cb.invokestatic(ExactConversionsSupport.class.describeConstable().orElseThrow(),
methodName,
MethodTypeDesc.of(ConstantDescs.CD_boolean, typePair.from.describeConstable().orElseThrow()));
cb.ifeq(next);
}
} else {
Optional<ClassDesc> classLabelConstableOpt = classLabel.describeConstable();
if (classLabelConstableOpt.isPresent()) {
cb.aload(SELECTOR_OBJ);
cb.instanceof_(classLabelConstableOpt.orElseThrow());
cb.ifeq(next);
} else {
cb.aload(EXTRA_CLASS_LABELS);
cb.constantInstruction(extraClassLabels.size());
cb.invokeinterface(ConstantDescs.CD_List,
"get",
MethodTypeDesc.of(ConstantDescs.CD_Object,
ConstantDescs.CD_int));
cb.checkcast(ConstantDescs.CD_Class);
cb.aload(SELECTOR_OBJ);
cb.invokevirtual(ConstantDescs.CD_Class,
"isInstance",
MethodTypeDesc.of(ConstantDescs.CD_boolean,
ConstantDescs.CD_Object));
cb.ifeq(next);
extraClassLabels.add(classLabel);
}
}
} else if (element.caseLabel() instanceof EnumDesc<?> enumLabel) {
int enumIdx = enumDescs.size();
enumDescs.add(enumLabel);
cb.aload(ENUM_CACHE);
cb.constantInstruction(enumIdx);
cb.invokestatic(ConstantDescs.CD_Integer,
"valueOf",
MethodTypeDesc.of(ConstantDescs.CD_Integer,
ConstantDescs.CD_int));
cb.aload(SELECTOR_OBJ);
cb.invokeinterface(BiPredicate.class.describeConstable().orElseThrow(),
"test",
MethodTypeDesc.of(ConstantDescs.CD_boolean,
ConstantDescs.CD_Object,
ConstantDescs.CD_Object));
cb.ifeq(next);
} else if (element.caseLabel() instanceof String stringLabel) {
cb.ldc(stringLabel);
cb.aload(SELECTOR_OBJ);
cb.invokevirtual(ConstantDescs.CD_Object,
"equals",
MethodTypeDesc.of(ConstantDescs.CD_boolean,
ConstantDescs.CD_Object));
cb.ifeq(next);
} else if (element.caseLabel() instanceof Integer integerLabel) {
Label compare = cb.newLabel();
Label notNumber = cb.newLabel();
cb.aload(SELECTOR_OBJ);
cb.instanceof_(ConstantDescs.CD_Number);
cb.ifeq(notNumber);
cb.aload(SELECTOR_OBJ);
cb.checkcast(ConstantDescs.CD_Number);
cb.invokevirtual(ConstantDescs.CD_Number,
"intValue",
MethodTypeDesc.of(ConstantDescs.CD_int));
cb.goto_(compare);
cb.labelBinding(notNumber);
cb.aload(SELECTOR_OBJ);
cb.instanceof_(ConstantDescs.CD_Character);
cb.ifeq(next);
cb.aload(SELECTOR_OBJ);
cb.checkcast(ConstantDescs.CD_Character);
cb.invokevirtual(ConstantDescs.CD_Character,
"charValue",
MethodTypeDesc.of(ConstantDescs.CD_char));
cb.labelBinding(compare);
cb.ldc(integerLabel);
cb.if_icmpne(next);
} else if ((element.caseLabel() instanceof Long ||
element.caseLabel() instanceof Float ||
element.caseLabel() instanceof Double ||
element.caseLabel() instanceof Boolean)) {
if (element.caseLabel() instanceof Boolean c) {
cb.constantInstruction(c ? 1 : 0);
} else {
cb.constantInstruction((ConstantDesc) element.caseLabel());
}
cb.invokestatic(element.caseLabel().getClass().describeConstable().orElseThrow(),
"valueOf",
MethodTypeDesc.of(element.caseLabel().getClass().describeConstable().orElseThrow(),
Wrapper.asPrimitiveType(element.caseLabel().getClass()).describeConstable().orElseThrow()));
cb.aload(SELECTOR_OBJ);
cb.invokevirtual(ConstantDescs.CD_Object,
"equals",
MethodTypeDesc.of(ConstantDescs.CD_boolean,
ConstantDescs.CD_Object));
cb.ifeq(next);
} else {
throw new InternalError("Unsupported label type: " +
element.caseLabel().getClass());
}
cb.constantInstruction(idx);
cb.ireturn();
}
cb.labelBinding(dflt);
cb.constantInstruction(cases.size());
cb.ireturn();
};
}
/*
* Construct the method handle that represents the method int typeSwitch(Object, int, BiPredicate, List)
*/
private static MethodHandle generateTypeSwitch(MethodHandles.Lookup caller, Class<?> selectorType, Object[] labelConstants) {
List<EnumDesc<?>> enumDescs = new ArrayList<>();
List<Class<?>> extraClassLabels = new ArrayList<>();
byte[] classBytes = ClassFile.of().build(ClassDesc.of(typeSwitchClassName(caller.lookupClass())), clb -> {
clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC)
.withMethodBody("typeSwitch",
TYPES_SWITCH_DESCRIPTOR,
ClassFile.ACC_FINAL | ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
cb -> {
cb.aload(0);
Label nonNullLabel = cb.newLabel();
cb.if_nonnull(nonNullLabel);
cb.iconst_m1();
cb.ireturn();
cb.labelBinding(nonNullLabel);
if (labels.length == 0) {
cb.constantInstruction(0)
.ireturn();
return ;
}
cb.iload(1);
Label dflt = cb.newLabel();
record Element(Label target, Label next, Object caseLabel) {}
List<Element> cases = new ArrayList<>();
List<SwitchCase> switchCases = new ArrayList<>();
Object lastLabel = null;
for (int idx = labels.length - 1; idx >= 0; idx--) {
Object currentLabel = labels[idx];
Label target = cb.newLabel();
Label next;
if (lastLabel == null) {
next = dflt;
} else if (lastLabel.equals(currentLabel)) {
next = cases.getLast().next();
} else {
next = cases.getLast().target();
}
lastLabel = currentLabel;
cases.add(new Element(target, next, currentLabel));
switchCases.add(SwitchCase.of(idx, target));
}
cases = cases.reversed();
switchCases = switchCases.reversed();
cb.tableswitch(0, labels.length - 1, dflt, switchCases);
for (int idx = 0; idx < cases.size(); idx++) {
Element element = cases.get(idx);
Label next = element.next();
cb.labelBinding(element.target());
if (element.caseLabel() instanceof Class<?> classLabel) {
Optional<ClassDesc> classLabelConstableOpt = classLabel.describeConstable();
if (classLabelConstableOpt.isPresent()) {
cb.aload(0);
cb.instanceof_(classLabelConstableOpt.orElseThrow());
cb.ifeq(next);
} else {
cb.aload(3);
cb.constantInstruction(extraClassLabels.size());
cb.invokeinterface(ConstantDescs.CD_List,
"get",
MethodTypeDesc.of(ConstantDescs.CD_Object,
ConstantDescs.CD_int));
cb.checkcast(ConstantDescs.CD_Class);
cb.aload(0);
cb.invokevirtual(ConstantDescs.CD_Class,
"isInstance",
MethodTypeDesc.of(ConstantDescs.CD_boolean,
ConstantDescs.CD_Object));
cb.ifeq(next);
extraClassLabels.add(classLabel);
}
} else if (element.caseLabel() instanceof EnumDesc<?> enumLabel) {
int enumIdx = enumDescs.size();
enumDescs.add(enumLabel);
cb.aload(2);
cb.constantInstruction(enumIdx);
cb.invokestatic(ConstantDescs.CD_Integer,
"valueOf",
MethodTypeDesc.of(ConstantDescs.CD_Integer,
ConstantDescs.CD_int));
cb.aload(0);
cb.invokeinterface(BiPredicate.class.describeConstable().orElseThrow(),
"test",
MethodTypeDesc.of(ConstantDescs.CD_boolean,
ConstantDescs.CD_Object,
ConstantDescs.CD_Object));
cb.ifeq(next);
} else if (element.caseLabel() instanceof String stringLabel) {
cb.ldc(stringLabel);
cb.aload(0);
cb.invokevirtual(ConstantDescs.CD_Object,
"equals",
MethodTypeDesc.of(ConstantDescs.CD_boolean,
ConstantDescs.CD_Object));
cb.ifeq(next);
} else if (element.caseLabel() instanceof Integer integerLabel) {
Label compare = cb.newLabel();
Label notNumber = cb.newLabel();
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Number);
cb.ifeq(notNumber);
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Number);
cb.invokevirtual(ConstantDescs.CD_Number,
"intValue",
MethodTypeDesc.of(ConstantDescs.CD_int));
cb.goto_(compare);
cb.labelBinding(notNumber);
cb.aload(0);
cb.instanceof_(ConstantDescs.CD_Character);
cb.ifeq(next);
cb.aload(0);
cb.checkcast(ConstantDescs.CD_Character);
cb.invokevirtual(ConstantDescs.CD_Character,
"charValue",
MethodTypeDesc.of(ConstantDescs.CD_char));
cb.labelBinding(compare);
cb.ldc(integerLabel);
cb.if_icmpne(next);
} else {
throw new InternalError("Unsupported label type: " +
element.caseLabel().getClass());
}
cb.constantInstruction(idx);
cb.ireturn();
}
cb.labelBinding(dflt);
cb.constantInstruction(cases.size());
cb.ireturn();
});
byte[] classBytes = ClassFile.of().build(ClassDesc.of(typeSwitchClassName(caller.lookupClass())),
clb -> {
clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC)
.withMethodBody("typeSwitch",
TYPES_SWITCH_DESCRIPTOR,
ClassFile.ACC_FINAL | ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
generateTypeSwitchSkeleton(selectorType, labelConstants, enumDescs, extraClassLabels));
});
try {
@ -525,8 +643,13 @@ public class SwitchBootstraps {
int.class,
BiPredicate.class,
List.class));
return MethodHandles.insertArguments(typeSwitch, 2, new ResolvedEnumLabels(caller, enumDescs.toArray(EnumDesc[]::new)),
List.copyOf(extraClassLabels));
typeSwitch = MethodHandles.insertArguments(typeSwitch, 2, new ResolvedEnumLabels(caller, enumDescs.toArray(EnumDesc[]::new)),
List.copyOf(extraClassLabels));
typeSwitch = MethodHandles.explicitCastArguments(typeSwitch,
MethodType.methodType(int.class,
selectorType,
int.class));
return typeSwitch;
} catch (Throwable t) {
throw new IllegalArgumentException(t);
}
@ -541,4 +664,61 @@ public class SwitchBootstraps {
}
return name + "$$TypeSwitch";
}
// this method should be in sync with com.sun.tools.javac.code.Types.checkUnconditionallyExactPrimitives
private static boolean unconditionalExactnessCheck(Class<?> selectorType, Class<?> targetType) {
Wrapper selectorWrapper = Wrapper.forBasicType(selectorType);
Wrapper targetWrapper = Wrapper.forBasicType(targetType);
if (selectorType.isPrimitive() && targetType.equals(selectorWrapper.wrapperType())) {
return true;
}
else if (selectorType.equals(targetType) ||
((selectorType.equals(byte.class) && !targetType.equals(char.class)) ||
(selectorType.equals(short.class) && (selectorWrapper.isStrictSubRangeOf(targetWrapper))) ||
(selectorType.equals(char.class) && (selectorWrapper.isStrictSubRangeOf(targetWrapper))) ||
(selectorType.equals(int.class) && (targetType.equals(double.class) || targetType.equals(long.class))) ||
(selectorType.equals(float.class) && (selectorWrapper.isStrictSubRangeOf(targetWrapper))))) return true;
return false;
}
// TypePairs should be in sync with the corresponding record in Lower
record TypePairs(Class<?> from, Class<?> to) {
public static TypePairs of(Class<?> from, Class<?> to) {
if (from == byte.class || from == short.class || from == char.class) {
from = int.class;
}
return new TypePairs(from, to);
}
public static Map<TypePairs, String> initialize() {
Map<TypePairs, String> typePairToName = new HashMap<>();
typePairToName.put(new TypePairs(byte.class, char.class), "isIntToCharExact"); // redirected
typePairToName.put(new TypePairs(short.class, byte.class), "isIntToByteExact"); // redirected
typePairToName.put(new TypePairs(short.class, char.class), "isIntToCharExact"); // redirected
typePairToName.put(new TypePairs(char.class, byte.class), "isIntToByteExact"); // redirected
typePairToName.put(new TypePairs(char.class, short.class), "isIntToShortExact"); // redirected
typePairToName.put(new TypePairs(int.class, byte.class), "isIntToByteExact");
typePairToName.put(new TypePairs(int.class, short.class), "isIntToShortExact");
typePairToName.put(new TypePairs(int.class, char.class), "isIntToCharExact");
typePairToName.put(new TypePairs(int.class, float.class), "isIntToFloatExact");
typePairToName.put(new TypePairs(long.class, byte.class), "isLongToByteExact");
typePairToName.put(new TypePairs(long.class, short.class), "isLongToShortExact");
typePairToName.put(new TypePairs(long.class, char.class), "isLongToCharExact");
typePairToName.put(new TypePairs(long.class, int.class), "isLongToIntExact");
typePairToName.put(new TypePairs(long.class, float.class), "isLongToFloatExact");
typePairToName.put(new TypePairs(long.class, double.class), "isLongToDoubleExact");
typePairToName.put(new TypePairs(float.class, byte.class), "isFloatToByteExact");
typePairToName.put(new TypePairs(float.class, short.class), "isFloatToShortExact");
typePairToName.put(new TypePairs(float.class, char.class), "isFloatToCharExact");
typePairToName.put(new TypePairs(float.class, int.class), "isFloatToIntExact");
typePairToName.put(new TypePairs(float.class, long.class), "isFloatToLongExact");
typePairToName.put(new TypePairs(double.class, byte.class), "isDoubleToByteExact");
typePairToName.put(new TypePairs(double.class, short.class), "isDoubleToShortExact");
typePairToName.put(new TypePairs(double.class, char.class), "isDoubleToCharExact");
typePairToName.put(new TypePairs(double.class, int.class), "isDoubleToIntExact");
typePairToName.put(new TypePairs(double.class, long.class), "isDoubleToLongExact");
typePairToName.put(new TypePairs(double.class, float.class), "isDoubleToFloatExact");
return typePairToName;
}
}
}

View File

@ -25,23 +25,25 @@
package sun.invoke.util;
import java.util.Map;
import static sun.invoke.util.Wrapper.NumericClasses.*;
import jdk.internal.vm.annotation.DontInline;
public enum Wrapper {
// wrapperType simple primitiveType simple char emptyArray format
BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0], Format.unsigned( 1)),
// wrapperType simple primitiveType simple char emptyArray format numericClass superClass
BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0], Format.unsigned( 1), 0, 0),
// These must be in the order defined for widening primitive conversions in JLS 5.1.2
// Avoid boxing integral types here to defer initialization of internal caches
BYTE ( Byte.class, "Byte", byte.class, "byte", 'B', new byte[0], Format.signed( 8)),
SHORT ( Short.class, "Short", short.class, "short", 'S', new short[0], Format.signed( 16)),
CHAR (Character.class, "Character", char.class, "char", 'C', new char[0], Format.unsigned(16)),
INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0], Format.signed( 32)),
LONG ( Long.class, "Long", long.class, "long", 'J', new long[0], Format.signed( 64)),
FLOAT ( Float.class, "Float", float.class, "float", 'F', new float[0], Format.floating(32)),
DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0], Format.floating(64)),
OBJECT ( Object.class, "Object", Object.class, "Object", 'L', new Object[0], Format.other( 1)),
BYTE ( Byte.class, "Byte", byte.class, "byte", 'B', new byte[0], Format.signed( 8), BYTE_CLASS, BYTE_SUPERCLASSES),
SHORT ( Short.class, "Short", short.class, "short", 'S', new short[0], Format.signed( 16), SHORT_CLASS, SHORT_SUPERCLASSES),
CHAR (Character.class, "Character", char.class, "char", 'C', new char[0], Format.unsigned(16), CHAR_CLASS, CHAR_SUPERCLASSES),
INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0], Format.signed( 32), INT_CLASS, INT_SUPERCLASSES),
LONG ( Long.class, "Long", long.class, "long", 'J', new long[0], Format.signed( 64), LONG_CLASS, LONG_SUPERCLASSES),
FLOAT ( Float.class, "Float", float.class, "float", 'F', new float[0], Format.floating(32), FLOAT_CLASS, FLOAT_SUPERCLASSES),
DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0], Format.floating(64), DOUBLE_CLASS, DOUBLE_CLASS),
OBJECT ( Object.class, "Object", Object.class, "Object", 'L', new Object[0], Format.other( 1), 0, 0),
// VOID must be the last type, since it is "assignable" from any other type:
VOID ( Void.class, "Void", void.class, "void", 'V', null, Format.other( 0)),
VOID ( Void.class, "Void", void.class, "void", 'V', null, Format.other( 0), 0, 0),
;
public static final int COUNT = 10;
@ -52,16 +54,20 @@ public enum Wrapper {
private final String basicTypeString;
private final Object emptyArray;
private final int format;
private final int numericClass;
private final int superClasses;
private final String wrapperSimpleName;
private final String primitiveSimpleName;
private Wrapper(Class<?> wtype, String wtypeName, Class<?> ptype, String ptypeName, char tchar, Object emptyArray, int format) {
private Wrapper(Class<?> wtype, String wtypeName, Class<?> ptype, String ptypeName, char tchar, Object emptyArray, int format, int numericClass, int superClasses) {
this.wrapperType = wtype;
this.primitiveType = ptype;
this.basicTypeChar = tchar;
this.basicTypeString = String.valueOf(this.basicTypeChar);
this.emptyArray = emptyArray;
this.format = format;
this.numericClass = numericClass;
this.superClasses = superClasses;
this.wrapperSimpleName = wtypeName;
this.primitiveSimpleName = ptypeName;
}
@ -424,6 +430,11 @@ public enum Wrapper {
return findWrapperType(type) != null;
}
/** Query: Is the given type a wrapper, such as {@code Integer}, {@code Byte}, etc excluding {@code Void} and {@code Object}? */
public static boolean isWrapperNumericOrBooleanType(Class<?> type) {
return isWrapperType(type) && findWrapperType(type) != VOID && findWrapperType(type) != OBJECT;
}
/** Query: Is the given type a primitive, such as {@code int} or {@code void}? */
public static boolean isPrimitiveType(Class<?> type) {
return type.isPrimitive();
@ -628,4 +639,34 @@ public enum Wrapper {
values[i+vpos] = value;
}
}
// NumericClasses should be in sync with com.sun.tools.javac.code.TypeTag.NumericClasses
public static class NumericClasses {
public static final int BYTE_CLASS = 1;
public static final int CHAR_CLASS = 2;
public static final int SHORT_CLASS = 4;
public static final int INT_CLASS = 8;
public static final int LONG_CLASS = 16;
public static final int FLOAT_CLASS = 32;
public static final int DOUBLE_CLASS = 64;
static final int BYTE_SUPERCLASSES = BYTE_CLASS | SHORT_CLASS | INT_CLASS |
LONG_CLASS | FLOAT_CLASS | DOUBLE_CLASS;
static final int CHAR_SUPERCLASSES = CHAR_CLASS | INT_CLASS |
LONG_CLASS | FLOAT_CLASS | DOUBLE_CLASS;
static final int SHORT_SUPERCLASSES = SHORT_CLASS | INT_CLASS |
LONG_CLASS | FLOAT_CLASS | DOUBLE_CLASS;
static final int INT_SUPERCLASSES = INT_CLASS | LONG_CLASS | FLOAT_CLASS | DOUBLE_CLASS;
static final int LONG_SUPERCLASSES = LONG_CLASS | FLOAT_CLASS | DOUBLE_CLASS;
static final int FLOAT_SUPERCLASSES = FLOAT_CLASS | DOUBLE_CLASS;
}
public boolean isStrictSubRangeOf(Wrapper target) {
return (this.superClasses & target.numericClass) != 0 && this != target;
}
}

View File

@ -210,6 +210,7 @@ public class Preview {
case STRING_TEMPLATES -> true;
case IMPLICIT_CLASSES -> true;
case SUPER_INIT -> true;
case PRIMITIVE_PATTERNS -> true;
//Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
//When real preview features will be added, this method can be implemented to return 'true'
//for those selected features, and 'false' for all the others.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -254,6 +254,7 @@ public enum Source {
IMPLICIT_CLASSES(JDK21, Fragments.FeatureImplicitClasses, DiagKind.PLURAL),
WARN_ON_ILLEGAL_UTF8(MIN, JDK21),
UNNAMED_VARIABLES(JDK22, Fragments.FeatureUnnamedVariables, DiagKind.PLURAL),
PRIMITIVE_PATTERNS(JDK23, Fragments.FeaturePrimitivePatterns, DiagKind.PLURAL),
SUPER_INIT(JDK22, Fragments.FeatureSuperInit, DiagKind.NORMAL),
;

View File

@ -163,6 +163,7 @@ public class Symtab {
*/
public final Type objectType;
public final Type objectMethodsType;
public final Type exactConversionsSupportType;
public final Type objectsType;
public final Type classType;
public final Type classLoaderType;
@ -545,6 +546,7 @@ public class Symtab {
// Enter predefined classes. All are assumed to be in the java.base module.
objectType = enterClass("java.lang.Object");
objectMethodsType = enterClass("java.lang.runtime.ObjectMethods");
exactConversionsSupportType = enterClass("java.lang.runtime.ExactConversionsSupport");
objectsType = enterClass("java.util.Objects");
classType = enterClass("java.lang.Class");
stringType = enterClass("java.lang.String");

View File

@ -186,6 +186,10 @@ public enum TypeTag {
return (this.superClasses & tag.numericClass) != 0;
}
public boolean isInSuperClassesOf(TypeTag tag) {
return (this.numericClass & tag.superClasses) != 0;
}
/** Returns the number of type tags.
*/
public static int getTypeTagCount() {

View File

@ -5024,6 +5024,52 @@ public class Types {
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Unconditionality">
/** Check unconditionality between any combination of reference or primitive types.
*
* Rules:
* an identity conversion
* a widening reference conversion
* a widening primitive conversion (delegates to `checkUnconditionallyExactPrimitives`)
* a boxing conversion
* a boxing conversion followed by a widening reference conversion
*
* @param source Source primitive or reference type
* @param target Target primitive or reference type
*/
public boolean isUnconditionallyExact(Type source, Type target) {
if (isSameType(source, target)) {
return true;
}
return target.isPrimitive()
? isUnconditionallyExactPrimitives(source, target)
: isSubtype(boxedTypeOrType(erasure(source)), target);
}
/** Check unconditionality between primitive types.
*
* - widening from one integral type to another,
* - widening from one floating point type to another,
* - widening from byte, short, or char to a floating point type,
* - widening from int to double.
*
* @param selectorType Type of selector
* @param targetType Target type
*/
public boolean isUnconditionallyExactPrimitives(Type selectorType, Type targetType) {
if (isSameType(selectorType, targetType)) {
return true;
}
return (selectorType.isPrimitive() && targetType.isPrimitive()) &&
((selectorType.hasTag(BYTE) && !targetType.hasTag(CHAR)) ||
(selectorType.hasTag(SHORT) && (selectorType.getTag().isStrictSubRangeOf(targetType.getTag()))) ||
(selectorType.hasTag(CHAR) && (selectorType.getTag().isStrictSubRangeOf(targetType.getTag()))) ||
(selectorType.hasTag(INT) && (targetType.hasTag(DOUBLE) || targetType.hasTag(LONG))) ||
(selectorType.hasTag(FLOAT) && (selectorType.getTag().isStrictSubRangeOf(targetType.getTag()))));
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Annotation support">

View File

@ -1682,18 +1682,19 @@ public class Attr extends JCTree.Visitor {
try {
boolean enumSwitch = (seltype.tsym.flags() & Flags.ENUM) != 0;
boolean stringSwitch = types.isSameType(seltype, syms.stringType);
boolean booleanSwitch = types.isSameType(types.unboxedTypeOrType(seltype), syms.booleanType);
boolean errorEnumSwitch = TreeInfo.isErrorEnumSwitch(selector, cases);
boolean intSwitch = types.isAssignable(seltype, syms.intType);
boolean errorPrimitiveSwitch = seltype.isPrimitive() && !intSwitch;
boolean patternSwitch;
if (seltype.isPrimitive() && !intSwitch) {
preview.checkSourceLevel(selector.pos(), Feature.PRIMITIVE_PATTERNS);
patternSwitch = true;
}
if (!enumSwitch && !stringSwitch && !errorEnumSwitch &&
!intSwitch && !errorPrimitiveSwitch) {
!intSwitch) {
preview.checkSourceLevel(selector.pos(), Feature.PATTERN_SWITCH);
patternSwitch = true;
} else {
if (errorPrimitiveSwitch) {
log.error(selector.pos(), Errors.SelectorTypeNotAllowed(seltype));
}
patternSwitch = cases.stream()
.flatMap(c -> c.labels.stream())
.anyMatch(l -> l.hasTag(PATTERNCASELABEL) ||
@ -1709,6 +1710,7 @@ public class Attr extends JCTree.Visitor {
boolean hasNullPattern = false; // Is there a null pattern?
CaseTree.CaseKind caseKind = null;
boolean wasError = false;
JCCaseLabel unconditionalCaseLabel = null;
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
JCCase c = l.head;
if (caseKind == null) {
@ -1776,10 +1778,15 @@ public class Attr extends JCTree.Visitor {
} else if (!constants.add(s)) {
log.error(label.pos(), Errors.DuplicateCaseLabel);
}
} else if (!stringSwitch && !intSwitch && !errorPrimitiveSwitch) {
log.error(label.pos(), Errors.ConstantLabelNotCompatible(pattype, seltype));
} else if (!constants.add(pattype.constValue())) {
log.error(c.pos(), Errors.DuplicateCaseLabel);
}
else {
if (!stringSwitch && !intSwitch &&
!((pattype.getTag().isInSuperClassesOf(LONG) || pattype.getTag().equals(BOOLEAN)) &&
types.isSameType(types.unboxedTypeOrType(seltype), pattype))) {
log.error(label.pos(), Errors.ConstantLabelNotCompatible(pattype, seltype));
} else if (!constants.add(pattype.constValue())) {
log.error(c.pos(), Errors.DuplicateCaseLabel);
}
}
}
}
@ -1788,6 +1795,8 @@ public class Attr extends JCTree.Visitor {
log.error(label.pos(), Errors.DuplicateDefaultLabel);
} else if (hasUnconditionalPattern) {
log.error(label.pos(), Errors.UnconditionalPatternAndDefault);
} else if (booleanSwitch && constants.containsAll(Set.of(0, 1))) {
log.error(label.pos(), Errors.DefaultAndBothBooleanValues);
}
hasDefault = true;
matchBindings = MatchBindingsComputer.EMPTY;
@ -1796,12 +1805,13 @@ public class Attr extends JCTree.Visitor {
JCPattern pat = patternlabel.pat;
attribExpr(pat, switchEnv, seltype);
Type primaryType = TreeInfo.primaryPatternType(pat);
if (!primaryType.hasTag(TYPEVAR)) {
if (primaryType.isPrimitive()) {
preview.checkSourceLevel(pat.pos(), Feature.PRIMITIVE_PATTERNS);
} else if (!primaryType.hasTag(TYPEVAR)) {
primaryType = chk.checkClassOrArrayType(pat.pos(), primaryType);
}
if (!errorPrimitiveSwitch) {
checkCastablePattern(pat.pos(), seltype, primaryType);
}
checkCastablePattern(pat.pos(), seltype, primaryType);
Type patternType = types.erasure(primaryType);
JCExpression guard = c.guard;
if (guardBindings == null && guard != null) {
@ -1824,15 +1834,17 @@ public class Attr extends JCTree.Visitor {
boolean unconditional =
unguarded &&
!patternType.isErroneous() &&
types.isSubtype(types.boxedTypeOrType(types.erasure(seltype)),
patternType);
types.isUnconditionallyExact(seltype, patternType);
if (unconditional) {
if (hasUnconditionalPattern) {
log.error(pat.pos(), Errors.DuplicateUnconditionalPattern);
} else if (hasDefault) {
log.error(pat.pos(), Errors.UnconditionalPatternAndDefault);
} else if (booleanSwitch && constants.containsAll(Set.of(0, 1))) {
log.error(pat.pos(), Errors.UnconditionalPatternAndBothBooleanValues);
}
hasUnconditionalPattern = true;
unconditionalCaseLabel = label;
}
lastPatternErroneous = patternType.isErroneous();
} else {
@ -1859,7 +1871,7 @@ public class Attr extends JCTree.Visitor {
}
if (patternSwitch) {
chk.checkSwitchCaseStructure(cases);
chk.checkSwitchCaseLabelDominated(cases);
chk.checkSwitchCaseLabelDominated(unconditionalCaseLabel, cases);
}
if (switchTree.hasTag(SWITCH)) {
((JCSwitch) switchTree).hasUnconditionalPattern =
@ -4097,8 +4109,13 @@ public class Attr extends JCTree.Visitor {
}
public void visitTypeTest(JCInstanceOf tree) {
Type exprtype = chk.checkNullOrRefType(
tree.expr.pos(), attribExpr(tree.expr, env));
Type exprtype = attribExpr(tree.expr, env);
if (exprtype.isPrimitive()) {
preview.checkSourceLevel(tree.expr.pos(), Feature.PRIMITIVE_PATTERNS);
} else {
exprtype = chk.checkNullOrRefType(
tree.expr.pos(), exprtype);
}
Type clazztype;
JCTree typeTree;
if (tree.pattern.getTag() == BINDINGPATTERN ||
@ -4119,20 +4136,24 @@ public class Attr extends JCTree.Visitor {
typeTree = tree.pattern;
chk.validate(typeTree, env, false);
}
if (!clazztype.hasTag(TYPEVAR)) {
clazztype = chk.checkClassOrArrayType(typeTree.pos(), clazztype);
}
if (!clazztype.isErroneous() && !types.isReifiable(clazztype)) {
boolean valid = false;
if (allowReifiableTypesInInstanceof) {
valid = checkCastablePattern(tree.expr.pos(), exprtype, clazztype);
} else {
log.error(DiagnosticFlag.SOURCE_LEVEL, tree.pos(),
Feature.REIFIABLE_TYPES_INSTANCEOF.error(this.sourceName));
allowReifiableTypesInInstanceof = true;
if (clazztype.isPrimitive()) {
preview.checkSourceLevel(tree.pattern.pos(), Feature.PRIMITIVE_PATTERNS);
} else {
if (!clazztype.hasTag(TYPEVAR)) {
clazztype = chk.checkClassOrArrayType(typeTree.pos(), clazztype);
}
if (!valid) {
clazztype = types.createErrorType(clazztype);
if (!clazztype.isErroneous() && !types.isReifiable(clazztype)) {
boolean valid = false;
if (allowReifiableTypesInInstanceof) {
valid = checkCastablePattern(tree.expr.pos(), exprtype, clazztype);
} else {
log.error(DiagnosticFlag.SOURCE_LEVEL, tree.pos(),
Feature.REIFIABLE_TYPES_INSTANCEOF.error(this.sourceName));
allowReifiableTypesInInstanceof = true;
}
if (!valid) {
clazztype = types.createErrorType(clazztype);
}
}
}
chk.checkCastable(tree.expr.pos(), exprtype, clazztype);
@ -4152,12 +4173,9 @@ public class Attr extends JCTree.Visitor {
diags.fragment(Fragments.InconvertibleTypes(exprType, pattType)));
return false;
} else if ((exprType.isPrimitive() || pattType.isPrimitive()) &&
(!exprType.isPrimitive() ||
!pattType.isPrimitive() ||
!types.isSameType(exprType, pattType))) {
chk.basicHandler.report(pos,
diags.fragment(Fragments.NotApplicableTypes(exprType, pattType)));
return false;
(!exprType.isPrimitive() || !pattType.isPrimitive() || !types.isSameType(exprType, pattType))) {
preview.checkSourceLevel(pos, Feature.PRIMITIVE_PATTERNS);
return true;
} else if (warner.hasLint(LintCategory.UNCHECKED)) {
log.error(pos,
Errors.InstanceofReifiableNotSafe(exprType, pattType));

View File

@ -4800,11 +4800,13 @@ public class Check {
return false;
}
void checkSwitchCaseLabelDominated(List<JCCase> cases) {
void checkSwitchCaseLabelDominated(JCCaseLabel unconditionalCaseLabel, List<JCCase> cases) {
List<Pair<JCCase, JCCaseLabel>> caseLabels = List.nil();
boolean seenDefault = false;
boolean seenDefaultLabel = false;
boolean warnDominatedByDefault = false;
boolean unconditionalFound = false;
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
JCCase c = l.head;
for (JCCaseLabel label : c.labels) {
@ -4832,10 +4834,11 @@ public class Check {
JCCase testCase = caseAndLabel.fst;
JCCaseLabel testCaseLabel = caseAndLabel.snd;
Type testType = labelType(testCaseLabel);
if (types.isSubtype(currentType, testType) &&
boolean dominated = false;
if (unconditionalCaseLabel == testCaseLabel) unconditionalFound = true;
if (types.isUnconditionallyExact(currentType, testType) &&
!currentType.hasTag(ERROR) && !testType.hasTag(ERROR)) {
//the current label is potentially dominated by the existing (test) label, check:
boolean dominated = false;
if (label instanceof JCConstantCaseLabel) {
dominated |= !(testCaseLabel instanceof JCConstantCaseLabel) &&
TreeInfo.unguardedCase(testCase);
@ -4845,9 +4848,15 @@ public class Check {
dominated = patternDominated(testPatternCaseLabel.pat,
patternCL.pat);
}
if (dominated) {
log.error(label.pos(), Errors.PatternDominated);
}
}
// Domination can occur even when we have not an unconditional pair between case labels.
if (unconditionalFound && unconditionalCaseLabel != label) {
dominated = true;
}
if (dominated) {
log.error(label.pos(), Errors.PatternDominated);
}
}
caseLabels = caseLabels.prepend(Pair.of(c, label));
@ -4858,23 +4867,16 @@ public class Check {
private Type labelType(JCCaseLabel label) {
return types.erasure(switch (label.getTag()) {
case PATTERNCASELABEL -> ((JCPatternCaseLabel) label).pat.type;
case CONSTANTCASELABEL -> types.boxedTypeOrType(((JCConstantCaseLabel) label).expr.type);
case CONSTANTCASELABEL -> ((JCConstantCaseLabel) label).expr.type;
default -> throw Assert.error("Unexpected tree kind: " + label.getTag());
});
}
private boolean patternDominated(JCPattern existingPattern, JCPattern currentPattern) {
Type existingPatternType = types.erasure(existingPattern.type);
Type currentPatternType = types.erasure(currentPattern.type);
if (existingPatternType.isPrimitive() ^ currentPatternType.isPrimitive()) {
if (!types.isUnconditionallyExact(currentPatternType, existingPatternType)) {
return false;
}
if (existingPatternType.isPrimitive()) {
return types.isSameType(existingPatternType, currentPatternType);
} else {
if (!types.isSubtype(currentPatternType, existingPatternType)) {
return false;
}
}
if (currentPattern instanceof JCBindingPattern ||
currentPattern instanceof JCAnyPattern) {
return existingPattern instanceof JCBindingPattern ||

View File

@ -757,9 +757,14 @@ public class Flow {
}
}
}
tree.isExhaustive = tree.hasUnconditionalPattern ||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) ||
exhausts(tree.selector, tree.cases);
if (tree.hasUnconditionalPattern ||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases)) {
tree.isExhaustive = true;
} else {
tree.isExhaustive = exhausts(tree.selector, tree.cases);
}
if (!tree.isExhaustive) {
log.error(tree, Errors.NotExhaustive);
}
@ -770,6 +775,7 @@ public class Flow {
private boolean exhausts(JCExpression selector, List<JCCase> cases) {
Set<PatternDescription> patternSet = new HashSet<>();
Map<Symbol, Set<Symbol>> enum2Constants = new HashMap<>();
Set<Object> booleanLiterals = new HashSet<>();
for (JCCase c : cases) {
if (!TreeInfo.unguardedCase(c))
continue;
@ -780,19 +786,29 @@ public class Flow {
patternSet.add(makePatternDescription(component, patternLabel.pat));
}
} else if (l instanceof JCConstantCaseLabel constantLabel) {
Symbol s = TreeInfo.symbol(constantLabel.expr);
if (s != null && s.isEnum()) {
enum2Constants.computeIfAbsent(s.owner, x -> {
Set<Symbol> result = new HashSet<>();
s.owner.members()
.getSymbols(sym -> sym.kind == Kind.VAR && sym.isEnum())
.forEach(result::add);
return result;
}).remove(s);
if (types.unboxedTypeOrType(selector.type).hasTag(TypeTag.BOOLEAN)) {
Object value = ((JCLiteral) constantLabel.expr).value;
booleanLiterals.add(value);
} else {
Symbol s = TreeInfo.symbol(constantLabel.expr);
if (s != null && s.isEnum()) {
enum2Constants.computeIfAbsent(s.owner, x -> {
Set<Symbol> result = new HashSet<>();
s.owner.members()
.getSymbols(sym -> sym.kind == Kind.VAR && sym.isEnum())
.forEach(result::add);
return result;
}).remove(s);
}
}
}
}
}
if (types.unboxedTypeOrType(selector.type).hasTag(TypeTag.BOOLEAN) && booleanLiterals.size() == 2) {
return true;
}
for (Entry<Symbol, Set<Symbol>> e : enum2Constants.entrySet()) {
if (e.getValue().isEmpty()) {
patternSet.add(new BindingPattern(e.getKey().type));
@ -823,8 +839,7 @@ public class Flow {
private boolean checkCovered(Type seltype, Iterable<PatternDescription> patterns) {
for (Type seltypeComponent : components(seltype)) {
for (PatternDescription pd : patterns) {
if (pd instanceof BindingPattern bp &&
types.isSubtype(seltypeComponent, types.erasure(bp.type))) {
if(isBpCovered(seltypeComponent, pd)) {
return true;
}
}
@ -1104,8 +1119,7 @@ public class Flow {
reducedNestedPatterns[i] = newNested;
}
covered &= newNested instanceof BindingPattern bp &&
types.isSubtype(types.erasure(componentType[i]), types.erasure(bp.type));
covered &= isBpCovered(componentType[i], newNested);
}
if (covered) {
return new BindingPattern(rpOne.recordType);
@ -1281,6 +1295,30 @@ public class Flow {
}
}
private boolean isBpCovered(Type componentType, PatternDescription newNested) {
if (newNested instanceof BindingPattern bp) {
var seltype = types.erasure(componentType);
if (seltype.isPrimitive()) {
if (types.isSameType(bp.type, types.boxedClass(seltype).type)) {
return true;
}
// if the target is unconditionally exact to the pattern, target is covered
if (types.isUnconditionallyExact(seltype, bp.type)) {
return true;
}
} else if (seltype.isReference() && bp.type.isPrimitive() && types.isCastable(seltype, bp.type)) {
return true;
} else {
if (types.isSubtype(seltype, types.erasure(bp.type))) {
return true;
}
}
}
return false;
}
/**
* This pass implements the second step of the dataflow analysis, namely
* the exception analysis. This is to ensure that every checked exception that is
@ -3462,7 +3500,7 @@ public class Flow {
sealed interface PatternDescription { }
public PatternDescription makePatternDescription(Type selectorType, JCPattern pattern) {
if (pattern instanceof JCBindingPattern binding) {
Type type = types.isSubtype(selectorType, binding.type)
Type type = !selectorType.isPrimitive() && types.isSubtype(selectorType, binding.type)
? selectorType : binding.type;
return new BindingPattern(type);
} else if (pattern instanceof JCRecordPattern record) {

View File

@ -102,6 +102,7 @@ public class Lower extends TreeTranslator {
private final PkgInfo pkginfoOpt;
private final boolean optimizeOuterThis;
private final boolean useMatchException;
private final HashMap<TypePairs, String> typePairToName;
@SuppressWarnings("this-escape")
protected Lower(Context context) {
@ -133,6 +134,7 @@ public class Lower extends TreeTranslator {
Preview preview = Preview.instance(context);
useMatchException = Feature.PATTERN_SWITCH.allowedInSource(source) &&
(preview.isEnabled() || !preview.isPreview(Feature.PATTERN_SWITCH));
typePairToName = TypePairs.initialize(syms);
}
/** The currently enclosing class.
@ -2921,6 +2923,178 @@ public class Lower extends TreeTranslator {
result = tree;
}
/**
* All the exactness checks between primitive types that require a run-time
* check are in {@code java.lang.runtime.ExactConversionsSupport}. Those methods
* are in the form {@code ExactConversionsSupport.is<S>To<T>Exact} where both
* {@code S} and {@code T} are primitive types and correspond to the runtime
* action that will be executed to check whether a certain value (that is passed
* as a parameter) can be converted to {@code T} without loss of information.
*
* Rewrite {@code instanceof if expr : Object} and Type is primitive type:
*
* {@snippet :
* Object v = ...
* if (v instanceof float)
* =>
* if (let tmp$123 = v; tmp$123 instanceof Float)
* }
*
* Rewrite {@code instanceof if expr : wrapper reference type}
*
* {@snippet :
* Integer v = ...
* if (v instanceof float)
* =>
* if (let tmp$123 = v; tmp$123 != null && ExactConversionsSupport.intToFloatExact(tmp$123.intValue()))
* }
*
* Rewrite {@code instanceof if expr : primitive}
*
* {@snippet :
* int v = ...
* if (v instanceof float)
* =>
* if (let tmp$123 = v; ExactConversionsSupport.intToFloatExact(tmp$123))
* }
*
* More rewritings:
* <ul>
* <li>If the {@code instanceof} check is unconditionally exact rewrite to true.</li>
* <li>If expression type is {@code Byte}, {@code Short}, {@code Integer}, ..., an
* unboxing conversion followed by a widening primitive conversion.</li>
* <li>If expression type is a supertype: {@code Number}, a narrowing reference
* conversion followed by an unboxing conversion.</li>
* </ul>
*/
public void visitTypeTest(JCInstanceOf tree) {
if (tree.expr.type.isPrimitive() || tree.pattern.type.isPrimitive()) {
JCExpression exactnessCheck = null;
JCExpression instanceOfExpr = translate(tree.expr);
// preserving the side effects of the value
VarSymbol dollar_s = new VarSymbol(FINAL | SYNTHETIC,
names.fromString("tmp" + tree.pos + this.target.syntheticNameChar()),
tree.expr.type,
currentMethodSym);
JCStatement var = make.at(tree.pos())
.VarDef(dollar_s, instanceOfExpr).setType(dollar_s.type);
if (types.isUnconditionallyExact(tree.expr.type, tree.pattern.type)) {
exactnessCheck = make
.LetExpr(List.of(var), make.Literal(BOOLEAN, 1).setType(syms.booleanType.constType(1)))
.setType(syms.booleanType);
}
else if (tree.expr.type.isReference()) {
JCExpression nullCheck = makeBinary(NE,
make.Ident(dollar_s),
makeNull());
if (types.isUnconditionallyExact(types.unboxedType(tree.expr.type), tree.pattern.type)) {
exactnessCheck = make
.LetExpr(List.of(var), nullCheck)
.setType(syms.booleanType);
} else if (types.unboxedType(tree.expr.type).isPrimitive()) {
exactnessCheck = getExactnessCheck(tree,
boxIfNeeded(make.Ident(dollar_s), types.unboxedType(tree.expr.type)));
} else {
exactnessCheck = make.at(tree.pos())
.TypeTest(tree.expr, make.Type(types.boxedClass(tree.pattern.type).type))
.setType(syms.booleanType);
}
exactnessCheck = make.LetExpr(List.of(var), makeBinary(AND,
nullCheck,
exactnessCheck))
.setType(syms.booleanType);
}
else if (tree.expr.type.isPrimitive()) {
JCIdent argument = make.Ident(dollar_s);
JCExpression exactnessCheckCall =
getExactnessCheck(tree, argument);
exactnessCheck = make.LetExpr(List.of(var), exactnessCheckCall)
.setType(syms.booleanType);
}
result = exactnessCheck;
} else {
tree.expr = translate(tree.expr);
tree.pattern = translate(tree.pattern);
result = tree;
}
}
// TypePairs should be in sync with the corresponding record in SwitchBootstraps
record TypePairs(TypeSymbol from, TypeSymbol to) {
public static TypePairs of(Symtab syms, Type from, Type to) {
if (from == syms.byteType || from == syms.shortType || from == syms.charType) {
from = syms.intType;
}
return new TypePairs(from, to);
}
public TypePairs(Type from, Type to) {
this(from.tsym, to.tsym);
}
public static HashMap<TypePairs, String> initialize(Symtab syms) {
HashMap<TypePairs, String> typePairToName = new HashMap<>();
typePairToName.put(new TypePairs(syms.byteType, syms.charType), "isIntToCharExact"); // redirected
typePairToName.put(new TypePairs(syms.shortType, syms.byteType), "isIntToByteExact"); // redirected
typePairToName.put(new TypePairs(syms.shortType, syms.charType), "isIntToCharExact"); // redirected
typePairToName.put(new TypePairs(syms.charType, syms.byteType), "isIntToByteExact"); // redirected
typePairToName.put(new TypePairs(syms.charType, syms.shortType), "isIntToShortExact"); // redirected
typePairToName.put(new TypePairs(syms.intType, syms.byteType), "isIntToByteExact");
typePairToName.put(new TypePairs(syms.intType, syms.shortType), "isIntToShortExact");
typePairToName.put(new TypePairs(syms.intType, syms.charType), "isIntToCharExact");
typePairToName.put(new TypePairs(syms.intType, syms.floatType), "isIntToFloatExact");
typePairToName.put(new TypePairs(syms.longType, syms.byteType), "isLongToByteExact");
typePairToName.put(new TypePairs(syms.longType, syms.shortType), "isLongToShortExact");
typePairToName.put(new TypePairs(syms.longType, syms.charType), "isLongToCharExact");
typePairToName.put(new TypePairs(syms.longType, syms.intType), "isLongToIntExact");
typePairToName.put(new TypePairs(syms.longType, syms.floatType), "isLongToFloatExact");
typePairToName.put(new TypePairs(syms.longType, syms.doubleType), "isLongToDoubleExact");
typePairToName.put(new TypePairs(syms.floatType, syms.byteType), "isFloatToByteExact");
typePairToName.put(new TypePairs(syms.floatType, syms.shortType), "isFloatToShortExact");
typePairToName.put(new TypePairs(syms.floatType, syms.charType), "isFloatToCharExact");
typePairToName.put(new TypePairs(syms.floatType, syms.intType), "isFloatToIntExact");
typePairToName.put(new TypePairs(syms.floatType, syms.longType), "isFloatToLongExact");
typePairToName.put(new TypePairs(syms.doubleType, syms.byteType), "isDoubleToByteExact");
typePairToName.put(new TypePairs(syms.doubleType, syms.shortType), "isDoubleToShortExact");
typePairToName.put(new TypePairs(syms.doubleType, syms.charType), "isDoubleToCharExact");
typePairToName.put(new TypePairs(syms.doubleType, syms.intType), "isDoubleToIntExact");
typePairToName.put(new TypePairs(syms.doubleType, syms.longType), "isDoubleToLongExact");
typePairToName.put(new TypePairs(syms.doubleType, syms.floatType), "isDoubleToFloatExact");
return typePairToName;
}
}
private JCExpression getExactnessCheck(JCInstanceOf tree, JCExpression argument) {
TypePairs pair = TypePairs.of(syms, types.unboxedTypeOrType(tree.expr.type), tree.pattern.type);
Name exactnessFunction = names.fromString(typePairToName.get(pair));
// Resolve the exactness method
Symbol ecsym = lookupMethod(tree.pos(),
exactnessFunction,
syms.exactConversionsSupportType,
List.of(pair.from.type));
// Generate the method call ExactnessChecks.<exactness method>(<argument>);
JCFieldAccess select = make.Select(
make.QualIdent(syms.exactConversionsSupportType.tsym),
exactnessFunction);
select.sym = ecsym;
select.setType(syms.booleanType);
JCExpression exactnessCheck = make.Apply(List.nil(),
select,
List.of(argument));
exactnessCheck.setType(syms.booleanType);
return exactnessCheck;
}
public void visitNewClass(JCNewClass tree) {
ClassSymbol c = (ClassSymbol)tree.constructor.owner;

View File

@ -236,7 +236,7 @@ public class TransPatterns extends TreeTranslator {
Type principalType = types.erasure(TreeInfo.primaryPatternType((pattern)));
JCExpression resultExpression = (JCExpression) this.<JCTree>translate(pattern);
if (!tree.allowNull && !principalType.isPrimitive()) {
if (!tree.allowNull || !types.isSubtype(currentValue.type, principalType)) {
resultExpression =
makeBinary(Tag.AND,
makeTypeTest(make.Ident(currentValue), make.Type(principalType)),
@ -496,12 +496,14 @@ public class TransPatterns extends TreeTranslator {
.toArray(s -> new LoadableConstant[s]);
boolean enumSelector = seltype.tsym.isEnum();
boolean primitiveSelector = seltype.isPrimitive();
Name bootstrapName = enumSelector ? names.enumSwitch : names.typeSwitch;
MethodSymbol bsm = rs.resolveInternalMethod(tree.pos(), env, syms.switchBootstrapsType,
bootstrapName, staticArgTypes, List.nil());
Type resolvedSelectorType = seltype;
MethodType indyType = new MethodType(
List.of(enumSelector ? seltype : syms.objectType, syms.intType),
List.of(resolvedSelectorType, syms.intType),
syms.intType,
List.nil(),
syms.methodClass
@ -735,6 +737,81 @@ public class TransPatterns extends TreeTranslator {
}
}
private Symbol.DynamicVarSymbol makePrimitive(DiagnosticPosition pos, Type primitiveType) {
Assert.checkNonNull(currentClass);
List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
syms.stringType,
new ClassType(syms.classType.getEnclosingType(),
List.of(syms.constantBootstrapsType),
syms.classType.tsym));
Name bootstrapName = names.fromString("primitiveClass");
MethodSymbol bsm = rs.resolveInternalMethod(pos, env, syms.constantBootstrapsType,
bootstrapName, bsm_staticArgs, List.nil());
PrimitiveGenerator primitiveGenerator = new PrimitiveGenerator();
primitiveGenerator.assembleSig(primitiveType);
return new Symbol.DynamicVarSymbol(names.fromString(primitiveGenerator.sb.toString()),
syms.noSymbol,
new Symbol.MethodHandleSymbol(bsm),
syms.classType,
new LoadableConstant[]{});
}
private Symbol.DynamicVarSymbol makeBooleanConstant(DiagnosticPosition pos, int constant) {
Assert.checkNonNull(currentClass);
List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
syms.stringType,
new ClassType(syms.classType.getEnclosingType(),
List.of(syms.constantBootstrapsType),
syms.classType.tsym));
Name bootstrapName = names.fromString("getStaticFinal");
MethodSymbol bsm = rs.resolveInternalMethod(pos, env, syms.constantBootstrapsType,
bootstrapName, bsm_staticArgs, List.nil());
return new Symbol.DynamicVarSymbol(constant == 0 ? names.fromString("FALSE") : names.fromString("TRUE"),
syms.noSymbol,
new Symbol.MethodHandleSymbol(bsm),
types.boxedTypeOrType(syms.booleanType),
new LoadableConstant[]{});
}
private class PrimitiveGenerator extends Types.SignatureGenerator {
/**
* An output buffer for type signatures.
*/
StringBuilder sb = new StringBuilder();
PrimitiveGenerator() {
super(types);
}
@Override
protected void append(char ch) {
sb.append(ch);
}
@Override
protected void append(byte[] ba) {
sb.append(new String(ba));
}
@Override
protected void append(Name name) {
sb.append(name.toString());
}
@Override
public String toString() {
return sb.toString();
}
}
JCMethodInvocation makeApply(JCExpression selector, Name name, List<JCExpression> args) {
MethodSymbol method = rs.resolveInternalMethod(
currentClassTree.pos(), env,
@ -823,7 +900,8 @@ public class TransPatterns extends TreeTranslator {
}
JCBindingPattern binding = (JCBindingPattern) instanceofCheck.pattern;
hasUnconditional =
instanceofCheck.allowNull &&
(!types.erasure(binding.type).isPrimitive() ? instanceofCheck.allowNull :
types.isUnconditionallyExact(commonNestedExpression.type, types.erasure(binding.type))) &&
accList.tail.isEmpty();
List<JCCaseLabel> newLabel;
@ -854,6 +932,7 @@ public class TransPatterns extends TreeTranslator {
}
JCSwitch newSwitch = make.Switch(commonNestedExpression, nestedCases.toList());
newSwitch.patternSwitch = true;
newSwitch.hasUnconditionalPattern = hasUnconditional;
JCPatternCaseLabel leadingTest =
(JCPatternCaseLabel) accummulator.first().labels.head;
leadingTest.syntheticGuard = null;
@ -930,16 +1009,20 @@ public class TransPatterns extends TreeTranslator {
}
private Type principalType(JCTree p) {
return types.boxedTypeOrType(types.erasure(TreeInfo.primaryPatternType(p)));
return types.erasure(TreeInfo.primaryPatternType(p));
}
private LoadableConstant toLoadableConstant(JCCaseLabel l, Type selector) {
if (l.hasTag(Tag.PATTERNCASELABEL)) {
Type principalType = principalType(((JCPatternCaseLabel) l).pat);
if (types.isSubtype(selector, principalType)) {
return (LoadableConstant) selector;
if (((JCPatternCaseLabel) l).pat.type.isReference()) {
if (types.isSubtype(selector, principalType)) {
return (LoadableConstant) selector;
} else {
return (LoadableConstant) principalType;
}
} else {
return (LoadableConstant) principalType;
return makePrimitive(l.pos(), principalType);
}
} else if (l.hasTag(Tag.CONSTANTCASELABEL) && !TreeInfo.isNullCaseLabel(l)) {
JCExpression expr = ((JCConstantCaseLabel) l).expr;
@ -954,8 +1037,11 @@ public class TransPatterns extends TreeTranslator {
Assert.checkNonNull(expr.type.constValue());
return switch (expr.type.getTag()) {
case BYTE, CHAR,
SHORT, INT -> LoadableConstant.Int((Integer) expr.type.constValue());
case BOOLEAN -> makeBooleanConstant(l.pos(), (Integer) expr.type.constValue());
case BYTE, CHAR, SHORT, INT -> LoadableConstant.Int((Integer) expr.type.constValue());
case LONG -> LoadableConstant.Long((Long) expr.type.constValue());
case FLOAT -> LoadableConstant.Float((Float) expr.type.constValue());
case DOUBLE -> LoadableConstant.Double((Double) expr.type.constValue());
case CLASS -> LoadableConstant.String((String) expr.type.constValue());
default -> throw new AssertionError();
};

View File

@ -549,6 +549,12 @@ compiler.err.duplicate.unconditional.pattern=\
compiler.err.unconditional.pattern.and.default=\
switch has both an unconditional pattern and a default label
compiler.err.unconditional.pattern.and.both.boolean.values=\
switch has both boolean values and an unconditional pattern
compiler.err.default.and.both.boolean.values=\
switch has both boolean values and a default label
compiler.err.guard.not.allowed=\
guards are only allowed for case with a pattern
@ -563,10 +569,6 @@ compiler.err.cannot.assign.not.declared.guard=\
compiler.err.constant.label.not.compatible=\
constant label of type {0} is not compatible with switch selector type {1}
# 0: type
compiler.err.selector.type.not.allowed=\
selector type {0} is not allowed
compiler.err.flows.through.to.pattern=\
illegal fall-through to a pattern
@ -2665,10 +2667,6 @@ compiler.warn.prob.found.req=\
compiler.misc.inconvertible.types=\
{0} cannot be converted to {1}
# 0: type, 1: type
compiler.misc.not.applicable.types=\
pattern of type {1} is not applicable at {0}
# 0: type, 1: type
compiler.misc.possible.loss.of.precision=\
possible lossy conversion from {0} to {1}
@ -3200,6 +3198,9 @@ compiler.misc.feature.deconstruction.patterns=\
compiler.misc.feature.unnamed.variables=\
unnamed variables
compiler.misc.feature.primitive.patterns=\
primitive patterns
compiler.misc.feature.records=\
records

View File

@ -209,14 +209,14 @@ class CompletenessAnalyzer {
THROWS(TokenKind.THROWS, XDECL|XBRACESNEEDED), // throws
// Primarive type names
BOOLEAN(TokenKind.BOOLEAN, XEXPR1|XDECL1), // boolean
BYTE(TokenKind.BYTE, XEXPR1|XDECL1), // byte
CHAR(TokenKind.CHAR, XEXPR1|XDECL1), // char
DOUBLE(TokenKind.DOUBLE, XEXPR1|XDECL1), // double
FLOAT(TokenKind.FLOAT, XEXPR1|XDECL1), // float
INT(TokenKind.INT, XEXPR1|XDECL1), // int
LONG(TokenKind.LONG, XEXPR1|XDECL1), // long
SHORT(TokenKind.SHORT, XEXPR1|XDECL1), // short
BOOLEAN(TokenKind.BOOLEAN, XEXPR1|XDECL1|XTERM), // boolean
BYTE(TokenKind.BYTE, XEXPR1|XDECL1|XTERM), // byte
CHAR(TokenKind.CHAR, XEXPR1|XDECL1|XTERM), // char
DOUBLE(TokenKind.DOUBLE, XEXPR1|XDECL1|XTERM), // double
FLOAT(TokenKind.FLOAT, XEXPR1|XDECL1|XTERM), // float
INT(TokenKind.INT, XEXPR1|XDECL1|XTERM), // int
LONG(TokenKind.LONG, XEXPR1|XDECL1|XTERM), // long
SHORT(TokenKind.SHORT, XEXPR1|XDECL1|XTERM), // short
VOID(TokenKind.VOID, XEXPR1|XDECL1), // void
// Modifiers keywords
@ -805,8 +805,24 @@ class CompletenessAnalyzer {
}
public Completeness parseExpression() {
while (token.kind.isExpression())
while (token.kind.isExpression()) {
CT prevToken = in.prevCT;
nextToken();
// primitive types can only appear in the end of an `instanceof` expression
switch (token.kind) {
case EOF:
switch (in.prevCT.kind) {
case BYTE, SHORT, CHAR, INT, LONG, FLOAT, DOUBLE, BOOLEAN:
switch (prevToken.kind) {
case INSTANCEOF:
return Completeness.COMPLETE;
default:
return Completeness.DEFINITELY_INCOMPLETE;
}
}
}
}
return Completeness.COMPLETE;
}

View File

@ -0,0 +1,236 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import org.testng.annotations.Test;
import java.io.Serializable;
import java.lang.Enum.EnumDesc;
import java.lang.classfile.ClassFile;
import java.lang.constant.ClassDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.MethodTypeDesc;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.AccessFlag;
import java.lang.runtime.ExactConversionsSupport;
import java.lang.runtime.SwitchBootstraps;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.testng.Assert.*;
/**
* @test
* @bug 8304487
* @summary Verify boundary and special cases of exact conversion predicates
* @enablePreview
* @modules java.base/jdk.internal.classfile
* @compile ExactnessConversionsSupportTest.java
* @run testng/othervm ExactnessConversionsSupportTest
*/
@Test
public class ExactnessConversionsSupportTest {
public static void main(String[] args) {
testByte();
testShort();
testChar();
testInt();
testLong();
testFloat();
testDouble();
}
public static void testByte() {
assertEquals(true, ExactConversionsSupport.isIntToByteExact((byte) (Byte.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToByteExact((byte) (0)));
assertEquals(true, ExactConversionsSupport.isIntToByteExact((byte) (Byte.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isIntToByteExact((short) (Short.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToByteExact((short) (0)));
assertEquals(false, ExactConversionsSupport.isIntToByteExact((short) (Short.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isIntToByteExact((char) (Character.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToByteExact((char) (Character.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isIntToByteExact(Integer.MAX_VALUE));
assertEquals(true, ExactConversionsSupport.isIntToByteExact(0));
assertEquals(false, ExactConversionsSupport.isIntToByteExact(Integer.MIN_VALUE));
assertEquals(false, ExactConversionsSupport.isLongToByteExact(Long.MAX_VALUE));
assertEquals(true, ExactConversionsSupport.isLongToByteExact(0L));
assertEquals(false, ExactConversionsSupport.isLongToByteExact(Long.MIN_VALUE));
assertEquals(false, ExactConversionsSupport.isFloatToByteExact(Float.MAX_VALUE));
assertEquals(true, ExactConversionsSupport.isFloatToByteExact((float) 0));
assertEquals(false, ExactConversionsSupport.isFloatToByteExact(Float.MIN_VALUE));
assertEquals(false, ExactConversionsSupport.isFloatToByteExact(Float.NaN));
assertEquals(false, ExactConversionsSupport.isFloatToByteExact(Float.POSITIVE_INFINITY));
assertEquals(false, ExactConversionsSupport.isFloatToByteExact(Float.NEGATIVE_INFINITY));
assertEquals(false, ExactConversionsSupport.isFloatToByteExact(-0.0f));
assertEquals(true, ExactConversionsSupport.isFloatToByteExact(+0.0f));
assertEquals(false, ExactConversionsSupport.isDoubleToByteExact(Double.MAX_VALUE));
assertEquals(true, ExactConversionsSupport.isDoubleToByteExact(0d));
assertEquals(false, ExactConversionsSupport.isDoubleToByteExact(Double.MIN_VALUE));
assertEquals(false, ExactConversionsSupport.isDoubleToByteExact(Double.NaN));
assertEquals(false, ExactConversionsSupport.isDoubleToByteExact(Double.POSITIVE_INFINITY));
assertEquals(false, ExactConversionsSupport.isDoubleToByteExact(Double.NEGATIVE_INFINITY));
assertEquals(false, ExactConversionsSupport.isDoubleToByteExact(-0.0d));
assertEquals(true, ExactConversionsSupport.isDoubleToByteExact(+0.0d));
}
public static void testShort() {
assertEquals(true, ExactConversionsSupport.isIntToShortExact((byte) (Byte.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToShortExact((byte) (0)));
assertEquals(true, ExactConversionsSupport.isIntToShortExact((byte) (Byte.MIN_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToShortExact((short) (Short.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToShortExact((short) (0)));
assertEquals(true, ExactConversionsSupport.isIntToShortExact((short) (Short.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isIntToShortExact((char) (Character.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToShortExact((char) (Character.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isIntToShortExact((Integer.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToShortExact((0)));
assertEquals(false, ExactConversionsSupport.isIntToShortExact((Integer.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isLongToShortExact(Long.MAX_VALUE));
assertEquals(true, ExactConversionsSupport.isLongToShortExact(0L));
assertEquals(false, ExactConversionsSupport.isLongToShortExact(Long.MIN_VALUE));
assertEquals(false, ExactConversionsSupport.isFloatToShortExact(Float.MAX_VALUE));
assertEquals(true, ExactConversionsSupport.isFloatToShortExact(0f));
assertEquals(false, ExactConversionsSupport.isFloatToShortExact(Float.MIN_VALUE));
assertEquals(false, ExactConversionsSupport.isFloatToShortExact(Float.MIN_VALUE));
assertEquals(false, ExactConversionsSupport.isFloatToShortExact(Float.NaN));
assertEquals(false, ExactConversionsSupport.isFloatToShortExact(Float.POSITIVE_INFINITY));
assertEquals(false, ExactConversionsSupport.isFloatToShortExact(Float.NEGATIVE_INFINITY));
assertEquals(false, ExactConversionsSupport.isFloatToShortExact(-0.0f));
assertEquals(true, ExactConversionsSupport.isFloatToShortExact(+0.0f));
assertEquals(false, ExactConversionsSupport.isDoubleToShortExact(Double.MAX_VALUE));
assertEquals(true, ExactConversionsSupport.isDoubleToShortExact((double) 0));
assertEquals(false, ExactConversionsSupport.isDoubleToShortExact(Double.MIN_VALUE));
assertEquals(false, ExactConversionsSupport.isDoubleToShortExact(Double.NaN));
assertEquals(false, ExactConversionsSupport.isDoubleToShortExact(Double.POSITIVE_INFINITY));
assertEquals(false, ExactConversionsSupport.isDoubleToShortExact(Double.NEGATIVE_INFINITY));
assertEquals(false, ExactConversionsSupport.isDoubleToShortExact(-0.0d));
assertEquals(true, ExactConversionsSupport.isDoubleToShortExact(+0.0d));
}
public static void testChar() {
assertEquals(true, ExactConversionsSupport.isIntToCharExact((byte) (Byte.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToCharExact((byte) (0)));
assertEquals(false, ExactConversionsSupport.isIntToCharExact((byte) (Byte.MIN_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToCharExact((short) (Short.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToCharExact((short) (0)));
assertEquals(false, ExactConversionsSupport.isIntToCharExact((short) (Short.MIN_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToCharExact((char) (Character.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToCharExact((char) (Character.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isIntToCharExact (Integer.MAX_VALUE));
assertEquals(true, ExactConversionsSupport.isIntToCharExact(0));
assertEquals(false, ExactConversionsSupport.isIntToCharExact(Integer.MIN_VALUE));
assertEquals(false, ExactConversionsSupport.isLongToCharExact(Long.MAX_VALUE));
assertEquals(true, ExactConversionsSupport.isLongToCharExact(0l));
assertEquals(false, ExactConversionsSupport.isLongToCharExact(Long.MIN_VALUE));
assertEquals(false, ExactConversionsSupport.isFloatToCharExact(Float.MAX_VALUE));
assertEquals(true, ExactConversionsSupport.isFloatToCharExact((float) 0));
assertEquals(false, ExactConversionsSupport.isFloatToCharExact(Float.MIN_VALUE));
assertEquals(false, ExactConversionsSupport.isFloatToCharExact(Float.NaN));
assertEquals(false, ExactConversionsSupport.isFloatToCharExact(Float.POSITIVE_INFINITY));
assertEquals(false, ExactConversionsSupport.isFloatToCharExact(Float.NEGATIVE_INFINITY));
assertEquals(false, ExactConversionsSupport.isFloatToCharExact(-0.0f));
assertEquals(true, ExactConversionsSupport.isFloatToCharExact(+0.0f));
assertEquals(false, ExactConversionsSupport.isDoubleToCharExact(Double.MAX_VALUE));
assertEquals(true, ExactConversionsSupport.isDoubleToCharExact((double) 0));
assertEquals(false, ExactConversionsSupport.isDoubleToCharExact(Double.MIN_VALUE));
assertEquals(false, ExactConversionsSupport.isDoubleToCharExact(Double.NaN));
assertEquals(false, ExactConversionsSupport.isDoubleToCharExact(Double.POSITIVE_INFINITY));
assertEquals(false, ExactConversionsSupport.isDoubleToCharExact(Double.NEGATIVE_INFINITY));
assertEquals(false, ExactConversionsSupport.isDoubleToCharExact(-0.0d));
assertEquals(true, ExactConversionsSupport.isDoubleToCharExact(+0.0d));
}
public static void testInt() {
assertEquals(false, ExactConversionsSupport.isLongToIntExact((Long.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isLongToIntExact((0L)));
assertEquals(false, ExactConversionsSupport.isLongToIntExact((Long.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isFloatToIntExact((Float.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isFloatToIntExact(((float) 0)));
assertEquals(false, ExactConversionsSupport.isFloatToIntExact((Float.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isFloatToIntExact((Float.NaN)));
assertEquals(false, ExactConversionsSupport.isFloatToIntExact((Float.POSITIVE_INFINITY)));
assertEquals(false, ExactConversionsSupport.isFloatToIntExact((Float.NEGATIVE_INFINITY)));
assertEquals(false, ExactConversionsSupport.isFloatToIntExact((-0.0f)));
assertEquals(true, ExactConversionsSupport.isFloatToIntExact((+0.0f)));
assertEquals(false, ExactConversionsSupport.isDoubleToIntExact((Double.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isDoubleToIntExact(((double) 0)));
assertEquals(false, ExactConversionsSupport.isDoubleToIntExact((Double.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isDoubleToIntExact((Double.NaN)));
assertEquals(false, ExactConversionsSupport.isDoubleToIntExact((Double.POSITIVE_INFINITY)));
assertEquals(false, ExactConversionsSupport.isDoubleToIntExact((Double.NEGATIVE_INFINITY)));
assertEquals(false, ExactConversionsSupport.isDoubleToIntExact((-0.0d)));
assertEquals(true, ExactConversionsSupport.isDoubleToIntExact((+0.0d)));
}
public static void testLong() {
assertEquals(false, ExactConversionsSupport.isFloatToLongExact((Float.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isFloatToLongExact(((float) 0)));
assertEquals(false, ExactConversionsSupport.isFloatToLongExact((Float.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isFloatToLongExact((Float.NaN)));
assertEquals(false, ExactConversionsSupport.isFloatToLongExact((Float.POSITIVE_INFINITY)));
assertEquals(false, ExactConversionsSupport.isFloatToLongExact((Float.NEGATIVE_INFINITY)));
assertEquals(false, ExactConversionsSupport.isFloatToLongExact((-0.0f)));
assertEquals(true, ExactConversionsSupport.isFloatToLongExact((+0.0f)));
assertEquals(false, ExactConversionsSupport.isDoubleToLongExact((Double.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isDoubleToLongExact(((double) 0)));
assertEquals(false, ExactConversionsSupport.isDoubleToLongExact((Double.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isDoubleToLongExact((Double.NaN)));
assertEquals(false, ExactConversionsSupport.isDoubleToLongExact((Double.POSITIVE_INFINITY)));
assertEquals(false, ExactConversionsSupport.isDoubleToLongExact((Double.NEGATIVE_INFINITY)));
assertEquals(false, ExactConversionsSupport.isDoubleToLongExact((-0.0d)));
assertEquals(true, ExactConversionsSupport.isDoubleToLongExact((+0.0d)));
}
public static void testFloat() {
assertEquals(true, ExactConversionsSupport.isIntToFloatExact(((byte) (Byte.MAX_VALUE))));
assertEquals(true, ExactConversionsSupport.isIntToFloatExact(((byte) (0))));
assertEquals(true, ExactConversionsSupport.isIntToFloatExact(((byte) (Byte.MIN_VALUE))));
assertEquals(true, ExactConversionsSupport.isIntToFloatExact(((short) (Short.MAX_VALUE))));
assertEquals(true, ExactConversionsSupport.isIntToFloatExact(((short) (0))));
assertEquals(true, ExactConversionsSupport.isIntToFloatExact(((short) (Short.MIN_VALUE))));
assertEquals(true, ExactConversionsSupport.isIntToFloatExact(((char) (Character.MAX_VALUE))));
assertEquals(true, ExactConversionsSupport.isIntToFloatExact(((char) (Character.MIN_VALUE))));
assertEquals(false, ExactConversionsSupport.isIntToFloatExact( (Integer.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isIntToFloatExact((0)));
assertEquals(true, ExactConversionsSupport.isIntToFloatExact((Integer.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isLongToFloatExact((Long.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isLongToFloatExact((0l)));
assertEquals(true, ExactConversionsSupport.isLongToFloatExact((Long.MIN_VALUE)));
assertEquals(false, ExactConversionsSupport.isDoubleToFloatExact((Double.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isDoubleToFloatExact(((double) 0)));
assertEquals(false, ExactConversionsSupport.isDoubleToFloatExact((Double.MIN_VALUE)));
assertEquals(true, ExactConversionsSupport.isDoubleToFloatExact((Double.NaN)));
assertEquals(true, ExactConversionsSupport.isDoubleToFloatExact((Double.POSITIVE_INFINITY)));
assertEquals(true, ExactConversionsSupport.isDoubleToFloatExact((Double.NEGATIVE_INFINITY)));
assertEquals(true, ExactConversionsSupport.isDoubleToFloatExact((-0.0d)));
assertEquals(true, ExactConversionsSupport.isDoubleToFloatExact((+0.0d)));
}
public static void testDouble() {
assertEquals(false, ExactConversionsSupport.isLongToDoubleExact((Long.MAX_VALUE)));
assertEquals(true, ExactConversionsSupport.isLongToDoubleExact((0L)));
assertEquals(true, ExactConversionsSupport.isLongToDoubleExact((Long.MIN_VALUE)));
}
static void assertEquals(boolean expected, boolean actual) {
if (expected != actual) {
throw new AssertionError("Expected: " + expected + ", actual: " + actual);
}
}
}

View File

@ -46,6 +46,7 @@ import static org.testng.Assert.fail;
* @test
* @bug 8318144
* @enablePreview
* @modules java.base/jdk.internal.classfile
* @compile SwitchBootstrapsTest.java
* @run testng/othervm SwitchBootstrapsTest
*/
@ -111,12 +112,9 @@ public class SwitchBootstrapsTest {
testType(Short.valueOf((short) 1), 0, 0, 1, Integer.class);
testType(Character.valueOf((char) 1), 0, 0, 1, Integer.class);
testType(Integer.valueOf((int) 1), 0, 0, 1, Integer.class);
try {
testType(1, 0, 1, 1.0, Integer.class);
fail("Didn't get the expected exception.");
} catch (IllegalArgumentException ex) {
//OK
}
testType(1, 0, 1, 1.0d, Integer.class);
testType(1, 0, 1, 1.0f, Integer.class);
testType(1, 0, 1, true, Integer.class);
testType("", 0, 0, String.class, String.class, String.class, String.class, String.class);
testType("", 1, 1, String.class, String.class, String.class, String.class, String.class);
testType("", 2, 2, String.class, String.class, String.class, String.class, String.class);
@ -124,6 +122,15 @@ public class SwitchBootstrapsTest {
testType("", 3, 3, String.class, String.class, String.class, String.class, String.class);
testType("", 4, 4, String.class, String.class, String.class, String.class, String.class);
testType("", 0, 0);
testType(new Object() {
@Override
public boolean equals(Object obj) {
if (obj instanceof Long i) {
return i == 1;
}
return super.equals(obj);
}
}, 0, 1, 1L);
}
public void testEnums() throws Throwable {
@ -178,7 +185,6 @@ public class SwitchBootstrapsTest {
public void testWrongSwitchTypes() throws Throwable {
MethodType[] switchTypes = new MethodType[] {
MethodType.methodType(int.class, Object.class),
MethodType.methodType(int.class, double.class, int.class),
MethodType.methodType(int.class, Object.class, Integer.class)
};
for (MethodType switchType : switchTypes) {

View File

@ -396,4 +396,9 @@ public class CompletenessTest extends KullaTesting {
assertStatus("int[] m = {1, 2}, n = new int[0]; int i;", COMPLETE,
"int[] m = {1, 2}, n = new int[0];");
}
public void testInstanceOf() {
assertStatus("i instanceof Integer", COMPLETE, "i instanceof Integer");
assertStatus("i instanceof int", COMPLETE, "i instanceof int");
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8304487
* @summary Compiler Implementation for Primitive types in patterns, instanceof, and switch (Preview)
* @build KullaTesting TestingInputStream
* @run testng PrimitiveInstanceOfTest
*/
import jdk.jshell.JShell;
import org.testng.annotations.Test;
import java.util.function.Consumer;
import static org.testng.Assert.assertEquals;
@Test
public class PrimitiveInstanceOfTest extends KullaTesting {
public void testInstanceOf() {
assertEval("int i = 42;");
assertEval("i instanceof Integer");
assertEval("i instanceof int");
}
public void testInstanceOfRef() {
assertEval("Integer i = 42;");
assertEval("i instanceof Integer");
assertEval("i instanceof Number");
}
@org.testng.annotations.BeforeMethod
public void setUp() {
super.setUp(bc -> bc.compilerOptions("--source", System.getProperty("java.specification.version"), "--enable-preview").remoteVMOptions("--enable-preview"));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -21,13 +21,16 @@
* questions.
*/
// key: compiler.misc.not.applicable.types
// key: compiler.err.prob.found.req
class NotApplicableTypes {
void t(int i) {
switch (i) {
case Integer j -> {}
}
// key: compiler.err.default.and.both.boolean.values
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview --source 23
public class DefaultAndBothBoolean {
private int test(boolean sel) {
return switch (sel) {
case true -> 1;
case false -> 2;
default -> 3;
};
}
}
}

View File

@ -21,12 +21,12 @@
* questions.
*/
// key: compiler.err.selector.type.not.allowed
// key: compiler.misc.feature.primitive.patterns
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
public class SelectorTypeNotAllowed {
private void noLong(long sel) {
switch (sel) {
default -> {}
}
class PrimitivePatternMatching {
boolean m(Object o) {
return o instanceof int s;
}
}
}

View File

@ -27,5 +27,13 @@
import java.util.*;
class TypeReqClassArray {
boolean b = (this instanceof int);
interface Sig {
void m(int s);
}
Sig consume(Sig s) { return s; }
public void meth() {
Sig s = consume(int::new);
}
}

View File

@ -25,6 +25,7 @@
// key: compiler.err.type.found.req
class TypeReqRef {
int i;
boolean b = (i instanceof Object);
void method(Inner<int> in) {}
class Inner<T> {}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// key: compiler.err.unconditional.pattern.and.both.boolean.values
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview --source 23
public class UnconditionalPatternAndBothBoolean {
private int test(boolean sel) {
return switch (sel) {
case true -> 1;
case false -> 2;
case boolean b -> 3;
};
}
}

View File

@ -3,7 +3,7 @@
* @bug 8231827
* @summary Match which involves a cast conversion
* @compile/fail/ref=CastConversionMatch.out -XDrawDiagnostics CastConversionMatch.java
*/
* @compile --enable-preview --source ${jdk.version} CastConversionMatch.java */
public class CastConversionMatch {
public static void meth() {

View File

@ -1,2 +1,2 @@
CastConversionMatch.java:11:26: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array)
CastConversionMatch.java:11:26: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.primitive.patterns)
1 error

View File

@ -27,13 +27,6 @@ public class DeconstructionPatternErrors {
if (p instanceof GenRecord<String>(Integer v)); //inconsistency in types
if (p instanceof P2(var v, var v)); //duplicated variables
if (p instanceof P6(P2(var v1, var v2), P2(var v1, var v2))); //duplicated variables
if (p instanceof P7(byte b)); //incorrect pattern type
if (p instanceof P7(long l)); //incorrect pattern type
switch (p) {
case P7(byte b) -> {} //incorrect pattern type - no exception should occur
case P7(long l) -> {} //incorrect pattern type - no exception should occur
default -> {}
}
GenRecord<String> r1 = null;
if (r1 instanceof GenRecord(String s)) {}
switch (r1) {

View File

@ -1,7 +1,7 @@
DeconstructionPatternErrors.java:42:37: compiler.err.illegal.start.of.type
DeconstructionPatternErrors.java:44:28: compiler.err.illegal.start.of.type
DeconstructionPatternErrors.java:46:42: compiler.err.expected: ';'
DeconstructionPatternErrors.java:46:43: compiler.err.not.stmt
DeconstructionPatternErrors.java:35:37: compiler.err.illegal.start.of.type
DeconstructionPatternErrors.java:37:28: compiler.err.illegal.start.of.type
DeconstructionPatternErrors.java:39:42: compiler.err.expected: ';'
DeconstructionPatternErrors.java:39:43: compiler.err.not.stmt
DeconstructionPatternErrors.java:15:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List<java.lang.String>, java.util.ArrayList<java.lang.Integer>)
DeconstructionPatternErrors.java:16:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, java.util.ArrayList<java.lang.Integer>
DeconstructionPatternErrors.java:17:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int)
@ -20,8 +20,4 @@ DeconstructionPatternErrors.java:27:13: compiler.err.instanceof.reifiable.not.sa
DeconstructionPatternErrors.java:28:40: compiler.err.match.binding.exists
DeconstructionPatternErrors.java:29:56: compiler.err.already.defined: kindname.variable, v1, kindname.method, meth()
DeconstructionPatternErrors.java:29:64: compiler.err.already.defined: kindname.variable, v2, kindname.method, meth()
DeconstructionPatternErrors.java:30:29: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, byte)
DeconstructionPatternErrors.java:31:29: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
DeconstructionPatternErrors.java:33:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, byte)
DeconstructionPatternErrors.java:34:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
26 errors
22 errors

View File

@ -0,0 +1,138 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8304487
* @summary Compiler Implementation for Primitive types in patterns, instanceof, and switch (Preview)
* @library /tools/lib /tools/javac/lib
* @modules
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.file
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.util
* @build toolbox.ToolBox toolbox.JavacTask
* @build combo.ComboTestHelper
* @compile PrimitiveInstanceOfComboTest.java
* @run main PrimitiveInstanceOfComboTest
*/
import combo.ComboInstance;
import combo.ComboParameter;
import combo.ComboTask;
import combo.ComboTestHelper;
import toolbox.ToolBox;
public class PrimitiveInstanceOfComboTest extends ComboInstance<PrimitiveInstanceOfComboTest> {
private static final String JAVA_VERSION = System.getProperty("java.specification.version");
protected ToolBox tb;
PrimitiveInstanceOfComboTest() {
super();
tb = new ToolBox();
}
public static void main(String... args) throws Exception {
new ComboTestHelper<PrimitiveInstanceOfComboTest>()
.withDimension("TYPE1", (x, type1) -> x.type1 = type1, Type.values())
.withDimension("TYPE2", (x, type2) -> x.type2 = type2, Type.values())
.withFailMode(ComboTestHelper.FailMode.FAIL_FAST)
.run(PrimitiveInstanceOfComboTest::new);
}
private Type type1;
private Type type2;
private static final String test1 =
"""
public class Test {
public static void doTest(#{TYPE1} in) {
var r = (#{TYPE2}) in;
}
}
""";
private static final String test2 =
"""
public class Test {
public static void doTest(#{TYPE1} in) {
var r = in instanceof #{TYPE2};
}
}
""";
@Override
protected void doWork() throws Throwable {
ComboTask task1 = newCompilationTask()
.withSourceFromTemplate(test1.replace("#{TYPE1}", type1.code).replace("#{TYPE2}", type2.code))
.withOption("--enable-preview")
.withOption("-source").withOption(JAVA_VERSION);;
ComboTask task2 = newCompilationTask()
.withSourceFromTemplate(test2.replace("#{TYPE1}", type1.code).replace("#{TYPE2}", type2.code))
.withOption("--enable-preview")
.withOption("-source").withOption(JAVA_VERSION);;
task1.generate(result1 -> {
task2.generate(result2 -> {
if (result1.hasErrors() ^ result2.hasErrors()) {
throw new AssertionError("Unexpected result: " +
"\n task1: " + result1.hasErrors() + ", info: " + result1.compilationInfo() +
"\n task1: " + result2.hasErrors() + ", info: " + result2.compilationInfo());
}
});
});
}
public enum Type implements ComboParameter {
BYTE("byte"),
SHORT("short"),
CHAR("char"),
INT("int"),
LONG("long"),
FLOAT("float"),
DOUBLE("double"),
BOOLEAN("boolean"),
BYTE_r("Byte"),
SHORT_r("Short"),
CHAR_r("Character"),
INTEGER_r("Integer"),
LONG_r("Long"),
FLOAT_r("Float"),
DOUBLE_r("Double"),
BOOLEAN_r("Boolean");
private final String code;
Type(String code) {
this.code = code;
}
@Override
public String expand(String optParameter) {
throw new UnsupportedOperationException("Not supported.");
}
}
}

View File

@ -0,0 +1,27 @@
/*
* @test /nodynamiccopyright/
* @bug 8304487
* @summary Compiler Implementation for Primitive types in patterns, instanceof, and switch (Preview)
* @enablePreview
* @compile/fail/ref=PrimitiveInstanceOfErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW PrimitiveInstanceOfErrors.java
*/
public class PrimitiveInstanceOfErrors {
public static boolean unboxingAndNarrowingPrimitiveNotAllowedPerCastingConversion() {
Long l_within_int_range = 42L;
Long l_outside_int_range = 999999999999999999L;
return l_within_int_range instanceof int && !(l_outside_int_range instanceof int);
}
public static <T extends Integer> boolean wideningReferenceConversionUnboxingAndNarrowingPrimitive(T i) {
return i instanceof byte;
}
public static void boxingConversionsBetweenIncompatibleTypes() {
int i = 42;
boolean ret1 = i instanceof Integer; // (Integer) i // OK and true
boolean ret2 = i instanceof Double; // error: incompatible types
boolean ret3 = i instanceof Short; // error: incompatible types
}
}

View File

@ -0,0 +1,8 @@
PrimitiveInstanceOfErrors.java:13:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Long, int)
PrimitiveInstanceOfErrors.java:13:55: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Long, int)
PrimitiveInstanceOfErrors.java:17:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: T, byte)
PrimitiveInstanceOfErrors.java:24:24: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.Double)
PrimitiveInstanceOfErrors.java:25:24: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.Short)
- compiler.note.preview.filename: PrimitiveInstanceOfErrors.java, DEFAULT
- compiler.note.preview.recompile
5 errors

View File

@ -0,0 +1,275 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8304487
* @summary Compiler Implementation for Primitive types in patterns, instanceof, and switch (Preview)
* @enablePreview
* @compile PrimitiveInstanceOfNumericValueTests.java
* @run main/othervm PrimitiveInstanceOfNumericValueTests
*/
public class PrimitiveInstanceOfNumericValueTests {
public static void main(String[] args) {
testByte();
testShort();
testChar();
testInt();
testLong();
testFloat();
testDouble();
}
public static void testByte() {
assertEquals(true, ((byte) (Byte.MAX_VALUE)) instanceof byte);
assertEquals(true, ((byte) (0)) instanceof byte);
assertEquals(true, ((byte) (Byte.MIN_VALUE)) instanceof byte);
assertEquals(false, ((short) (Short.MAX_VALUE)) instanceof byte);
assertEquals(true, ((short) (0)) instanceof byte);
assertEquals(false, ((short) (Short.MIN_VALUE)) instanceof byte);
assertEquals(false, ((char) (Character.MAX_VALUE)) instanceof byte);
assertEquals(true, ((char) (Character.MIN_VALUE)) instanceof byte);
assertEquals(false, (Integer.MAX_VALUE) instanceof byte);
assertEquals(true, (0) instanceof byte);
assertEquals(false, (Integer.MIN_VALUE) instanceof byte);
assertEquals(false, (Long.MAX_VALUE) instanceof byte);
assertEquals(true, (0L) instanceof byte);
assertEquals(false, (Long.MIN_VALUE) instanceof byte);
assertEquals(false, (Float.MAX_VALUE) instanceof byte);
assertEquals(true, ((float) 0) instanceof byte);
assertEquals(false, (Float.MIN_VALUE) instanceof byte);
assertEquals(false, (Float.NaN) instanceof byte);
assertEquals(false, (Float.POSITIVE_INFINITY) instanceof byte);
assertEquals(false, (Float.NEGATIVE_INFINITY) instanceof byte);
assertEquals(false, (-0.0f) instanceof byte);
assertEquals(true, (+0.0f) instanceof byte);
assertEquals(false, (Double.MAX_VALUE) instanceof byte);
assertEquals(true, ((double) 0) instanceof byte);
assertEquals(false, (Double.MIN_VALUE) instanceof byte);
assertEquals(false, (Double.NaN) instanceof byte);
assertEquals(false, (Double.POSITIVE_INFINITY) instanceof byte);
assertEquals(false, (Double.NEGATIVE_INFINITY) instanceof byte);
assertEquals(false, (-0.0d) instanceof byte);
assertEquals(true, (+0.0d) instanceof byte);
}
public static void testShort() {
assertEquals(true, ((byte) (Byte.MAX_VALUE)) instanceof short);
assertEquals(true, ((byte) (0)) instanceof short);
assertEquals(true, ((byte) (Byte.MIN_VALUE)) instanceof short);
assertEquals(true, ((short) (Short.MAX_VALUE)) instanceof short);
assertEquals(true, ((short) (0)) instanceof short);
assertEquals(true, ((short) (Short.MIN_VALUE)) instanceof short);
assertEquals(false, ((char) (Character.MAX_VALUE)) instanceof short);
assertEquals(true, ((char) (Character.MIN_VALUE)) instanceof short);
assertEquals(false, (Integer.MAX_VALUE) instanceof short);
assertEquals(true, (0) instanceof short);
assertEquals(false, (Integer.MIN_VALUE) instanceof short);
assertEquals(false, (Long.MAX_VALUE) instanceof short);
assertEquals(true, (0L) instanceof short);
assertEquals(false, (Long.MIN_VALUE) instanceof short);
assertEquals(false, (Float.MAX_VALUE) instanceof short);
assertEquals(true, ((float) 0) instanceof short);
assertEquals(false, (Float.MIN_VALUE) instanceof short);
assertEquals(false, (Float.MIN_VALUE) instanceof short);
assertEquals(false, (Float.NaN) instanceof short);
assertEquals(false, (Float.POSITIVE_INFINITY) instanceof short);
assertEquals(false, (Float.NEGATIVE_INFINITY) instanceof short);
assertEquals(false, (-0.0f) instanceof short);
assertEquals(true, (+0.0f) instanceof short);
assertEquals(false, (Double.MAX_VALUE) instanceof short);
assertEquals(true, ((double) 0) instanceof short);
assertEquals(false, (Double.MIN_VALUE) instanceof short);
assertEquals(false, (Double.NaN) instanceof short);
assertEquals(false, (Double.POSITIVE_INFINITY) instanceof short);
assertEquals(false, (Double.NEGATIVE_INFINITY) instanceof short);
assertEquals(false, (-0.0d) instanceof short);
assertEquals(true, (+0.0d) instanceof short);
}
public static void testChar() {
assertEquals(true, ((byte) (Byte.MAX_VALUE)) instanceof char);
assertEquals(true, ((byte) (0)) instanceof char);
assertEquals(false, ((byte) (Byte.MIN_VALUE)) instanceof char);
assertEquals(true, ((short) (Short.MAX_VALUE)) instanceof char);
assertEquals(true, ((short) (0)) instanceof char);
assertEquals(false, ((short) (Short.MIN_VALUE)) instanceof char);
assertEquals(true, ((char) (Character.MAX_VALUE)) instanceof char);
assertEquals(true, ((char) (Character.MIN_VALUE)) instanceof char);
assertEquals(false, (Integer.MAX_VALUE) instanceof char);
assertEquals(true, (0) instanceof char);
assertEquals(false, (Integer.MIN_VALUE) instanceof char);
assertEquals(false, (Long.MAX_VALUE) instanceof char);
assertEquals(true, (0L) instanceof char);
assertEquals(false, (Long.MIN_VALUE) instanceof char);
assertEquals(false, (Float.MAX_VALUE) instanceof char);
assertEquals(true, ((float) 0) instanceof char);
assertEquals(false, (Float.MIN_VALUE) instanceof char);
assertEquals(false, (Float.NaN) instanceof char);
assertEquals(false, (Float.POSITIVE_INFINITY) instanceof char);
assertEquals(false, (Float.NEGATIVE_INFINITY) instanceof char);
assertEquals(false, (-0.0f) instanceof char);
assertEquals(true, (+0.0f) instanceof char);
assertEquals(false, (Double.MAX_VALUE) instanceof char);
assertEquals(true, ((double) 0) instanceof char);
assertEquals(false, (Double.MIN_VALUE) instanceof char);
assertEquals(false, (Double.NaN) instanceof char);
assertEquals(false, (Double.POSITIVE_INFINITY) instanceof char);
assertEquals(false, (Double.NEGATIVE_INFINITY) instanceof char);
assertEquals(false, (-0.0d) instanceof char);
assertEquals(true, (+0.0d) instanceof char);
}
public static void testInt() {
assertEquals(true, ((byte) (Byte.MAX_VALUE)) instanceof int);
assertEquals(true, ((byte) (0)) instanceof int);
assertEquals(true, ((byte) (Byte.MIN_VALUE)) instanceof int);
assertEquals(true, ((short) (Short.MAX_VALUE)) instanceof int);
assertEquals(true, ((short) (0)) instanceof int);
assertEquals(true, ((short) (Short.MIN_VALUE)) instanceof int);
assertEquals(true, ((char) (Character.MAX_VALUE)) instanceof int);
assertEquals(true, ((char) (Character.MIN_VALUE)) instanceof int);
assertEquals(true, (Integer.MAX_VALUE) instanceof int);
assertEquals(true, (0) instanceof int);
assertEquals(true, (Integer.MIN_VALUE) instanceof int);
assertEquals(false, (Long.MAX_VALUE) instanceof int);
assertEquals(true, (0L) instanceof int);
assertEquals(false, (Long.MIN_VALUE) instanceof int);
assertEquals(false, (Float.MAX_VALUE) instanceof int);
assertEquals(true, ((float) 0) instanceof int);
assertEquals(false, (Float.MIN_VALUE) instanceof int);
assertEquals(false, (Float.NaN) instanceof int);
assertEquals(false, (Float.POSITIVE_INFINITY) instanceof int);
assertEquals(false, (Float.NEGATIVE_INFINITY) instanceof int);
assertEquals(false, (-0.0f) instanceof int);
assertEquals(true, (+0.0f) instanceof int);
assertEquals(false, (Double.MAX_VALUE) instanceof int);
assertEquals(true, ((double) 0) instanceof int);
assertEquals(false, (Double.MIN_VALUE) instanceof int);
assertEquals(false, (Double.NaN) instanceof int);
assertEquals(false, (Double.POSITIVE_INFINITY) instanceof int);
assertEquals(false, (Double.NEGATIVE_INFINITY) instanceof int);
assertEquals(false, (-0.0d) instanceof int);
assertEquals(true, (+0.0d) instanceof int);
}
public static void testLong() {
assertEquals(true, ((byte) (Byte.MAX_VALUE)) instanceof long);
assertEquals(true, ((byte) (0)) instanceof long);
assertEquals(true, ((byte) (Byte.MIN_VALUE)) instanceof long);
assertEquals(true, ((short) (Short.MAX_VALUE)) instanceof long);
assertEquals(true, ((short) (0)) instanceof long);
assertEquals(true, ((short) (Short.MIN_VALUE)) instanceof long);
assertEquals(true, ((char) (Character.MAX_VALUE)) instanceof long);
assertEquals(true, ((char) (Character.MIN_VALUE)) instanceof long);
assertEquals(true, (Integer.MAX_VALUE) instanceof long);
assertEquals(true, (0L) instanceof long);
assertEquals(true, (Integer.MIN_VALUE) instanceof long);
assertEquals(true, (Long.MAX_VALUE) instanceof long);
assertEquals(true, (0) instanceof long);
assertEquals(true, (Long.MIN_VALUE) instanceof long);
assertEquals(false, (Float.MAX_VALUE) instanceof long);
assertEquals(true, ((float) 0) instanceof long);
assertEquals(false, (Float.MIN_VALUE) instanceof long);
assertEquals(false, (Float.NaN) instanceof long);
assertEquals(false, (Float.POSITIVE_INFINITY) instanceof long);
assertEquals(false, (Float.NEGATIVE_INFINITY) instanceof long);
assertEquals(false, (-0.0f) instanceof long);
assertEquals(true, (+0.0f) instanceof long);
assertEquals(false, (Double.MAX_VALUE) instanceof long);
assertEquals(true, ((double) 0) instanceof long);
assertEquals(false, (Double.MIN_VALUE) instanceof long);
assertEquals(false, (Double.NaN) instanceof long);
assertEquals(false, (Double.POSITIVE_INFINITY) instanceof long);
assertEquals(false, (Double.NEGATIVE_INFINITY) instanceof long);
assertEquals(false, (-0.0d) instanceof long);
assertEquals(true, (+0.0d) instanceof long);
}
public static void testFloat() {
assertEquals(true, ((byte) (Byte.MAX_VALUE)) instanceof float);
assertEquals(true, ((byte) (0) instanceof float));
assertEquals(true, ((byte) (Byte.MIN_VALUE)) instanceof float);
assertEquals(true, ((short) (Short.MAX_VALUE)) instanceof float);
assertEquals(true, ((short) (0)) instanceof float);
assertEquals(true, ((short) (Short.MIN_VALUE)) instanceof float);
assertEquals(true, ((char) (Character.MAX_VALUE)) instanceof float);
assertEquals(true, ((char) (Character.MIN_VALUE)) instanceof float);
assertEquals(false, (Integer.MAX_VALUE) instanceof float);
assertEquals(true, (0) instanceof float);
assertEquals(true, (Integer.MIN_VALUE) instanceof float);
assertEquals(false, (Long.MAX_VALUE) instanceof float);
assertEquals(true, (0L) instanceof float);
assertEquals(true, (Long.MIN_VALUE) instanceof float);
assertEquals(true, (Float.MAX_VALUE) instanceof float);
assertEquals(true, ((float) 0) instanceof float);
assertEquals(true, (Float.MIN_VALUE) instanceof float);
assertEquals(true, (Float.NaN) instanceof float);
assertEquals(true, (Float.POSITIVE_INFINITY) instanceof float);
assertEquals(true, (Float.NEGATIVE_INFINITY) instanceof float);
assertEquals(true, (-0.0f) instanceof float);
assertEquals(true, (+0.0f) instanceof float);
assertEquals(false, (Double.MAX_VALUE) instanceof float);
assertEquals(true, ((double) 0) instanceof float);
assertEquals(false, (Double.MIN_VALUE) instanceof float);
assertEquals(true, (Double.NaN) instanceof float);
assertEquals(true, (Double.POSITIVE_INFINITY) instanceof float);
assertEquals(true, (Double.NEGATIVE_INFINITY) instanceof float);
assertEquals(true, (-0.0d) instanceof float);
assertEquals(true, (+0.0d) instanceof float);
}
public static void testDouble() {
assertEquals(true, ((byte) (Byte.MAX_VALUE)) instanceof double);
assertEquals(true, ((byte) (0)) instanceof double);
assertEquals(true, ((byte) (Byte.MIN_VALUE)) instanceof double);
assertEquals(true, ((short) (Short.MAX_VALUE)) instanceof double);
assertEquals(true, ((short) (0)) instanceof double);
assertEquals(true, ((short) (Short.MIN_VALUE)) instanceof double);
assertEquals(true, ((char) (Character.MAX_VALUE)) instanceof double);
assertEquals(true, ((char) (Character.MIN_VALUE)) instanceof double);
assertEquals(true, (Integer.MAX_VALUE) instanceof double);
assertEquals(true, (0) instanceof double);
assertEquals(true, (Integer.MIN_VALUE) instanceof double);
assertEquals(false, (Long.MAX_VALUE) instanceof double);
assertEquals(true, (0L) instanceof double);
assertEquals(true, (Long.MIN_VALUE) instanceof double);
assertEquals(true, (Float.MAX_VALUE) instanceof double);
assertEquals(true, ((float) 0) instanceof double);
assertEquals(true, (Float.MIN_VALUE) instanceof double);
assertEquals(true, (Float.NaN) instanceof double);
assertEquals(true, (Float.POSITIVE_INFINITY) instanceof double);
assertEquals(true, (Float.NEGATIVE_INFINITY) instanceof double);
assertEquals(true, (-0.0f) instanceof double);
assertEquals(true, (+0.0f) instanceof double);
assertEquals(true, (Double.MAX_VALUE) instanceof double);
assertEquals(true, ((double) 0) instanceof double);
assertEquals(true, (Double.MIN_VALUE) instanceof double);
assertEquals(true, (Double.NaN) instanceof double);
assertEquals(true, (Double.POSITIVE_INFINITY) instanceof double);
assertEquals(true, (Double.NEGATIVE_INFINITY) instanceof double);
assertEquals(true, (-0.0d) instanceof double);
assertEquals(true, (+0.0d) instanceof double);
}
static void assertEquals(boolean expected, boolean actual) {
if (expected != actual) {
throw new AssertionError("Expected: " + expected + ", actual: " + actual);
}
}
}

View File

@ -0,0 +1,163 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8304487
* @summary Compiler Implementation for Primitive types in patterns, instanceof, and switch (Preview)
* @enablePreview
* @compile PrimitiveInstanceOfPatternOpWithRecordPatterns.java
* @run main/othervm PrimitiveInstanceOfPatternOpWithRecordPatterns
*/
public class PrimitiveInstanceOfPatternOpWithRecordPatterns {
public static void main(String[] args) {
assertEquals(true, identityPrimitiveConversion());
assertEquals(true, wideningPrimitiveConversion());
assertEquals(true, narrowingPrimitiveConversion());
assertEquals(true, wideningAndNarrowingPrimitiveConversion());
assertEquals(true, boxingConversion());
assertEquals(true, boxingAndWideningReferenceConversion());
assertEquals(true, unboxing());
assertEquals(true, unboxingWithObject());
assertEquals(true, wideningReferenceConversionUnboxing());
assertEquals(true, wideningReferenceConversionUnboxingAndWideningPrimitive());
assertEquals(true, unboxingAndWideningPrimitiveExact());
assertEquals(false, unboxingAndWideningPrimitiveNotExact());
assertEquals(true, unboxingWhenNullAndWideningPrimitive());
assertEquals(true, narrowingAndUnboxing());
}
public static boolean identityPrimitiveConversion() {
R_int r = new R_int(42);
return r instanceof R_int(int _);
}
public static boolean wideningPrimitiveConversion() {
R_byte b = new R_byte((byte) 42);
R_short s = new R_short((short) 42);
R_char c = new R_char('a');
return b instanceof R_byte(int _) && s instanceof R_short(int _) && c instanceof R_char(int _);
}
public static boolean narrowingPrimitiveConversion() {
R_long l_within_int_range = new R_long(42L);
R_long l_outside_int_range = new R_long(999999999999999999L);
return l_within_int_range instanceof R_long(int _) && !(l_outside_int_range instanceof R_long(int _));
}
public static boolean wideningAndNarrowingPrimitiveConversion() {
R_byte b = new R_byte((byte) 42);
R_byte b2 = new R_byte((byte) -42);
R_char c = new R_char((char) 42);
return b instanceof R_byte(char _) && c instanceof R_char(byte _) && !(b2 instanceof R_byte(char _));
}
public static boolean boxingConversion() {
R_int i = new R_int(42);
return i instanceof R_int(Integer _);
}
public static boolean boxingAndWideningReferenceConversion() {
R_int i = new R_int(42);
return i instanceof R_int(Object _) &&
i instanceof R_int(Number _) &&
i instanceof R_int(Comparable _);
}
public static boolean unboxing() {
R_Integer i = new R_Integer(Integer.valueOf(1));
return i instanceof R_Integer(int _);
}
public static boolean unboxingWithObject() {
R_Object o1 = new R_Object((int) 42);
R_Object o2 = new R_Object((byte) 42);
return o1 instanceof R_Object(int i1) &&
o2 instanceof R_Object(byte b1) &&
!(o1 instanceof R_Object(byte b2) &&
!(o2 instanceof R_Object(int i2)));
}
public static <T extends Integer> boolean wideningReferenceConversionUnboxing() {
R_generic i = new R_generic(42);
return i instanceof R_generic(int _);
}
public static <T extends Integer> boolean wideningReferenceConversionUnboxingAndWideningPrimitive() {
R_generic i = new R_generic(42);
return i instanceof R_generic(double _);
}
public static boolean unboxingAndWideningPrimitiveExact() {
R_ByteValue b = new R_ByteValue(Byte.valueOf((byte)42));
R_ShortValue s = new R_ShortValue(Short.valueOf((short)42));
R_CharacterValue c = new R_CharacterValue(Character.valueOf('a'));
return (b instanceof R_ByteValue(int _)) && (s instanceof R_ShortValue(int _)) && (c instanceof R_CharacterValue(int _));
}
public static boolean unboxingAndWideningPrimitiveNotExact() {
int smallestIntNotRepresentable = 16777217; // 2^24 + 1
R_Integer i = new R_Integer(Integer.valueOf(smallestIntNotRepresentable));
return i instanceof R_Integer(float _);
}
public static boolean unboxingWhenNullAndWideningPrimitive() {
R_ByteValue b = new R_ByteValue(null);
R_ShortValue s = new R_ShortValue(null);
R_CharacterValue c = new R_CharacterValue(null);
return !(b instanceof R_ByteValue(int _)) && !(s instanceof R_ShortValue(int _)) && !(c instanceof R_CharacterValue(int _));
}
public static boolean narrowingAndUnboxing() {
R_Number n = new R_Number(Byte.valueOf((byte) 42));
return n instanceof R_Number(byte _);
}
static void assertEquals(boolean expected, boolean actual) {
if (expected != actual) {
throw new AssertionError("Expected: " + expected + ", actual: " + actual);
}
}
record R_int(int i) {}
record R_byte(byte b) {}
record R_short(short b) {}
record R_char(char c) {}
record R_long(long l) {}
record R_Integer(Integer i) {}
record R_Object(Object i) {}
record R_generic<T extends Integer>(int i) {}
record R_ByteValue(Byte b) {}
record R_ShortValue(Short s) {}
record R_CharacterValue(Character s) {}
record R_Number(Number s) {}
}

View File

@ -0,0 +1,172 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8304487
* @summary Compiler Implementation for Primitive types in patterns, instanceof, and switch (Preview)
* @enablePreview
* @compile PrimitiveInstanceOfTypeComparisonOp.java
* @run main/othervm PrimitiveInstanceOfTypeComparisonOp
*/
public class PrimitiveInstanceOfTypeComparisonOp {
public static void main(String[] args) {
assertEquals(true, identityPrimitiveConversion());
assertEquals(true, wideningPrimitiveConversion());
assertEquals(true, narrowingPrimitiveConversion());
assertEquals(true, wideningAndNarrowingPrimitiveConversion());
assertEquals(true, boxingConversion());
assertEquals(true, boxingAndWideningReferenceConversion());
assertEquals(true, unboxing());
assertEquals(true, unboxingWithObject());
assertEquals(true, wideningReferenceConversionUnboxing(42));
assertEquals(true, wideningReferenceConversionUnboxingAndWideningPrimitive(42));
assertEquals(true, unboxingAndWideningPrimitiveExact());
assertEquals(false, unboxingAndWideningPrimitiveNotExact());
assertEquals(true, unboxingWhenNullAndWideningPrimitive());
assertEquals(true, narrowingAndUnboxing());
assertEquals(true, patternExtractRecordComponent());
assertEquals(true, exprMethod());
assertEquals(true, exprStaticallyQualified());
}
public static boolean identityPrimitiveConversion() {
int i = 42;
return i instanceof int;
}
public static boolean wideningPrimitiveConversion() {
byte b = (byte) 42;
short s = (short) 42;
char c = 'a';
return b instanceof int && s instanceof int && c instanceof int;
}
public static boolean narrowingPrimitiveConversion() {
long l_within_int_range = 42L;
long l_outside_int_range = 999999999999999999L;
return l_within_int_range instanceof int && !(l_outside_int_range instanceof int);
}
public static boolean wideningAndNarrowingPrimitiveConversion() {
byte b = (byte) 42;
byte b2 = (byte) -42;
char c = (char) 42;
return b instanceof char && c instanceof byte && !(b2 instanceof char);
}
public static boolean boxingConversion() {
int i = 42;
return i instanceof Integer;
}
public static boolean boxingAndWideningReferenceConversion() {
int i = 42;
return i instanceof Object &&
i instanceof Number &&
i instanceof Comparable;
}
public static boolean unboxing() {
Integer i = Integer.valueOf(1);
return i instanceof int;
}
public static boolean unboxingWithObject() {
Object o1 = (int) 42;
Object o2 = (byte) 42;
return o1 instanceof int i1 &&
o2 instanceof byte b1 &&
!(o1 instanceof byte b2 &&
!(o2 instanceof int i2));
}
public static <T extends Integer> boolean wideningReferenceConversionUnboxing(T i) {
return i instanceof int;
}
public static <T extends Integer> boolean wideningReferenceConversionUnboxingAndWideningPrimitive(T i) {
return i instanceof double;
}
public static boolean unboxingAndWideningPrimitiveExact() {
Byte b = Byte.valueOf((byte)42);
Short s = Short.valueOf((short)42);
Character c = Character.valueOf('a');
return (b instanceof int) && (s instanceof int) && (c instanceof int);
}
public static boolean unboxingAndWideningPrimitiveNotExact() {
int smallestIntNotRepresentable = 16777217; // 2^24 + 1
Integer i = Integer.valueOf(smallestIntNotRepresentable);
return i instanceof float;
}
public static boolean unboxingWhenNullAndWideningPrimitive() {
Byte b = null;
Short s = null;
Character c = null;
return !(b instanceof int) && !(s instanceof int) && !(c instanceof int);
}
public static boolean narrowingAndUnboxing() {
Number n = Byte.valueOf((byte) 42);
return n instanceof byte;
}
public record P(int i) { }
public static boolean patternExtractRecordComponent() {
Object p = new P(42);
if (p instanceof P(byte b)) {
return b == 42;
}
return false;
}
public static int meth() {return 42;}
public static boolean exprMethod() {
return meth() instanceof int;
}
public class A1 {
public static int i = 42;
}
public static boolean exprStaticallyQualified() {
return A1.i instanceof int;
}
static void assertEquals(boolean expected, boolean actual) {
if (expected != actual) {
throw new AssertionError("Expected: " + expected + ", actual: " + actual);
}
}
}

View File

@ -0,0 +1,597 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8304487
* @summary Compiler Implementation for Primitive types in patterns, instanceof, and switch (Preview)
* @enablePreview
* @compile PrimitivePatternsSwitch.java
* @run main/othervm PrimitivePatternsSwitch
*/
public class PrimitivePatternsSwitch {
public static void main(String[] args) {
assertEquals(1, primitiveSwitch(42));
assertEquals(2, primitiveSwitch(123));
assertEquals(1, primitiveSwitchUnnamed(42));
assertEquals(2, primitiveSwitchUnnamed(123));
assertEquals(42, primitiveSwitch2());
assertEquals(42, primitiveSwitch3());
assertEquals(1, primitiveSwitch4(0.0f));
assertEquals(2, primitiveSwitch4(1.0f));
assertEquals(1, primitiveSwitchUnconditionallyExact(Byte.MAX_VALUE));
assertEquals(42, exhaustive0());
assertEquals(1, exhaustive1WithDefault());
assertEquals(2, exhaustive2WithDefault());
assertEquals(1, exhaustive1());
assertEquals(1, exhaustive2());
assertEquals(1, exhaustive3());
assertEquals(1, exhaustive4());
assertEquals(2, exhaustive5());
assertEquals(1, exhaustive6());
assertEquals(1, exhaustive7(true));
assertEquals(1, exhaustive7s(true));
assertEquals(1, exhaustive8(true));
assertEquals(1, exhaustive9(true));
assertEquals(1, exhaustive9(false));
assertEquals(1, exhaustiveWithRecords1());
assertEquals(1, exhaustiveWithRecords2());
assertEquals(1, exhaustiveWithRecords4());
assertEquals(1, exhaustiveWithRecords5());
assertEquals(1, exhaustiveWithRecords6());
assertEquals(2, ensureProperSelectionWithRecords());
assertEquals(1, ensureProperSelectionWithRecords2());
assertEquals(3, ensureProperSelectionWithRecords3());
assertEquals(42, switchAndDowncastFromObjectPrimitive());
assertEquals(42, dominationBetweenBoxedAndPrimitive());
assertEquals(2, wideningAndUnboxing());
assertEquals(2, wideningAndUnboxingInRecord());
assertEquals(2, wideningAndInferredUnboxingInRecord());
assertEquals(5f, switchOverBoxedFloat(0f));
assertEquals(7f, switchOverBoxedFloat(1f));
assertEquals(9f, switchOverBoxedFloat(2f));
assertEquals(9f, switchOverBoxedFloat(2f));
assertEquals(5f, switchOverPrimitiveDouble(0d));
assertEquals(7f, switchOverPrimitiveDouble(1d));
assertEquals(9f, switchOverPrimitiveDouble(2d));
assertEquals(1, switchOverPrimitiveChar('a'));
assertEquals(-1, switchOverPrimitiveChar('x'));
assertTrue(switchOverBoxedBooleanWithUnconditional(Boolean.valueOf(true)));
assertTrue(switchOverBoxedBooleanWithUnconditional(true));
assertTrue(!switchOverBoxedBooleanWithUnconditional(false));
assertEquals(1, switchOverPrimitiveBooleanWithDefault(true));
assertEquals(2, switchOverPrimitiveBooleanWithDefault(false));
assertEquals(1, switchOverPrimitiveBoolean(true));
assertEquals(2, switchOverPrimitiveBoolean(false));
assertEquals(1, switchOverPrimitiveFloat(0.0f/0.0f));
assertEquals(2, switchOverPrimitiveFloat((float) Math.pow(0.0f/0.0f, 0)));
assertEquals(3, switchOverPrimitiveFloat(0.0f));
assertEquals(4, switchOverPrimitiveFloat(-0.0f));
assertEquals(1, switchRedirectedExactnessMethods1('a'));
assertEquals(-1, switchRedirectedExactnessMethods1('\u03A9'));
assertEquals(1, switchRedirectedExactnessMethods2('\u03A9'));
assertEquals(-1, switchRedirectedExactnessMethods2('\uFFFF'));
assertEquals(1, switchLongAndUnconditional(32778L));
assertEquals(2, switchLongAndUnconditional(42L));
assertEquals(1, switchByte((byte) 128));
assertEquals(2, switchByte((byte) 42));
assertEquals(1, switchShort((short) 32778));
assertEquals(2, switchShort((short) 42));
assertEquals(1, switchInt(32778));
assertEquals(2, switchInt(42));
assertEquals(1, switchChar( '\u0010'));
assertEquals(2, switchChar('a'));
assertEquals(1, testIntInNonEnhancedSwitchStatement(1));
assertEquals(0, testIntInNonEnhancedSwitchStatement(0));
assertEquals(1, testFloatInEnhancedSwitchStatement(1.0f));
assertEquals(0, testFloatInEnhancedSwitchStatement(0.0f));
assertEquals(1, testDoubleInEnhancedSwitchStatement(1.0d));
assertEquals(0, testDoubleInEnhancedSwitchStatement(0.0d));
assertEquals(1, testLongInEnhancedSwitchStatement(1l));
assertEquals(0, testLongInEnhancedSwitchStatement(0l));
assertEquals(1, testBooleanInEnhancedSwitchStatement(true));
assertEquals(0, testBooleanInEnhancedSwitchStatement(false));
assertEquals(1, testByteWrapperToIntUnconditionallyExact());
assertEquals(1, testIntegerWrapperToFloat());
assertEquals(-1, testIntegerWrapperToFloatInexact());
}
public static int primitiveSwitch(int i) {
return switch (i) {
case int j when j == 42-> 1;
case int j -> 2;
};
}
public static int primitiveSwitchUnnamed(int i) {
return switch (i) {
case int _ when i == 42-> 1;
case int _ -> 2;
};
}
public static int primitiveSwitch2() {
Object o = Integer.valueOf(42);
switch (o) {
case int i: return i;
default: break;
}
return -1;
}
public static int primitiveSwitch3() {
int i = 42;
switch (i) {
case Integer ii: return ii;
}
}
public static int primitiveSwitch4(float f) {
return switch (f) {
case 0.0f -> 1;
case Float fi when fi == 1f -> 2;
case Float fi -> 3;
};
}
public static int primitiveSwitchUnconditionallyExact(byte c) {
return switch (c) {
case short _ -> 1;
};
}
public static int exhaustive0() {
Integer i = 42;
switch (i) {
case int j: return j;
}
}
public static int exhaustive1WithDefault() {
int i = 42;
return switch (i) {
case byte b -> 1;
default -> 2;
};
}
public static int exhaustive2WithDefault() {
int i = 30000;
return switch (i) {
case byte b -> 1;
case short s -> 2;
default -> 3;
};
}
public static int exhaustive1() {
int i = 42;
return switch (i) {
case Integer p -> 1;
};
}
public static int exhaustive2() {
int i = 42;
return switch (i) {
case long d -> 1;
};
}
public static int exhaustive3() {
int i = 42;
return switch (i) {
case double d -> 1;
};
}
public static int exhaustive4() {
int i = 127;
return switch (i) {
case byte b -> 1;
case double d -> 2;
};
}
public static int exhaustive5() {
int i = 127 + 1;
return switch (i) {
case byte b -> 1;
case double d -> 2;
};
}
public static int exhaustive6() {
Integer i = Integer.valueOf(42);
return switch (i) {
case int p -> 1;
};
}
public static int exhaustive7(Boolean b) {
switch (b) {
case true: return 1;
case false: return 2; // with reminder, null, OK
}
}
public static int exhaustive7s(Boolean b) {
return switch (b) {
case true -> 1;
case false -> 2; // with reminder, null, OK
};
}
public static int exhaustive8(Boolean b) {
switch (b) {
case boolean bb: return 1;
}
}
public static int exhaustive9(boolean b) {
switch (b) {
case Boolean bb: return 1;
}
}
public static int exhaustiveWithRecords1() {
R_int r = new R_int(42);
return switch (r) {
// exhaustive, because Integer exhaustive at type int
case R_int(Integer x) -> 1;
};
}
public static int exhaustiveWithRecords2() {
R_int r = new R_int(42);
return switch (r) {
// exhaustive, because double unconditional at int
case R_int(double x) -> 1;
};
}
public static int exhaustiveWithRecords4() {
R_Integer r = new R_Integer(42);
return switch (r) {
// exhaustive, because R_Integer(int) exhaustive at type R_Integer(Integer), because int exhaustive at type Integer
case R_Integer(int x) -> 1;
};
}
public static int exhaustiveWithRecords5() {
R_Integer r = new R_Integer(42);
return switch (r) {
// exhaustive, because double exhaustive at Integer
case R_Integer(double x) -> 1;
};
}
public static int exhaustiveWithRecords6() {
R_int r = new R_int(42);
return switch (r) {
case R_int(byte x) -> 1;
case R_int(int x) -> 2;
};
}
public static int ensureProperSelectionWithRecords() {
R_int r = new R_int(4242);
return switch (r) {
case R_int(byte x) -> 1;
case R_int(int x) -> 2;
};
}
public static int ensureProperSelectionWithRecords2() {
R_double r = new R_double(42);
switch (r) {
case R_double(int i):
return meth_int(i);
case R_double(double x):
return meth_double(x);
}
}
public static int ensureProperSelectionWithRecords3() {
R_int r = new R_int(4242);
return switch (r) {
case R_int(byte x) -> 1;
case R_int(int x) when x == 236 -> 2;
case R_int(int x) -> 3;
};
}
public static int meth_int(int i) { return 1; }
public static int meth_double(double d) { return 2;}
public static int switchAndDowncastFromObjectPrimitive() {
Object i = 42;
return switch (i) {
case Integer ib -> ib;
default -> -1;
};
}
public static int dominationBetweenBoxedAndPrimitive() {
Object i = 42;
return switch (i) {
case Integer ib -> ib;
case byte ip -> ip;
default -> -1;
};
}
static int wideningAndUnboxing() {
Number o = Integer.valueOf(42);
return switch (o) {
case byte b -> 1;
case int i -> 2;
case float f -> 3;
default -> 4;
};
}
static int wideningAndUnboxingInRecord() {
Box<Number> box = new Box<>(Integer.valueOf(42));
return switch (box) {
case Box<Number>(byte b) -> 1;
case Box<Number>(int i) -> 2;
case Box<Number>(float f) -> 3;
default -> 4;
};
}
static int wideningAndInferredUnboxingInRecord() {
Box<Number> box = new Box<>(Integer.valueOf(42));
return switch (box) {
case Box(byte b) -> 1;
case Box(int i) -> 2;
case Box(float f) -> 3;
default -> 4;
};
}
public static float switchOverBoxedFloat(Float f) {
return switch (f) {
case 0f -> 5f + 0f;
case Float fi when fi == 1f -> 6f + fi;
case Float fi -> 7f + fi;
};
}
public static double switchOverPrimitiveDouble(Double d) {
return switch (d) {
case 0d -> 5d + 0d;
case Double di when di == 1d -> 6d + di;
case Double di -> 7d + di;
};
}
public static boolean switchOverBoxedBooleanWithUnconditional(Boolean b) {
return switch (b) {
case true -> true;
case Boolean bi -> bi;
};
}
public static int switchOverPrimitiveBooleanWithDefault(boolean b) {
return switch (b) {
case true -> 1;
default -> 2;
};
}
public static int switchOverPrimitiveBoolean(boolean b) {
return switch (b) {
case true -> 1;
case false -> 2;
};
}
public static int switchOverPrimitiveChar(char c) {
return switch (c) {
case 'a' -> 1;
default -> -1;
};
}
public static final float NaNconstant = Float.NaN;
public static int switchOverPrimitiveFloat(float f) {
return switch (f) {
case NaNconstant -> 1;
case 1.0f -> 2;
case 0.0f -> 3;
case -0.0f -> 4;
default -> -1;
};
}
// tests that Exactness.char_byte is properly redirected to int_byte
public static int switchRedirectedExactnessMethods1(char c) {
return switch (c) {
case byte _ -> 1;
default -> -1;
};
}
// tests that Exactness.char_short is properly redirected to int_short
public static int switchRedirectedExactnessMethods2(char c) {
return switch (c) {
case short _ -> 1;
default -> -1;
};
}
// tests that Exactness.short_byte is properly redirected to int_byte
public static int switchRedirectedExactnessMethods2(short c) {
return switch (c) {
case byte _ -> 1;
default -> -1;
};
}
public static int switchLongAndUnconditional(long l) {
return switch (l) {
case 32778L -> 1;
case long c -> 2;
};
}
public static int switchByte(byte b) {
return switch (b) {
case (byte)128 -> 1;
case byte c -> 2;
};
}
public static int switchShort(short s) {
return switch (s) {
case (short)32778 -> 1;
case short c -> 2;
};
}
public static int switchInt(int i) {
return switch (i) {
case 32778 -> 1;
case int c -> 2;
};
}
public static int switchChar(char c) {
return switch (c) {
case '\u0010' -> 1;
case char cc -> 2;
};
}
public static int testIntInNonEnhancedSwitchStatement(int v1) {
int i = 0;
switch (v1) {
case 1:
i = 1;
break;
}
return i;
}
public static int testFloatInEnhancedSwitchStatement(float v1) {
int i = 0;
switch (v1) {
case 1.0f:
i = 1;
break;
default:
i = 0;
}
return i;
}
public static int testDoubleInEnhancedSwitchStatement(double v1) {
int i = 0;
switch (v1) {
case 1d:
i = 1;
break;
default:
i = 0;
}
return i;
}
public static int testLongInEnhancedSwitchStatement(long v1) {
int i = 0;
switch (v1) {
case 1l:
i = 1;
break;
default:
i = 0;
}
return i;
}
public static int testBooleanInEnhancedSwitchStatement(boolean v1) {
int i = 0;
switch (v1) {
case true:
i = 1;
break;
default:
i = 0;
}
return i;
}
public static int testByteWrapperToIntUnconditionallyExact() {
Byte b = Byte.valueOf((byte) 42);
return switch (b) {
case int p -> 1;
};
}
public static int testIntegerWrapperToFloat() {
Integer i = Integer.valueOf(42);
return switch (i) {
case float p -> 1;
default -> -1;
};
}
public static int testIntegerWrapperToFloatInexact() {
Integer i = Integer.valueOf(Integer.MAX_VALUE);
return switch (i) {
case float p -> 1;
default -> -1;
};
}
record R_Integer(Integer x) {}
record R_int(int x) {}
record R_double(double x) {}
record Box<N extends Number>(N num) {}
static void assertEquals(int expected, int actual) {
if (expected != actual) {
throw new AssertionError("Expected: " + expected + ", actual: " + actual);
}
}
static void assertEquals(float expected, float actual) {
if (Float.compare(expected, actual) != 0) {
throw new AssertionError("Expected: " + expected + ", but got: " + actual);
}
}
static void assertEquals(double expected, double actual) {
if (Double.compare(expected, actual) != 0) {
throw new AssertionError("Expected: " + expected + ", but got: " + actual);
}
}
static void assertTrue(boolean actual) {
if (!actual) {
throw new AssertionError("Expected: true, but got false");
}
}
}

View File

@ -0,0 +1,220 @@
/*
* @test /nodynamiccopyright/
* @bug 8304487
* @summary Compiler Implementation for Primitive types in patterns, instanceof, and switch (Preview)
* @enablePreview
* @compile/fail/ref=PrimitivePatternsSwitchErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW PrimitivePatternsSwitchErrors.java
*/
public class PrimitivePatternsSwitchErrors {
record R_int(int x) {}
public static void dominationBetweenPrimitivePatterns() {
int i = 42;
switch (i) {
case short s -> System.out.println("its a short");
case byte b -> System.out.println("its a byte"); // Error - dominated!
default -> System.out.println("any other integral value");
}
}
public static int dominationWithRecordPatterns() {
R_int r = new R_int(42);
return switch (r) {
case R_int(int x) -> 1;
case R_int(byte x) -> 2; // Error - dominated!
};
}
public static int inconvertibleNestedComponent() {
R_int r = new R_int(42);
return switch (r) {
case R_int(Long x) -> 1; // inconvertible
};
}
public static int nonExhaustive1() {
int i = 42;
return switch (i) { // Error - not exhaustive
case short s -> s;
};
}
public static int nonExhaustive2() {
int i = 42;
return switch (i) { // Error - not exhaustive
case byte b -> 1;
case short s -> 2;
};
}
public static int nonExhaustive3() {
int i = 42;
return switch (i) { // Error - not exhaustive
case byte b -> 1;
case float f -> 2;
};
}
public static int dominationBetweenBoxedAndPrimitive() {
int i = 42;
return switch (i) {
case Integer ib -> ib;
case byte ip -> ip; // Error - dominated!
};
}
public static int constantDominatedWithPrimitivePattern() {
int i = 42;
return switch (i) {
case int j -> 42;
case 43 -> -1; // Error - dominated!
};
}
public static int constantDominatedWithFloatPrimitivePattern() {
float f = 42.0f;
return switch (f) {
case Float ff -> 42;
case 43.0f -> -1; // Error - dominated!
};
}
void switchLongOverByte(byte b) {
switch (b) {
case 0L: return ;
}
}
void switchOverPrimitiveFloatFromInt(float f) {
switch (f) {
case 16777216:
break;
case 16777217:
break;
default:
break;
}
}
void switchOverNotRepresentableFloat(Float f) {
switch (f) {
case 1.0f:
break;
case 0.999999999f:
break;
case Float fi:
break;
}
}
int switchOverPrimitiveBooleanExhaustiveWithNonPermittedDefault(boolean b) {
return switch (b) {
case true -> 1;
case false -> 2;
default -> 3;
};
}
int switchOverPrimitiveBooleanExhaustiveWithNonPermittedDefaultStatement(boolean b) {
switch (b) {
case true: return 1;
case false: return 2;
default: return 3;
}
}
int switchOverPrimitiveBooleanExhaustiveWithNonPermittedUnconditionalStatement(boolean b) {
switch (b) {
case true: return 1;
case false: return 2;
case boolean bb: return 3; // error
}
}
void switchCombinationsNonIntegral() {
float f = 0f;
long l = 0L;
double d = 0d;
Float fB = 0F;
Long lB = 0L;
Double dB = 0D;
switch (f) {
case 1l: return;
case 2f: return;
case 3d: return;
default:
}
switch (l) {
case 1l: return;
case 2f: return;
case 3d: return;
default:
}
switch (d) {
case 1l: return;
case 2f: return;
case 3d: return;
default:
}
switch (fB) {
case 1l: return;
case 2f: return;
case 3d: return;
default:
}
switch (lB) {
case 1l: return;
case 2f: return;
case 3d: return;
default:
}
switch (dB) {
case 1l: return;
case 2f: return;
case 3d: return;
default:
}
}
int switchOverPrimitiveBooleanExhaustiveWithNonPermittedUnconditional(boolean b) {
return switch (b) {
case true -> 1;
case false -> 2;
case boolean bb -> 3; // error
};
}
int duplicateUnconditionalWithPrimitives(int i) {
return switch (i) {
case int ii -> 1;
case long l -> 2; // error
};
}
int booleanSingleCase1(boolean b) {
return switch (b) {
case true -> 1;
};
}
int booleanSingleCase2(boolean b) {
switch (b) {
case true: return 1;
}
}
void nullAndPrimitive() {
int i = 42;
switch (i) {
case short s -> System.out.println("its a short");
case null -> System.out.println("oops");
default -> System.out.println("any other integral value");
}
}
}

View File

@ -0,0 +1,37 @@
PrimitivePatternsSwitchErrors.java:15:18: compiler.err.pattern.dominated
PrimitivePatternsSwitchErrors.java:24:18: compiler.err.pattern.dominated
PrimitivePatternsSwitchErrors.java:31:24: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.Long)
PrimitivePatternsSwitchErrors.java:62:18: compiler.err.pattern.dominated
PrimitivePatternsSwitchErrors.java:70:18: compiler.err.pattern.dominated
PrimitivePatternsSwitchErrors.java:78:18: compiler.err.pattern.dominated
PrimitivePatternsSwitchErrors.java:84:18: compiler.err.prob.found.req: (compiler.misc.possible.loss.of.precision: long, byte)
PrimitivePatternsSwitchErrors.java:90:18: compiler.err.constant.label.not.compatible: int, float
PrimitivePatternsSwitchErrors.java:92:18: compiler.err.constant.label.not.compatible: int, float
PrimitivePatternsSwitchErrors.java:103:13: compiler.err.duplicate.case.label
PrimitivePatternsSwitchErrors.java:114:13: compiler.err.default.and.both.boolean.values
PrimitivePatternsSwitchErrors.java:122:13: compiler.err.default.and.both.boolean.values
PrimitivePatternsSwitchErrors.java:130:18: compiler.err.unconditional.pattern.and.both.boolean.values
PrimitivePatternsSwitchErrors.java:143:18: compiler.err.constant.label.not.compatible: long, float
PrimitivePatternsSwitchErrors.java:145:18: compiler.err.prob.found.req: (compiler.misc.possible.loss.of.precision: double, float)
PrimitivePatternsSwitchErrors.java:151:18: compiler.err.prob.found.req: (compiler.misc.possible.loss.of.precision: float, long)
PrimitivePatternsSwitchErrors.java:152:18: compiler.err.prob.found.req: (compiler.misc.possible.loss.of.precision: double, long)
PrimitivePatternsSwitchErrors.java:157:18: compiler.err.constant.label.not.compatible: long, double
PrimitivePatternsSwitchErrors.java:158:18: compiler.err.constant.label.not.compatible: float, double
PrimitivePatternsSwitchErrors.java:164:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: long, java.lang.Float)
PrimitivePatternsSwitchErrors.java:166:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: double, java.lang.Float)
PrimitivePatternsSwitchErrors.java:172:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: float, java.lang.Long)
PrimitivePatternsSwitchErrors.java:173:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: double, java.lang.Long)
PrimitivePatternsSwitchErrors.java:178:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: long, java.lang.Double)
PrimitivePatternsSwitchErrors.java:179:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: float, java.lang.Double)
PrimitivePatternsSwitchErrors.java:189:18: compiler.err.unconditional.pattern.and.both.boolean.values
PrimitivePatternsSwitchErrors.java:196:18: compiler.err.duplicate.unconditional.pattern
PrimitivePatternsSwitchErrors.java:216:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: compiler.misc.type.null, int)
PrimitivePatternsSwitchErrors.java:30:16: compiler.err.not.exhaustive
PrimitivePatternsSwitchErrors.java:37:16: compiler.err.not.exhaustive
PrimitivePatternsSwitchErrors.java:44:16: compiler.err.not.exhaustive
PrimitivePatternsSwitchErrors.java:52:16: compiler.err.not.exhaustive
PrimitivePatternsSwitchErrors.java:201:16: compiler.err.not.exhaustive
PrimitivePatternsSwitchErrors.java:207:9: compiler.err.not.exhaustive.statement
- compiler.note.preview.filename: PrimitivePatternsSwitchErrors.java, DEFAULT
- compiler.note.preview.recompile
34 errors

View File

@ -128,7 +128,210 @@ public class SourceLevelChecks extends TestRunner {
"1 error");
}
@Test
public void testNestedPrimitive1(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(R r) {
if (r instanceof R(byte b)) {
}
}
record R(int i) {}
}
""",
21,
"Test.java:4:28: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.primitive.patterns)",
"1 error");
}
@Test
public void testNestedPrimitive2(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(R r) {
switch (r) {
case R(byte b) -> {}
default -> {}
}
}
record R(int i) {}
}
""",
21,
"Test.java:5:20: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.primitive.patterns)",
"1 error");
}
@Test
public void testNestedPrimitive3(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(R r) {
if (r instanceof R(long l)) {
}
}
record R(int i) {}
}
""",
21,
"Test.java:4:28: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.primitive.patterns)",
"1 error");
}
@Test
public void testNestedPrimitive4(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(R r) {
switch (r) {
case R(long l) -> {}
default -> {}
}
}
record R(int i) {}
}
""",
21,
"Test.java:5:20: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.primitive.patterns)",
"1 error");
}
@Test
public void testPrimitive2Reference1(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(int i) {
if (i instanceof Integer j) {}
}
}
""",
21,
"Test.java:4:13: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.primitive.patterns)",
"1 error");
}
@Test
public void testPrimitive2Reference2(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(int i) {
switch (i) {
case Integer j -> {}
}
}
}
""",
21,
"Test.java:5:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.primitive.patterns)",
"1 error");
}
@Test
public void testReference2Primitive1(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(Integer i) {
if (i instanceof int j) {}
}
}
""",
21,
"Test.java:4:26: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.primitive.patterns)",
"1 error");
}
@Test
public void testReference2Primitive2(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(Integer i) {
switch (i) {
case int j -> {}
}
}
}
""",
21,
"Test.java:5:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.primitive.patterns)",
"1 error");
}
@Test
public void testPrimitivePatternObject1(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(Object o) {
if (o instanceof int j) {}
}
}
""",
21,
"Test.java:4:26: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.primitive.patterns)",
"1 error");
}
@Test
public void testPrimitivePatternObject2(Path base) throws Exception {
doTest(base,
"""
package test;
public class Test {
private void test(Object o) {
switch (o) {
case int j -> {}
}
}
record R(int i) {}
}
""",
21,
"Test.java:5:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.primitive.patterns)",
"1 error");
}
@Test
public void testOtherPrimitives(Path base) throws Exception {
for (String type : new String[] {"boolean", "long", "float", "double"}) {
doTest(base,
"""
package test;
public class Test {
private void test($type v) {
switch (v) {
default -> {}
}
}
}
""".replace("$type", type),
21,
"Test.java:4:16: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.primitive.patterns)",
"1 error");
}
}
private void doTest(Path base, String testCode, String... expectedErrors) throws IOException {
doTest(base, testCode, 17, expectedErrors);
}
private void doTest(Path base, String testCode, int sourceLevel, String... expectedErrors) throws IOException {
Path current = base.resolve(".");
Path src = current.resolve("src");
Path classes = current.resolve("classes");
@ -138,7 +341,7 @@ public class SourceLevelChecks extends TestRunner {
var log =
new JavacTask(tb)
.options("--release", "17",
.options("--release", "" + sourceLevel,
"-XDrawDiagnostics")
.outdir(classes)
.files(tb.findJavaFiles(src))

View File

@ -127,12 +127,6 @@ public class SwitchErrors {
default: break;
}
}
void primitivePattern(Object o) {
switch (o) {
case int i: break;
default: break;
}
}
void patternAndDefault1(Object o) {
switch (o) {
case String s, default: break;
@ -239,16 +233,6 @@ public class SwitchErrors {
case CharSequence cs: break;
}
}
void primitiveToReference(int i) {
switch (i) {
case Integer j: break;
}
}
void referenceToPrimitive(Integer i) {
switch (i) {
case int j: break;
}
}
void nullAndParenthesized1(Object o) {
record R(Object o) {}
switch (o) {

View File

@ -1,18 +1,18 @@
SwitchErrors.java:66:18: compiler.err.default.label.not.allowed
SwitchErrors.java:72:18: compiler.err.default.label.not.allowed
SwitchErrors.java:72:27: compiler.err.default.label.not.allowed
SwitchErrors.java:138:28: compiler.err.default.label.not.allowed
SwitchErrors.java:144:18: compiler.err.default.label.not.allowed
SwitchErrors.java:149:18: compiler.err.default.label.not.allowed
SwitchErrors.java:154:18: compiler.err.default.label.not.allowed
SwitchErrors.java:213:29: compiler.err.default.label.not.allowed
SwitchErrors.java:220:32: compiler.err.default.label.not.allowed
SwitchErrors.java:227:32: compiler.err.default.label.not.allowed
SwitchErrors.java:283:20: compiler.err.illegal.start.of.type
SwitchErrors.java:286:28: compiler.err.illegal.start.of.type
SwitchErrors.java:132:28: compiler.err.default.label.not.allowed
SwitchErrors.java:138:18: compiler.err.default.label.not.allowed
SwitchErrors.java:143:18: compiler.err.default.label.not.allowed
SwitchErrors.java:148:18: compiler.err.default.label.not.allowed
SwitchErrors.java:207:29: compiler.err.default.label.not.allowed
SwitchErrors.java:214:32: compiler.err.default.label.not.allowed
SwitchErrors.java:221:32: compiler.err.default.label.not.allowed
SwitchErrors.java:267:20: compiler.err.illegal.start.of.type
SwitchErrors.java:270:28: compiler.err.illegal.start.of.type
SwitchErrors.java:290:42: compiler.err.expected2: :, ->
SwitchErrors.java:299:45: compiler.err.expected2: :, ->
SwitchErrors.java:306:42: compiler.err.expected2: :, ->
SwitchErrors.java:315:45: compiler.err.expected2: :, ->
SwitchErrors.java:322:42: compiler.err.expected2: :, ->
SwitchErrors.java:11:18: compiler.err.constant.label.not.compatible: java.lang.String, java.lang.Object
SwitchErrors.java:17:18: compiler.err.constant.label.not.compatible: int, java.lang.Object
SwitchErrors.java:23:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
@ -36,30 +36,27 @@ SwitchErrors.java:108:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:113:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
SwitchErrors.java:119:18: compiler.err.instanceof.reifiable.not.safe: java.util.List, java.util.List<java.lang.Integer>
SwitchErrors.java:125:18: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:132:18: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array)
SwitchErrors.java:143:18: compiler.err.flows.through.from.pattern
SwitchErrors.java:149:27: compiler.err.flows.through.from.pattern
SwitchErrors.java:155:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:167:18: compiler.err.pattern.expected
SwitchErrors.java:173:78: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:179:73: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:186:21: compiler.err.invalid.case.label.combination
SwitchErrors.java:197:13: compiler.err.unconditional.pattern.and.default
SwitchErrors.java:195:29: compiler.err.flows.through.from.pattern
SwitchErrors.java:204:24: compiler.err.invalid.case.label.combination
SwitchErrors.java:220:21: compiler.err.invalid.case.label.combination
SwitchErrors.java:227:29: compiler.err.flows.through.from.pattern
SwitchErrors.java:239:18: compiler.err.duplicate.unconditional.pattern
SwitchErrors.java:244:18: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, java.lang.Integer)
SwitchErrors.java:249:18: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array)
SwitchErrors.java:255:24: compiler.err.invalid.case.label.combination
SwitchErrors.java:262:24: compiler.err.invalid.case.label.combination
SwitchErrors.java:269:18: compiler.err.flows.through.from.pattern
SwitchErrors.java:276:18: compiler.err.flows.through.from.pattern
SwitchErrors.java:292:49: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location: kindname.class, java.lang.Object, null)
SwitchErrors.java:294:55: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location: kindname.class, java.lang.Object, null)
SwitchErrors.java:300:26: compiler.err.pattern.type.cannot.infer
SwitchErrors.java:315:21: compiler.err.invalid.case.label.combination
SwitchErrors.java:137:18: compiler.err.flows.through.from.pattern
SwitchErrors.java:143:27: compiler.err.flows.through.from.pattern
SwitchErrors.java:149:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:161:18: compiler.err.pattern.expected
SwitchErrors.java:167:78: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:173:73: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:180:21: compiler.err.invalid.case.label.combination
SwitchErrors.java:191:13: compiler.err.unconditional.pattern.and.default
SwitchErrors.java:189:29: compiler.err.flows.through.from.pattern
SwitchErrors.java:198:24: compiler.err.invalid.case.label.combination
SwitchErrors.java:214:21: compiler.err.invalid.case.label.combination
SwitchErrors.java:221:29: compiler.err.flows.through.from.pattern
SwitchErrors.java:233:18: compiler.err.duplicate.unconditional.pattern
SwitchErrors.java:239:24: compiler.err.invalid.case.label.combination
SwitchErrors.java:246:24: compiler.err.invalid.case.label.combination
SwitchErrors.java:253:18: compiler.err.flows.through.from.pattern
SwitchErrors.java:260:18: compiler.err.flows.through.from.pattern
SwitchErrors.java:276:49: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location: kindname.class, java.lang.Object, null)
SwitchErrors.java:278:55: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location: kindname.class, java.lang.Object, null)
SwitchErrors.java:284:26: compiler.err.pattern.type.cannot.infer
SwitchErrors.java:299:21: compiler.err.invalid.case.label.combination
SwitchErrors.java:10:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:16:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:22:9: compiler.err.not.exhaustive.statement
@ -70,6 +67,6 @@ SwitchErrors.java:87:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:92:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:98:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:105:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:159:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:232:9: compiler.err.not.exhaustive.statement
74 errors
SwitchErrors.java:153:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:226:9: compiler.err.not.exhaustive.statement
71 errors

View File

@ -1,34 +0,0 @@
/*
* @test /nodynamiccopyright/
* @bug 8206986
* @summary Verify switch over boolean/long/float/double is not allowed.
* @compile/fail/ref=SwitchNoExtraTypes.out -XDrawDiagnostics SwitchNoExtraTypes.java
*/
public class SwitchNoExtraTypes {
private void switchBoolean(boolean b) {
switch (b) {
case true: return ;
}
}
private void switchLong(long l) {
switch (l) {
case 0: return ;
}
}
private void switchFloat(float f) {
switch (f) {
case 0: return ;
}
}
private void switchDouble(double d) {
switch (d) {
case 0: return ;
}
}
}

View File

@ -1,5 +0,0 @@
SwitchNoExtraTypes.java:11:16: compiler.err.selector.type.not.allowed: boolean
SwitchNoExtraTypes.java:17:16: compiler.err.selector.type.not.allowed: long
SwitchNoExtraTypes.java:23:16: compiler.err.selector.type.not.allowed: float
SwitchNoExtraTypes.java:29:16: compiler.err.selector.type.not.allowed: double
4 errors

View File

@ -0,0 +1,145 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.preview.patterns;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
/**
* Tests Exactness methods
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations=5, time=1)
@Measurement(iterations=5, time=1)
@Threads(2)
@Fork(value = 1,
jvmArgsPrepend = {"-Djmh.blackhole.mode=COMPILER",
"--enable-preview"})
@State(Scope.Thread)
@SuppressWarnings("preview")
public class Exactness {
private static boolean int_float_based_on_leading_trailing(int n) {
if (n == Integer.MIN_VALUE)
return true;
n = Math.abs(n);
return Float.PRECISION >= // 24
(32 - (Integer.numberOfLeadingZeros(n) +
Integer.numberOfTrailingZeros(n))) ;
}
@Benchmark
public void test_int_float_based_on_leading_trailing(Blackhole bh) {
for(int n = Integer.MIN_VALUE; n < Integer.MAX_VALUE; n++) {
bh.consume(int_float_based_on_leading_trailing(n));
}
}
private static boolean int_float_based_on_filtering(int n) {
return n == (int)(float)n && n != Integer.MAX_VALUE;
}
@Benchmark
public void test_int_float_based_on_filtering(Blackhole bh) {
for(int n = Integer.MIN_VALUE; n < Integer.MAX_VALUE; n++) {
bh.consume(int_float_based_on_filtering(n));
}
}
private static boolean long_float_based_on_leading_trailing(long n) {
if (n == Long.MIN_VALUE)
return true;
n = Math.abs(n);
return Float.PRECISION >= // 24
(64 - (Long.numberOfLeadingZeros(n) +
Long.numberOfTrailingZeros(n))) ;
}
@Benchmark
public void test_long_float_based_on_leading_trailing(Blackhole bh) {
for(int n = Integer.MIN_VALUE; n < Integer.MAX_VALUE; n++) {
bh.consume(long_float_based_on_leading_trailing(n));
}
}
private static boolean long_float_based_on_filtering(long n) {
return n == (long)(float)n && n != Long.MAX_VALUE;
}
@Benchmark
public void test_long_float_based_on_filtering(Blackhole bh) {
for(int n = Integer.MIN_VALUE; n < Integer.MAX_VALUE; n++) {
bh.consume(long_float_based_on_filtering(n));
}
}
private static boolean long_double_based_on_leading_trailing(long n) {
if (n == Long.MIN_VALUE)
return true;
n = Math.abs(n);
return Double.PRECISION >= // 53
(64 - (Long.numberOfLeadingZeros(n) +
Long.numberOfTrailingZeros(n))) ;
}
@Benchmark
public void test_long_double_based_on_leading_trailing(Blackhole bh) {
for(int n = Integer.MIN_VALUE; n < Integer.MAX_VALUE; n++) {
bh.consume(long_double_based_on_leading_trailing(n));
}
}
private static boolean long_double_based_on_filtering(long n) {
return n == (long)(double)n && n != Long.MAX_VALUE;
}
@Benchmark
public void test_long_double_based_on_filtering(Blackhole bh) {
for(int n = Integer.MIN_VALUE; n < Integer.MAX_VALUE; n++) {
bh.consume(long_double_based_on_filtering(n));
}
}
private static boolean float_int_based_on_compare(float n) {
return Double.compare((double)n, (double)((int)n)) == 0;
}
@Benchmark
public void test_float_int_based_on_compare(Blackhole bh) {
float n = -Float.MAX_VALUE;
while (n <= Float.MAX_VALUE) {
bh.consume(float_int_based_on_compare(n));
n = Math.nextUp(n);
}
}
private static boolean isNegativeZero(float n) {
return Float.floatToRawIntBits(n) == Integer.MIN_VALUE;
}
private static boolean float_int_based_on_filtering(float n) {
return n == (float)(int)n && n != 0x1p31f && !isNegativeZero(n);
}
@Benchmark
public void test_float_int_based_on_filtering(Blackhole bh) {
float n = -Float.MAX_VALUE;
while (n <= Float.MAX_VALUE) {
bh.consume(float_int_based_on_filtering(n));
n = Math.nextUp(n);
}
}
}