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:
parent
66971600f7
commit
1733d2ea24
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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),
|
||||
;
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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() {
|
||||
|
@ -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">
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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 ||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
236
test/jdk/java/lang/runtime/ExactnessConversionsSupportTest.java
Normal file
236
test/jdk/java/lang/runtime/ExactnessConversionsSupportTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
56
test/langtools/jdk/jshell/PrimitiveInstanceOfTest.java
Normal file
56
test/langtools/jdk/jshell/PrimitiveInstanceOfTest.java
Normal 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"));
|
||||
}
|
||||
}
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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> {}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
597
test/langtools/tools/javac/patterns/PrimitivePatternsSwitch.java
Normal file
597
test/langtools/tools/javac/patterns/PrimitivePatternsSwitch.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
@ -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))
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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 ;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
145
test/micro/org/openjdk/bench/jdk/preview/patterns/Exactness.java
Normal file
145
test/micro/org/openjdk/bench/jdk/preview/patterns/Exactness.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user