8300543: Compiler Implementation for Pattern Matching for switch
8300545: Compiler Implementation for Record Patterns Co-authored-by: Aggelos Biboudis <abimpoudis@openjdk.org> Reviewed-by: vromero, mcimadamore
This commit is contained in:
parent
5ccc962942
commit
eaa80ad08c
@ -25,48 +25,57 @@
|
|||||||
|
|
||||||
package java.lang;
|
package java.lang;
|
||||||
|
|
||||||
import jdk.internal.javac.PreviewFeature;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown to indicate an unexpected failure in pattern matching.
|
* Thrown to indicate an unexpected failure in pattern matching.
|
||||||
*
|
*
|
||||||
* <p>{@code MatchException} may be thrown when an exhaustive pattern matching language construct
|
* <p>{@code MatchException} may be thrown when an exhaustive pattern matching
|
||||||
* (such as a switch expression) encounters a value that does not match any of the provided
|
* language construct (such as a {@code switch} expression) encounters a value
|
||||||
* patterns at runtime. This can arise from a number of cases:
|
* that does not match any of the specified patterns at run time, even though
|
||||||
|
* the construct has been deemed exhaustive. This is intentional and can arise
|
||||||
|
* from a number of cases:
|
||||||
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Separate compilation anomalies, where a sealed interface has a different set of permitted
|
* <li>Separate compilation anomalies, where parts of the type hierarchy that
|
||||||
* subtypes at runtime than it had at compilation time, an enum has a different set of
|
* the patterns reference have been changed, but the pattern matching
|
||||||
* constants at runtime than it had at compilation time, or the type hierarchy has changed
|
* construct has not been recompiled. For example, if a sealed interface
|
||||||
* in incompatible ways between compile time and run time.</li>
|
* has a different set of permitted subtypes at run time than it had at
|
||||||
* <li>{@code null} values and nested patterns using sealed types. If an interface or abstract
|
* compile time, or if an enum class has a different set of enum constants
|
||||||
* class {@code C} is sealed to permit {@code A} and {@code B}, then the set of record
|
* at runtime than it had at compile time, or if the type hierarchy has
|
||||||
* patterns {@code R(A a)} and {@code R(B b)} are exhaustive on a record {@code R} whose
|
* been changed in some incompatible way between compile time and run time.</li>
|
||||||
* sole component is of type {@code C}, but neither of these patterns will match
|
*
|
||||||
* {@code new R(null)}.</li>
|
* <li>{@code null} values and nested patterns involving sealed classes. If,
|
||||||
* <li>Null targets and nested record patterns. Given a record type {@code R} whose sole
|
* for example, an interface {@code I} is {@code sealed} with two permitted
|
||||||
* component is {@code S}, which in turn is a record whose sole component is {@code String},
|
* subclasses {@code A} and {@code B}, and a record class {@code R} has a
|
||||||
* then the nested record pattern {@code R(S(String s))} will not match {@code new R(null)}.</li>
|
* single component of type {@code I}, then the two record patterns {@code
|
||||||
|
* R(A a)} and {@code R(B b)} together are considered to be exhaustive for
|
||||||
|
* the type {@code R}, but neither of these patterns will match against the
|
||||||
|
* result of {@code new R(null)}.</li>
|
||||||
|
*
|
||||||
|
* <li>{@code null} values and nested record patterns. Given a record class
|
||||||
|
* {@code S} with a single component of type {@code T}, where {@code T} is
|
||||||
|
* another record class with a single component of type {@code String},
|
||||||
|
* then the nested record pattern {@code R(S(var s))} is considered
|
||||||
|
* exhaustive for the type {@code R} but it does not match against the
|
||||||
|
* result of {@code new R(null)} (whereas it does match against the result
|
||||||
|
* of {@code new R(new S(null))} does).</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>Match failures arising from unexpected inputs will generally throw {@code MatchException} only
|
* <p>{@code MatchException} may also be thrown by the process of pattern matching
|
||||||
* after all patterns have been tried; even if {@code R(S(String s))} does not match
|
* a value against a pattern. For example, pattern matching involving a record
|
||||||
* {@code new R(null)}, a later pattern (such as {@code R r}) may still match the target.
|
* pattern may require accessor methods to be implicitly invoked in order to
|
||||||
|
* extract the component values. If any of these accessor methods throws an
|
||||||
|
* exception, pattern matching completes abruptly and throws {@code
|
||||||
|
* MatchException}. The original exception will be set as a {@link
|
||||||
|
* Throwable#getCause() cause} of the {@code MatchException}. No {@link
|
||||||
|
* Throwable#addSuppressed(java.lang.Throwable) suppressed} exceptions will be
|
||||||
|
* recorded.
|
||||||
*
|
*
|
||||||
* <p>MatchException may also be thrown when operations performed as part of pattern matching throw
|
* @jls 14.11.3 Execution of a {@code switch} Statement
|
||||||
* an unexpected exception. For example, pattern matching may cause methods such as record component
|
|
||||||
* accessors to be implicitly invoked in order to extract pattern bindings. If these methods throw
|
|
||||||
* an exception, execution of the pattern matching construct may fail with {@code MatchException}.
|
|
||||||
* The original exception will be set as a {@link Throwable#getCause() cause} of
|
|
||||||
* the {@code MatchException}. No {@link Throwable#addSuppressed(java.lang.Throwable) suppressed}
|
|
||||||
* exceptions will be recorded.
|
|
||||||
*
|
|
||||||
* @jls 14.11.3 Execution of a switch Statement
|
|
||||||
* @jls 14.30.2 Pattern Matching
|
* @jls 14.30.2 Pattern Matching
|
||||||
* @jls 15.28.2 Run-Time Evaluation of switch Expressions
|
* @jls 15.28.2 Run-Time Evaluation of {@code switch} Expressions
|
||||||
*
|
*
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING)
|
|
||||||
public final class MatchException extends RuntimeException {
|
public final class MatchException extends RuntimeException {
|
||||||
@java.io.Serial
|
@java.io.Serial
|
||||||
private static final long serialVersionUID = 0L;
|
private static final long serialVersionUID = 0L;
|
||||||
|
@ -25,17 +25,18 @@
|
|||||||
|
|
||||||
package java.lang.runtime;
|
package java.lang.runtime;
|
||||||
|
|
||||||
|
import java.lang.Enum.EnumDesc;
|
||||||
import java.lang.invoke.CallSite;
|
import java.lang.invoke.CallSite;
|
||||||
import java.lang.invoke.ConstantBootstraps;
|
import java.lang.invoke.ConstantBootstraps;
|
||||||
import java.lang.invoke.ConstantCallSite;
|
import java.lang.invoke.ConstantCallSite;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import jdk.internal.javac.PreviewFeature;
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import jdk.internal.vm.annotation.Stable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bootstrap methods for linking {@code invokedynamic} call sites that implement
|
* Bootstrap methods for linking {@code invokedynamic} call sites that implement
|
||||||
@ -43,9 +44,8 @@ import static java.util.Objects.requireNonNull;
|
|||||||
* take additional static arguments corresponding to the {@code case} labels
|
* take additional static arguments corresponding to the {@code case} labels
|
||||||
* of the {@code switch}, implicitly numbered sequentially from {@code [0..N)}.
|
* of the {@code switch}, implicitly numbered sequentially from {@code [0..N)}.
|
||||||
*
|
*
|
||||||
* @since 17
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING)
|
|
||||||
public class SwitchBootstraps {
|
public class SwitchBootstraps {
|
||||||
|
|
||||||
private SwitchBootstraps() {}
|
private SwitchBootstraps() {}
|
||||||
@ -60,7 +60,8 @@ public class SwitchBootstraps {
|
|||||||
DO_TYPE_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "doTypeSwitch",
|
DO_TYPE_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "doTypeSwitch",
|
||||||
MethodType.methodType(int.class, Object.class, int.class, Object[].class));
|
MethodType.methodType(int.class, Object.class, int.class, Object[].class));
|
||||||
DO_ENUM_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "doEnumSwitch",
|
DO_ENUM_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "doEnumSwitch",
|
||||||
MethodType.methodType(int.class, Enum.class, int.class, Object[].class));
|
MethodType.methodType(int.class, Enum.class, int.class, Object[].class,
|
||||||
|
MethodHandles.Lookup.class, Class.class, ResolvedEnumLabels.class));
|
||||||
}
|
}
|
||||||
catch (ReflectiveOperationException e) {
|
catch (ReflectiveOperationException e) {
|
||||||
throw new ExceptionInInitializerError(e);
|
throw new ExceptionInInitializerError(e);
|
||||||
@ -71,7 +72,7 @@ public class SwitchBootstraps {
|
|||||||
* Bootstrap method for linking an {@code invokedynamic} call site that
|
* Bootstrap method for linking an {@code invokedynamic} call site that
|
||||||
* implements a {@code switch} on a target of a reference type. The static
|
* implements a {@code switch} on a target of a reference type. The static
|
||||||
* arguments are an array of case labels which must be non-null and of type
|
* arguments are an array of case labels which must be non-null and of type
|
||||||
* {@code String} or {@code Integer} or {@code Class}.
|
* {@code String} or {@code Integer} or {@code Class} or {@code EnumDesc}.
|
||||||
* <p>
|
* <p>
|
||||||
* The type of the returned {@code CallSite}'s method handle will have
|
* The type of the returned {@code CallSite}'s method handle will have
|
||||||
* a return type of {@code int}. It has two parameters: the first argument
|
* a return type of {@code int}. It has two parameters: the first argument
|
||||||
@ -89,10 +90,16 @@ public class SwitchBootstraps {
|
|||||||
* from the target's class; or</li>
|
* from the target's class; or</li>
|
||||||
* <li>the element is of type {@code String} or {@code Integer} and
|
* <li>the element is of type {@code String} or {@code Integer} and
|
||||||
* equals to the target.</li>
|
* equals to the target.</li>
|
||||||
|
* <li>the element is of type {@code EnumDesc}, that describes a constant that is
|
||||||
|
* equals to the target.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* If no element in the {@code labels} array matches the target, then
|
* If no element in the {@code labels} array matches the target, then
|
||||||
* the method of the call site return the length of the {@code labels} array.
|
* the method of the call site return the length of the {@code labels} array.
|
||||||
|
* <p>
|
||||||
|
* The value of the {@code restart} index must be between {@code 0} (inclusive) and
|
||||||
|
* the length of the {@code labels} array (inclusive),
|
||||||
|
* both or an {@link IndexOutOfBoundsException} is thrown.
|
||||||
*
|
*
|
||||||
* @param lookup Represents a lookup context with the accessibility
|
* @param lookup Represents a lookup context with the accessibility
|
||||||
* privileges of the caller. When used with {@code invokedynamic},
|
* privileges of the caller. When used with {@code invokedynamic},
|
||||||
@ -101,7 +108,7 @@ public class SwitchBootstraps {
|
|||||||
* @param invocationType The invocation type of the {@code CallSite} with two parameters,
|
* @param invocationType The invocation type of the {@code CallSite} with two parameters,
|
||||||
* a reference type, an {@code int}, and {@code int} as a return type.
|
* a reference type, an {@code int}, and {@code int} as a return type.
|
||||||
* @param labels case labels - {@code String} and {@code Integer} constants
|
* @param labels case labels - {@code String} and {@code Integer} constants
|
||||||
* and {@code Class} instances, in any combination
|
* and {@code Class} and {@code EnumDesc} instances, in any combination
|
||||||
* @return a {@code CallSite} returning the first matching element as described above
|
* @return a {@code CallSite} returning the first matching element as described above
|
||||||
*
|
*
|
||||||
* @throws NullPointerException if any argument is {@code null}
|
* @throws NullPointerException if any argument is {@code null}
|
||||||
@ -109,7 +116,7 @@ public class SwitchBootstraps {
|
|||||||
* invocation type is not not a method type of first parameter of a reference type,
|
* 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,
|
* 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},
|
* or if {@code labels} contains an element that is not of type {@code String},
|
||||||
* {@code Integer} or {@code Class}.
|
* {@code Integer}, {@code Class} or {@code EnumDesc}.
|
||||||
* @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
|
* @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
|
||||||
* @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
|
* @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
|
||||||
*/
|
*/
|
||||||
@ -138,12 +145,15 @@ public class SwitchBootstraps {
|
|||||||
Class<?> labelClass = label.getClass();
|
Class<?> labelClass = label.getClass();
|
||||||
if (labelClass != Class.class &&
|
if (labelClass != Class.class &&
|
||||||
labelClass != String.class &&
|
labelClass != String.class &&
|
||||||
labelClass != Integer.class) {
|
labelClass != Integer.class &&
|
||||||
|
labelClass != EnumDesc.class) {
|
||||||
throw new IllegalArgumentException("label with illegal type found: " + label.getClass());
|
throw new IllegalArgumentException("label with illegal type found: " + label.getClass());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int doTypeSwitch(Object target, int startIndex, Object[] labels) {
|
private static int doTypeSwitch(Object target, int startIndex, Object[] labels) {
|
||||||
|
Objects.checkIndex(startIndex, labels.length + 1);
|
||||||
|
|
||||||
if (target == null)
|
if (target == null)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -160,6 +170,11 @@ public class SwitchBootstraps {
|
|||||||
} else if (target instanceof Character input && constant.intValue() == input.charValue()) {
|
} else if (target instanceof Character input && constant.intValue() == input.charValue()) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
} else if (label instanceof EnumDesc<?> enumDesc) {
|
||||||
|
if (target.getClass().isEnum() &&
|
||||||
|
((Enum<?>) target).describeConstable().stream().anyMatch(d -> d.equals(enumDesc))) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
} else if (label.equals(target)) {
|
} else if (label.equals(target)) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -200,6 +215,10 @@ public class SwitchBootstraps {
|
|||||||
* <p>
|
* <p>
|
||||||
* If no element in the {@code labels} array matches the target, then
|
* If no element in the {@code labels} array matches the target, then
|
||||||
* the method of the call site return the length of the {@code labels} array.
|
* the method of the call site return the length of the {@code labels} array.
|
||||||
|
* <p>
|
||||||
|
* The value of the {@code restart} index must be between {@code 0} (inclusive) and
|
||||||
|
* the length of the {@code labels} array (inclusive),
|
||||||
|
* both or an {@link IndexOutOfBoundsException} is thrown.
|
||||||
*
|
*
|
||||||
* @param lookup Represents a lookup context with the accessibility
|
* @param lookup Represents a lookup context with the accessibility
|
||||||
* privileges of the caller. When used with {@code invokedynamic},
|
* privileges of the caller. When used with {@code invokedynamic},
|
||||||
@ -235,13 +254,28 @@ public class SwitchBootstraps {
|
|||||||
labels = labels.clone();
|
labels = labels.clone();
|
||||||
|
|
||||||
Class<?> enumClass = invocationType.parameterType(0);
|
Class<?> enumClass = invocationType.parameterType(0);
|
||||||
labels = Stream.of(labels).map(l -> convertEnumConstants(lookup, enumClass, l)).toArray();
|
Stream.of(labels).forEach(l -> validateEnumLabel(enumClass, l));
|
||||||
|
MethodHandle temporary =
|
||||||
|
MethodHandles.insertArguments(DO_ENUM_SWITCH, 2, labels, lookup, enumClass, new ResolvedEnumLabels());
|
||||||
|
temporary = temporary.asType(invocationType);
|
||||||
|
|
||||||
MethodHandle target =
|
return new ConstantCallSite(temporary);
|
||||||
MethodHandles.insertArguments(DO_ENUM_SWITCH, 2, (Object) labels);
|
}
|
||||||
target = target.asType(invocationType);
|
|
||||||
|
|
||||||
return new ConstantCallSite(target);
|
private static <E extends Enum<E>> void validateEnumLabel(Class<?> enumClassTemplate, Object label) {
|
||||||
|
if (label == null) {
|
||||||
|
throw new IllegalArgumentException("null label found");
|
||||||
|
}
|
||||||
|
Class<?> labelClass = label.getClass();
|
||||||
|
if (labelClass == Class.class) {
|
||||||
|
if (label != enumClassTemplate) {
|
||||||
|
throw new IllegalArgumentException("the Class label: " + label +
|
||||||
|
", expected the provided enum class: " + enumClassTemplate);
|
||||||
|
}
|
||||||
|
} else if (labelClass != String.class) {
|
||||||
|
throw new IllegalArgumentException("label with illegal type found: " + labelClass +
|
||||||
|
", expected label of type either String or Class");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <E extends Enum<E>> Object convertEnumConstants(MethodHandles.Lookup lookup, Class<?> enumClassTemplate, Object label) {
|
private static <E extends Enum<E>> Object convertEnumConstants(MethodHandles.Lookup lookup, Class<?> enumClassTemplate, Object label) {
|
||||||
@ -269,10 +303,22 @@ public class SwitchBootstraps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int doEnumSwitch(Enum<?> target, int startIndex, Object[] labels) {
|
private static int doEnumSwitch(Enum<?> target, int startIndex, Object[] unresolvedLabels,
|
||||||
|
MethodHandles.Lookup lookup, Class<?> enumClass,
|
||||||
|
ResolvedEnumLabels resolvedLabels) {
|
||||||
|
Objects.checkIndex(startIndex, unresolvedLabels.length + 1);
|
||||||
|
|
||||||
if (target == null)
|
if (target == null)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if (resolvedLabels.resolvedLabels == null) {
|
||||||
|
resolvedLabels.resolvedLabels = Stream.of(unresolvedLabels)
|
||||||
|
.map(l -> convertEnumConstants(lookup, enumClass, l))
|
||||||
|
.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
Object[] labels = resolvedLabels.resolvedLabels;
|
||||||
|
|
||||||
// Dumbest possible strategy
|
// Dumbest possible strategy
|
||||||
Class<?> targetClass = target.getClass();
|
Class<?> targetClass = target.getClass();
|
||||||
for (int i = startIndex; i < labels.length; i++) {
|
for (int i = startIndex; i < labels.length; i++) {
|
||||||
@ -288,4 +334,8 @@ public class SwitchBootstraps {
|
|||||||
return labels.length;
|
return labels.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class ResolvedEnumLabels {
|
||||||
|
@Stable
|
||||||
|
public Object[] resolvedLabels;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -480,12 +480,10 @@ public abstract class CallArranger {
|
|||||||
StorageCalculator.StructStorage[] structStorages
|
StorageCalculator.StructStorage[] structStorages
|
||||||
= storageCalculator.structStorages((GroupLayout) layout, forHFA);
|
= storageCalculator.structStorages((GroupLayout) layout, forHFA);
|
||||||
|
|
||||||
for (StorageCalculator.StructStorage(
|
for (StorageCalculator.StructStorage structStorage : structStorages) {
|
||||||
long offset, Class<?> ca, int byteWidth, VMStorage storage
|
|
||||||
) : structStorages) {
|
|
||||||
bindings.dup();
|
bindings.dup();
|
||||||
bindings.vmLoad(storage, ca)
|
bindings.vmLoad(structStorage.storage(), structStorage.carrier())
|
||||||
.bufferStore(offset, ca, byteWidth);
|
.bufferStore(structStorage.offset(), structStorage.carrier(), structStorage.byteWidth());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case STRUCT_REFERENCE -> {
|
case STRUCT_REFERENCE -> {
|
||||||
|
@ -64,10 +64,6 @@ public @interface PreviewFeature {
|
|||||||
* Values should be annotated with the feature's {@code JEP}.
|
* Values should be annotated with the feature's {@code JEP}.
|
||||||
*/
|
*/
|
||||||
public enum Feature {
|
public enum Feature {
|
||||||
@JEP(number=433, title="Pattern Matching for switch", status="Fourth Preview")
|
|
||||||
SWITCH_PATTERN_MATCHING(),
|
|
||||||
@JEP(number=432, title="Record Patterns", status="Second Preview")
|
|
||||||
RECORD_PATTERNS,
|
|
||||||
// not used
|
// not used
|
||||||
VIRTUAL_THREADS,
|
VIRTUAL_THREADS,
|
||||||
@JEP(number=442, title="Foreign Function & Memory API", status="Third Preview")
|
@JEP(number=442, title="Foreign Function & Memory API", status="Third Preview")
|
||||||
|
@ -25,12 +25,9 @@
|
|||||||
|
|
||||||
package com.sun.source.tree;
|
package com.sun.source.tree;
|
||||||
|
|
||||||
import jdk.internal.javac.PreviewFeature;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A marker interface for {@code Tree}s that may be used as {@link CaseTree} labels.
|
* A marker interface for {@code Tree}s that may be used as {@link CaseTree} labels.
|
||||||
*
|
*
|
||||||
* @since 17
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
public interface CaseLabelTree extends Tree {}
|
public interface CaseLabelTree extends Tree {}
|
||||||
|
@ -27,8 +27,6 @@ package com.sun.source.tree;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jdk.internal.javac.PreviewFeature;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tree node for a {@code case} in a {@code switch} statement or expression.
|
* A tree node for a {@code case} in a {@code switch} statement or expression.
|
||||||
*
|
*
|
||||||
@ -72,11 +70,18 @@ public interface CaseTree extends Tree {
|
|||||||
* For {@code default} case return a list with a single element, {@link DefaultCaseLabelTree}.
|
* For {@code default} case return a list with a single element, {@link DefaultCaseLabelTree}.
|
||||||
*
|
*
|
||||||
* @return labels for this case
|
* @return labels for this case
|
||||||
* @since 17
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
List<? extends CaseLabelTree> getLabels();
|
List<? extends CaseLabelTree> getLabels();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The guard for the case.
|
||||||
|
*
|
||||||
|
* @return the guard
|
||||||
|
* @since 21
|
||||||
|
*/
|
||||||
|
ExpressionTree getGuard();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For case with kind {@linkplain CaseKind#STATEMENT},
|
* For case with kind {@linkplain CaseKind#STATEMENT},
|
||||||
* returns the statements labeled by the case.
|
* returns the statements labeled by the case.
|
||||||
|
@ -25,13 +25,10 @@
|
|||||||
|
|
||||||
package com.sun.source.tree;
|
package com.sun.source.tree;
|
||||||
|
|
||||||
import jdk.internal.javac.PreviewFeature;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A case label element that refers to a constant expression
|
* A case label element that refers to a constant expression
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
public interface ConstantCaseLabelTree extends CaseLabelTree {
|
public interface ConstantCaseLabelTree extends CaseLabelTree {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,14 +26,12 @@
|
|||||||
package com.sun.source.tree;
|
package com.sun.source.tree;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import jdk.internal.javac.PreviewFeature;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A deconstruction pattern tree.
|
* A deconstruction pattern tree.
|
||||||
*
|
*
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
|
|
||||||
public interface DeconstructionPatternTree extends PatternTree {
|
public interface DeconstructionPatternTree extends PatternTree {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,12 +24,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.sun.source.tree;
|
package com.sun.source.tree;
|
||||||
|
|
||||||
import jdk.internal.javac.PreviewFeature;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A case label that marks {@code default} in {@code case null, default}.
|
* A case label that marks {@code default} in {@code case null, default}.
|
||||||
*
|
*
|
||||||
* @since 17
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
public interface DefaultCaseLabelTree extends CaseLabelTree {}
|
public interface DefaultCaseLabelTree extends CaseLabelTree {}
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
package com.sun.source.tree;
|
package com.sun.source.tree;
|
||||||
|
|
||||||
import jdk.internal.javac.PreviewFeature;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tree node for an "enhanced" {@code for} loop statement.
|
* A tree node for an "enhanced" {@code for} loop statement.
|
||||||
*
|
*
|
||||||
@ -43,37 +41,12 @@ import jdk.internal.javac.PreviewFeature;
|
|||||||
* @since 1.6
|
* @since 1.6
|
||||||
*/
|
*/
|
||||||
public interface EnhancedForLoopTree extends StatementTree {
|
public interface EnhancedForLoopTree extends StatementTree {
|
||||||
/**
|
|
||||||
* "Enhanced" {@code for} declarations come in two forms:
|
|
||||||
* <ul>
|
|
||||||
* <li> local variable declarations and
|
|
||||||
* <li> record patterns
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @since 20
|
|
||||||
*/
|
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
|
|
||||||
public enum DeclarationKind {
|
|
||||||
/** enum constant for local variable declarations */
|
|
||||||
VARIABLE,
|
|
||||||
/** enum constant for record pattern declarations */
|
|
||||||
PATTERN
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the control variable for the loop.
|
* Returns the control variable for the loop.
|
||||||
* @return the control variable, or {@code null} if this "enhanced" {@code for} uses a pattern
|
* @return the control variable
|
||||||
*/
|
*/
|
||||||
VariableTree getVariable();
|
VariableTree getVariable();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the control variable or pattern for the loop.
|
|
||||||
* @return the control variable or pattern
|
|
||||||
* @since 20
|
|
||||||
*/
|
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
|
|
||||||
Tree getVariableOrRecordPattern();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the expression yielding the values for the control variable.
|
* Returns the expression yielding the values for the control variable.
|
||||||
* @return the expression
|
* @return the expression
|
||||||
@ -85,12 +58,4 @@ public interface EnhancedForLoopTree extends StatementTree {
|
|||||||
* @return the body of the loop
|
* @return the body of the loop
|
||||||
*/
|
*/
|
||||||
StatementTree getStatement();
|
StatementTree getStatement();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the kind of the declaration of the "enhanced" {@code for}.
|
|
||||||
* @return the kind of the declaration
|
|
||||||
* @since 20
|
|
||||||
*/
|
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
|
|
||||||
DeclarationKind getDeclarationKind();
|
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
package com.sun.source.tree;
|
package com.sun.source.tree;
|
||||||
|
|
||||||
import jdk.internal.javac.PreviewFeature;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tree node for an {@code instanceof} expression.
|
* A tree node for an {@code instanceof} expression.
|
||||||
*
|
*
|
||||||
@ -43,22 +41,6 @@ import jdk.internal.javac.PreviewFeature;
|
|||||||
*/
|
*/
|
||||||
public interface InstanceOfTree extends ExpressionTree {
|
public interface InstanceOfTree extends ExpressionTree {
|
||||||
|
|
||||||
/**
|
|
||||||
* Two possible variants of instanceof expressions:
|
|
||||||
* <ul>
|
|
||||||
* <li> testing types, and
|
|
||||||
* <li> performing pattern matching
|
|
||||||
* </ul>
|
|
||||||
* @since 20
|
|
||||||
*/
|
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
|
|
||||||
public enum TestKind {
|
|
||||||
/** instanceof only testing a type */
|
|
||||||
TYPE,
|
|
||||||
/** instanceof doing a pattern matching */
|
|
||||||
PATTERN
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the expression to be tested.
|
* Returns the expression to be tested.
|
||||||
* @return the expression
|
* @return the expression
|
||||||
@ -93,12 +75,4 @@ public interface InstanceOfTree extends ExpressionTree {
|
|||||||
*/
|
*/
|
||||||
PatternTree getPattern();
|
PatternTree getPattern();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the kind of this instanceof expression.
|
|
||||||
*
|
|
||||||
* @return the kind of this instanceof expression
|
|
||||||
* @since 20
|
|
||||||
*/
|
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
|
|
||||||
TestKind getTestKind();
|
|
||||||
}
|
}
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2005, 2014, 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 com.sun.source.tree;
|
|
||||||
|
|
||||||
import jdk.internal.javac.PreviewFeature;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A tree node for a parenthesized pattern.
|
|
||||||
*
|
|
||||||
* For example:
|
|
||||||
* <pre>
|
|
||||||
* ( <em>pattern</em> )
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @jls 14.30.1 Kinds of Patterns
|
|
||||||
*
|
|
||||||
* @since 17
|
|
||||||
*/
|
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
public interface ParenthesizedPatternTree extends PatternTree {
|
|
||||||
/**
|
|
||||||
* Returns the pattern within the parentheses.
|
|
||||||
* @return the pattern
|
|
||||||
*/
|
|
||||||
PatternTree getPattern();
|
|
||||||
}
|
|
@ -25,13 +25,10 @@
|
|||||||
|
|
||||||
package com.sun.source.tree;
|
package com.sun.source.tree;
|
||||||
|
|
||||||
import jdk.internal.javac.PreviewFeature;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A case label element that refers to an expression
|
* A case label element that refers to an expression
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
public interface PatternCaseLabelTree extends CaseLabelTree {
|
public interface PatternCaseLabelTree extends CaseLabelTree {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,11 +38,4 @@ public interface PatternCaseLabelTree extends CaseLabelTree {
|
|||||||
*/
|
*/
|
||||||
public PatternTree getPattern();
|
public PatternTree getPattern();
|
||||||
|
|
||||||
/**
|
|
||||||
* The guard for the case.
|
|
||||||
*
|
|
||||||
* @return the guard
|
|
||||||
*/
|
|
||||||
ExpressionTree getGuard();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -234,44 +234,32 @@ public interface Tree {
|
|||||||
*/
|
*/
|
||||||
BINDING_PATTERN(BindingPatternTree.class),
|
BINDING_PATTERN(BindingPatternTree.class),
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for instances of {@link ParenthesizedPatternTree}.
|
|
||||||
*
|
|
||||||
* @since 17
|
|
||||||
*/
|
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
PARENTHESIZED_PATTERN(ParenthesizedPatternTree.class),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for instances of {@link DefaultCaseLabelTree}.
|
* Used for instances of {@link DefaultCaseLabelTree}.
|
||||||
*
|
*
|
||||||
* @since 17
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
DEFAULT_CASE_LABEL(DefaultCaseLabelTree.class),
|
DEFAULT_CASE_LABEL(DefaultCaseLabelTree.class),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for instances of {@link ConstantCaseLabelTree}.
|
* Used for instances of {@link ConstantCaseLabelTree}.
|
||||||
*
|
*
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
CONSTANT_CASE_LABEL(ConstantCaseLabelTree.class),
|
CONSTANT_CASE_LABEL(ConstantCaseLabelTree.class),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for instances of {@link PatternCaseLabelTree}.
|
* Used for instances of {@link PatternCaseLabelTree}.
|
||||||
*
|
*
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
PATTERN_CASE_LABEL(PatternCaseLabelTree.class),
|
PATTERN_CASE_LABEL(PatternCaseLabelTree.class),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for instances of {@link DeconstructionPatternTree}.
|
* Used for instances of {@link DeconstructionPatternTree}.
|
||||||
*
|
*
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
|
|
||||||
DECONSTRUCTION_PATTERN(DeconstructionPatternTree.class),
|
DECONSTRUCTION_PATTERN(DeconstructionPatternTree.class),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -282,9 +282,8 @@ public interface TreeVisitor<R,P> {
|
|||||||
* @param node the node being visited
|
* @param node the node being visited
|
||||||
* @param p a parameter value
|
* @param p a parameter value
|
||||||
* @return a result value
|
* @return a result value
|
||||||
* @since 17
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p);
|
R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -292,9 +291,8 @@ public interface TreeVisitor<R,P> {
|
|||||||
* @param node the node being visited
|
* @param node the node being visited
|
||||||
* @param p a parameter value
|
* @param p a parameter value
|
||||||
* @return a result value
|
* @return a result value
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
R visitConstantCaseLabel(ConstantCaseLabelTree node, P p);
|
R visitConstantCaseLabel(ConstantCaseLabelTree node, P p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -302,9 +300,8 @@ public interface TreeVisitor<R,P> {
|
|||||||
* @param node the node being visited
|
* @param node the node being visited
|
||||||
* @param p a parameter value
|
* @param p a parameter value
|
||||||
* @return a result value
|
* @return a result value
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
R visitPatternCaseLabel(PatternCaseLabelTree node, P p);
|
R visitPatternCaseLabel(PatternCaseLabelTree node, P p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -312,9 +309,8 @@ public interface TreeVisitor<R,P> {
|
|||||||
* @param node the node being visited
|
* @param node the node being visited
|
||||||
* @param p a parameter value
|
* @param p a parameter value
|
||||||
* @return a result value
|
* @return a result value
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
|
|
||||||
R visitDeconstructionPattern(DeconstructionPatternTree node, P p);
|
R visitDeconstructionPattern(DeconstructionPatternTree node, P p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -341,16 +337,6 @@ public interface TreeVisitor<R,P> {
|
|||||||
*/
|
*/
|
||||||
R visitNewArray(NewArrayTree node, P p);
|
R visitNewArray(NewArrayTree node, P p);
|
||||||
|
|
||||||
/**
|
|
||||||
* Visits a {@code ParenthesizedPatternTree} node.
|
|
||||||
* @param node the node being visited
|
|
||||||
* @param p a parameter value
|
|
||||||
* @return a result value
|
|
||||||
* @since 17
|
|
||||||
*/
|
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
R visitParenthesizedPattern(ParenthesizedPatternTree node, P p);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits a {@code NewClassTree} node.
|
* Visits a {@code NewClassTree} node.
|
||||||
* @param node the node being visited
|
* @param node the node being visited
|
||||||
|
@ -664,10 +664,9 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
|
|||||||
* @param node {@inheritDoc}
|
* @param node {@inheritDoc}
|
||||||
* @param p {@inheritDoc}
|
* @param p {@inheritDoc}
|
||||||
* @return the result of {@code defaultAction}
|
* @return the result of {@code defaultAction}
|
||||||
* @since 17
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
public R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) {
|
public R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) {
|
||||||
return defaultAction(node, p);
|
return defaultAction(node, p);
|
||||||
}
|
}
|
||||||
@ -680,10 +679,9 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
|
|||||||
* @param node {@inheritDoc}
|
* @param node {@inheritDoc}
|
||||||
* @param p {@inheritDoc}
|
* @param p {@inheritDoc}
|
||||||
* @return the result of {@code defaultAction}
|
* @return the result of {@code defaultAction}
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
public R visitConstantCaseLabel(ConstantCaseLabelTree node, P p) {
|
public R visitConstantCaseLabel(ConstantCaseLabelTree node, P p) {
|
||||||
return defaultAction(node, p);
|
return defaultAction(node, p);
|
||||||
}
|
}
|
||||||
@ -696,10 +694,9 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
|
|||||||
* @param node {@inheritDoc}
|
* @param node {@inheritDoc}
|
||||||
* @param p {@inheritDoc}
|
* @param p {@inheritDoc}
|
||||||
* @return the result of {@code defaultAction}
|
* @return the result of {@code defaultAction}
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
|
|
||||||
public R visitDeconstructionPattern(DeconstructionPatternTree node, P p) {
|
public R visitDeconstructionPattern(DeconstructionPatternTree node, P p) {
|
||||||
return defaultAction(node, p);
|
return defaultAction(node, p);
|
||||||
}
|
}
|
||||||
@ -712,10 +709,9 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
|
|||||||
* @param node {@inheritDoc}
|
* @param node {@inheritDoc}
|
||||||
* @param p {@inheritDoc}
|
* @param p {@inheritDoc}
|
||||||
* @return the result of {@code defaultAction}
|
* @return the result of {@code defaultAction}
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
public R visitPatternCaseLabel(PatternCaseLabelTree node, P p) {
|
public R visitPatternCaseLabel(PatternCaseLabelTree node, P p) {
|
||||||
return defaultAction(node, p);
|
return defaultAction(node, p);
|
||||||
}
|
}
|
||||||
@ -748,22 +744,6 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
|
|||||||
return defaultAction(node, p);
|
return defaultAction(node, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* @implSpec This implementation calls {@code defaultAction}.
|
|
||||||
*
|
|
||||||
* @param node {@inheritDoc}
|
|
||||||
* @param p {@inheritDoc}
|
|
||||||
* @return the result of {@code defaultAction}
|
|
||||||
* @since 17
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
public R visitParenthesizedPattern(ParenthesizedPatternTree node, P p) {
|
|
||||||
return defaultAction(node, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
|
@ -333,7 +333,7 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public R visitEnhancedForLoop(EnhancedForLoopTree node, P p) {
|
public R visitEnhancedForLoop(EnhancedForLoopTree node, P p) {
|
||||||
R r = scan(node.getVariableOrRecordPattern(), p);
|
R r = scan(node.getVariable(), p);
|
||||||
r = scanAndReduce(node.getExpression(), p, r);
|
r = scanAndReduce(node.getExpression(), p, r);
|
||||||
r = scanAndReduce(node.getStatement(), p, r);
|
r = scanAndReduce(node.getStatement(), p, r);
|
||||||
return r;
|
return r;
|
||||||
@ -397,6 +397,7 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
|
|||||||
@Override
|
@Override
|
||||||
public R visitCase(CaseTree node, P p) {
|
public R visitCase(CaseTree node, P p) {
|
||||||
R r = scan(node.getLabels(), p);
|
R r = scan(node.getLabels(), p);
|
||||||
|
r = scanAndReduce(node.getGuard(), p, r);
|
||||||
if (node.getCaseKind() == CaseTree.CaseKind.RULE)
|
if (node.getCaseKind() == CaseTree.CaseKind.RULE)
|
||||||
r = scanAndReduce(node.getBody(), p, r);
|
r = scanAndReduce(node.getBody(), p, r);
|
||||||
else
|
else
|
||||||
@ -799,10 +800,9 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
|
|||||||
* @param node {@inheritDoc}
|
* @param node {@inheritDoc}
|
||||||
* @param p {@inheritDoc}
|
* @param p {@inheritDoc}
|
||||||
* @return the result of scanning
|
* @return the result of scanning
|
||||||
* @since 17
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
public R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) {
|
public R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -815,10 +815,9 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
|
|||||||
* @param node {@inheritDoc}
|
* @param node {@inheritDoc}
|
||||||
* @param p {@inheritDoc}
|
* @param p {@inheritDoc}
|
||||||
* @return the result of scanning
|
* @return the result of scanning
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
public R visitConstantCaseLabel(ConstantCaseLabelTree node, P p) {
|
public R visitConstantCaseLabel(ConstantCaseLabelTree node, P p) {
|
||||||
return scan(node.getConstantExpression(), p);
|
return scan(node.getConstantExpression(), p);
|
||||||
}
|
}
|
||||||
@ -831,14 +830,11 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
|
|||||||
* @param node {@inheritDoc}
|
* @param node {@inheritDoc}
|
||||||
* @param p {@inheritDoc}
|
* @param p {@inheritDoc}
|
||||||
* @return the result of scanning
|
* @return the result of scanning
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
public R visitPatternCaseLabel(PatternCaseLabelTree node, P p) {
|
public R visitPatternCaseLabel(PatternCaseLabelTree node, P p) {
|
||||||
R r = scan(node.getPattern(), p);
|
return scan(node.getPattern(), p);
|
||||||
r = scanAndReduce(node.getGuard(), p, r);
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -849,10 +845,9 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
|
|||||||
* @param node {@inheritDoc}
|
* @param node {@inheritDoc}
|
||||||
* @param p {@inheritDoc}
|
* @param p {@inheritDoc}
|
||||||
* @return the result of scanning
|
* @return the result of scanning
|
||||||
* @since 19
|
* @since 21
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
|
|
||||||
public R visitDeconstructionPattern(DeconstructionPatternTree node, P p) {
|
public R visitDeconstructionPattern(DeconstructionPatternTree node, P p) {
|
||||||
R r = scan(node.getDeconstructor(), p);
|
R r = scan(node.getDeconstructor(), p);
|
||||||
r = scanAndReduce(node.getNestedPatterns(), p, r);
|
r = scanAndReduce(node.getNestedPatterns(), p, r);
|
||||||
@ -887,22 +882,6 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
|
|||||||
return scan(node.getExpression(), p);
|
return scan(node.getExpression(), p);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* @implSpec This implementation scans the children in left to right order.
|
|
||||||
*
|
|
||||||
* @param node {@inheritDoc}
|
|
||||||
* @param p {@inheritDoc}
|
|
||||||
* @return the result of scanning
|
|
||||||
* @since 17
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
|
|
||||||
public R visitParenthesizedPattern(ParenthesizedPatternTree node, P p) {
|
|
||||||
return scan(node.getPattern(), p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
|
@ -209,11 +209,7 @@ public class Preview {
|
|||||||
*/
|
*/
|
||||||
public boolean isPreview(Feature feature) {
|
public boolean isPreview(Feature feature) {
|
||||||
return switch (feature) {
|
return switch (feature) {
|
||||||
case CASE_NULL -> true;
|
|
||||||
case PATTERN_SWITCH -> true;
|
|
||||||
case STRING_TEMPLATES -> true;
|
case STRING_TEMPLATES -> true;
|
||||||
case UNCONDITIONAL_PATTERN_IN_INSTANCEOF -> true;
|
|
||||||
case RECORD_PATTERNS -> true;
|
|
||||||
|
|
||||||
//Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
|
//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'
|
//When real preview features will be added, this method can be implemented to return 'true'
|
||||||
|
@ -232,12 +232,12 @@ public enum Source {
|
|||||||
REIFIABLE_TYPES_INSTANCEOF(JDK16, Fragments.FeatureReifiableTypesInstanceof, DiagKind.PLURAL),
|
REIFIABLE_TYPES_INSTANCEOF(JDK16, Fragments.FeatureReifiableTypesInstanceof, DiagKind.PLURAL),
|
||||||
RECORDS(JDK16, Fragments.FeatureRecords, DiagKind.PLURAL),
|
RECORDS(JDK16, Fragments.FeatureRecords, DiagKind.PLURAL),
|
||||||
SEALED_CLASSES(JDK17, Fragments.FeatureSealedClasses, DiagKind.PLURAL),
|
SEALED_CLASSES(JDK17, Fragments.FeatureSealedClasses, DiagKind.PLURAL),
|
||||||
CASE_NULL(JDK17, Fragments.FeatureCaseNull, DiagKind.NORMAL),
|
CASE_NULL(JDK21, Fragments.FeatureCaseNull, DiagKind.NORMAL),
|
||||||
PATTERN_SWITCH(JDK17, Fragments.FeaturePatternSwitch, DiagKind.PLURAL),
|
PATTERN_SWITCH(JDK21, Fragments.FeaturePatternSwitch, DiagKind.PLURAL),
|
||||||
REDUNDANT_STRICTFP(JDK17),
|
REDUNDANT_STRICTFP(JDK17),
|
||||||
|
UNCONDITIONAL_PATTERN_IN_INSTANCEOF(JDK21, Fragments.FeatureUnconditionalPatternsInInstanceof, DiagKind.PLURAL),
|
||||||
|
RECORD_PATTERNS(JDK21, Fragments.FeatureDeconstructionPatterns, DiagKind.PLURAL),
|
||||||
STRING_TEMPLATES(JDK21, Fragments.FeatureStringTemplates, DiagKind.PLURAL),
|
STRING_TEMPLATES(JDK21, Fragments.FeatureStringTemplates, DiagKind.PLURAL),
|
||||||
UNCONDITIONAL_PATTERN_IN_INSTANCEOF(JDK19, Fragments.FeatureUnconditionalPatternsInInstanceof, DiagKind.PLURAL),
|
|
||||||
RECORD_PATTERNS(JDK19, Fragments.FeatureDeconstructionPatterns, DiagKind.PLURAL),
|
|
||||||
WARN_ON_ILLEGAL_UTF8(MIN, JDK21),
|
WARN_ON_ILLEGAL_UTF8(MIN, JDK21),
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -191,7 +191,6 @@ public class Symtab {
|
|||||||
public final Type incompatibleClassChangeErrorType;
|
public final Type incompatibleClassChangeErrorType;
|
||||||
public final Type cloneNotSupportedExceptionType;
|
public final Type cloneNotSupportedExceptionType;
|
||||||
public final Type matchExceptionType;
|
public final Type matchExceptionType;
|
||||||
public final Type nullPointerExceptionType;
|
|
||||||
public final Type annotationType;
|
public final Type annotationType;
|
||||||
public final TypeSymbol enumSym;
|
public final TypeSymbol enumSym;
|
||||||
public final Type listType;
|
public final Type listType;
|
||||||
@ -224,8 +223,11 @@ public class Symtab {
|
|||||||
public final Type typeDescriptorType;
|
public final Type typeDescriptorType;
|
||||||
public final Type recordType;
|
public final Type recordType;
|
||||||
public final Type switchBootstrapsType;
|
public final Type switchBootstrapsType;
|
||||||
|
public final Type constantBootstrapsType;
|
||||||
public final Type valueBasedType;
|
public final Type valueBasedType;
|
||||||
public final Type valueBasedInternalType;
|
public final Type valueBasedInternalType;
|
||||||
|
public final Type classDescType;
|
||||||
|
public final Type enumDescType;
|
||||||
|
|
||||||
// For serialization lint checking
|
// For serialization lint checking
|
||||||
public final Type objectStreamFieldType;
|
public final Type objectStreamFieldType;
|
||||||
@ -565,7 +567,6 @@ public class Symtab {
|
|||||||
incompatibleClassChangeErrorType = enterClass("java.lang.IncompatibleClassChangeError");
|
incompatibleClassChangeErrorType = enterClass("java.lang.IncompatibleClassChangeError");
|
||||||
cloneNotSupportedExceptionType = enterClass("java.lang.CloneNotSupportedException");
|
cloneNotSupportedExceptionType = enterClass("java.lang.CloneNotSupportedException");
|
||||||
matchExceptionType = enterClass("java.lang.MatchException");
|
matchExceptionType = enterClass("java.lang.MatchException");
|
||||||
nullPointerExceptionType = enterClass("java.lang.NullPointerException");
|
|
||||||
annotationType = enterClass("java.lang.annotation.Annotation");
|
annotationType = enterClass("java.lang.annotation.Annotation");
|
||||||
classLoaderType = enterClass("java.lang.ClassLoader");
|
classLoaderType = enterClass("java.lang.ClassLoader");
|
||||||
enumSym = enterClass(java_base, names.java_lang_Enum);
|
enumSym = enterClass(java_base, names.java_lang_Enum);
|
||||||
@ -609,8 +610,11 @@ public class Symtab {
|
|||||||
typeDescriptorType = enterClass("java.lang.invoke.TypeDescriptor");
|
typeDescriptorType = enterClass("java.lang.invoke.TypeDescriptor");
|
||||||
recordType = enterClass("java.lang.Record");
|
recordType = enterClass("java.lang.Record");
|
||||||
switchBootstrapsType = enterClass("java.lang.runtime.SwitchBootstraps");
|
switchBootstrapsType = enterClass("java.lang.runtime.SwitchBootstraps");
|
||||||
|
constantBootstrapsType = enterClass("java.lang.invoke.ConstantBootstraps");
|
||||||
valueBasedType = enterClass("jdk.internal.ValueBased");
|
valueBasedType = enterClass("jdk.internal.ValueBased");
|
||||||
valueBasedInternalType = enterSyntheticAnnotation("jdk.internal.ValueBased+Annotation");
|
valueBasedInternalType = enterSyntheticAnnotation("jdk.internal.ValueBased+Annotation");
|
||||||
|
classDescType = enterClass("java.lang.constant.ClassDesc");
|
||||||
|
enumDescType = enterClass("java.lang.Enum$EnumDesc");
|
||||||
// For serialization lint checking
|
// For serialization lint checking
|
||||||
objectStreamFieldType = enterClass("java.io.ObjectStreamField");
|
objectStreamFieldType = enterClass("java.io.ObjectStreamField");
|
||||||
objectInputStreamType = enterClass("java.io.ObjectInputStream");
|
objectInputStreamType = enterClass("java.io.ObjectInputStream");
|
||||||
|
@ -33,7 +33,6 @@ import java.util.Map;
|
|||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.sun.source.tree.EnhancedForLoopTree;
|
|
||||||
import com.sun.source.tree.LambdaExpressionTree;
|
import com.sun.source.tree.LambdaExpressionTree;
|
||||||
import com.sun.source.tree.NewClassTree;
|
import com.sun.source.tree.NewClassTree;
|
||||||
import com.sun.source.tree.VariableTree;
|
import com.sun.source.tree.VariableTree;
|
||||||
@ -426,24 +425,18 @@ public class Analyzer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean match(JCEnhancedForLoop tree){
|
boolean match(JCEnhancedForLoop tree){
|
||||||
return tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE &&
|
return !isImplicitlyTyped(tree.var);
|
||||||
!isImplicitlyTyped((JCVariableDecl) tree.varOrRecordPattern);
|
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
List<JCEnhancedForLoop> rewrite(JCEnhancedForLoop oldTree) {
|
List<JCEnhancedForLoop> rewrite(JCEnhancedForLoop oldTree) {
|
||||||
Assert.check(oldTree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE);
|
|
||||||
|
|
||||||
JCEnhancedForLoop newTree = copier.copy(oldTree);
|
JCEnhancedForLoop newTree = copier.copy(oldTree);
|
||||||
newTree.varOrRecordPattern = rewriteVarType((JCVariableDecl) oldTree.varOrRecordPattern);
|
newTree.var = rewriteVarType(oldTree.var);
|
||||||
newTree.body = make.at(oldTree.body).Block(0, List.nil());
|
newTree.body = make.at(oldTree.body).Block(0, List.nil());
|
||||||
return List.of(newTree);
|
return List.of(newTree);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
void process(JCEnhancedForLoop oldTree, JCEnhancedForLoop newTree, boolean hasErrors){
|
void process(JCEnhancedForLoop oldTree, JCEnhancedForLoop newTree, boolean hasErrors){
|
||||||
Assert.check(oldTree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE);
|
processVar(oldTree.var, newTree.var, hasErrors);
|
||||||
|
|
||||||
processVar((JCVariableDecl) oldTree.varOrRecordPattern,
|
|
||||||
(JCVariableDecl) newTree.varOrRecordPattern, hasErrors);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ import javax.lang.model.element.ElementKind;
|
|||||||
import javax.tools.JavaFileObject;
|
import javax.tools.JavaFileObject;
|
||||||
|
|
||||||
import com.sun.source.tree.CaseTree;
|
import com.sun.source.tree.CaseTree;
|
||||||
import com.sun.source.tree.EnhancedForLoopTree;
|
|
||||||
import com.sun.source.tree.IdentifierTree;
|
import com.sun.source.tree.IdentifierTree;
|
||||||
import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
|
import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
|
||||||
import com.sun.source.tree.MemberSelectTree;
|
import com.sun.source.tree.MemberSelectTree;
|
||||||
@ -171,8 +170,8 @@ public class Attr extends JCTree.Visitor {
|
|||||||
allowRecords = Feature.RECORDS.allowedInSource(source);
|
allowRecords = Feature.RECORDS.allowedInSource(source);
|
||||||
allowPatternSwitch = (preview.isEnabled() || !preview.isPreview(Feature.PATTERN_SWITCH)) &&
|
allowPatternSwitch = (preview.isEnabled() || !preview.isPreview(Feature.PATTERN_SWITCH)) &&
|
||||||
Feature.PATTERN_SWITCH.allowedInSource(source);
|
Feature.PATTERN_SWITCH.allowedInSource(source);
|
||||||
allowUnconditionalPatternsInstanceOf = (preview.isEnabled() || !preview.isPreview(Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF)) &&
|
allowUnconditionalPatternsInstanceOf =
|
||||||
Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF.allowedInSource(source);
|
Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF.allowedInSource(source);
|
||||||
sourceName = source.name;
|
sourceName = source.name;
|
||||||
useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning");
|
useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning");
|
||||||
|
|
||||||
@ -1514,25 +1513,24 @@ public class Attr extends JCTree.Visitor {
|
|||||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||||
Env<AttrContext> loopEnv =
|
Env<AttrContext> loopEnv =
|
||||||
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
|
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//the Formal Parameter of a for-each loop is not in the scope when
|
//the Formal Parameter of a for-each loop is not in the scope when
|
||||||
//attributing the for-each expression; we mimic this by attributing
|
//attributing the for-each expression; we mimic this by attributing
|
||||||
//the for-each expression first (against original scope).
|
//the for-each expression first (against original scope).
|
||||||
Type exprType = types.cvarUpperBound(attribExpr(tree.expr, loopEnv));
|
Type exprType = types.cvarUpperBound(attribExpr(tree.expr, loopEnv));
|
||||||
chk.checkNonVoid(tree.pos(), exprType);
|
chk.checkNonVoid(tree.pos(), exprType);
|
||||||
tree.elementType = types.elemtype(exprType); // perhaps expr is an array?
|
Type elemtype = types.elemtype(exprType); // perhaps expr is an array?
|
||||||
if (tree.elementType == null) {
|
if (elemtype == null) {
|
||||||
// or perhaps expr implements Iterable<T>?
|
// or perhaps expr implements Iterable<T>?
|
||||||
Type base = types.asSuper(exprType, syms.iterableType.tsym);
|
Type base = types.asSuper(exprType, syms.iterableType.tsym);
|
||||||
if (base == null) {
|
if (base == null) {
|
||||||
log.error(tree.expr.pos(),
|
log.error(tree.expr.pos(),
|
||||||
Errors.ForeachNotApplicableToType(exprType,
|
Errors.ForeachNotApplicableToType(exprType,
|
||||||
Fragments.TypeReqArrayOrIterable));
|
Fragments.TypeReqArrayOrIterable));
|
||||||
tree.elementType = types.createErrorType(exprType);
|
elemtype = types.createErrorType(exprType);
|
||||||
} else {
|
} else {
|
||||||
List<Type> iterableParams = base.allparams();
|
List<Type> iterableParams = base.allparams();
|
||||||
tree.elementType = iterableParams.isEmpty()
|
elemtype = iterableParams.isEmpty()
|
||||||
? syms.objectType
|
? syms.objectType
|
||||||
: types.wildUpperBound(iterableParams.head);
|
: types.wildUpperBound(iterableParams.head);
|
||||||
|
|
||||||
@ -1546,40 +1544,14 @@ public class Attr extends JCTree.Visitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) {
|
if (tree.var.isImplicitlyTyped()) {
|
||||||
if (jcVariableDecl.isImplicitlyTyped()) {
|
Type inferredType = chk.checkLocalVarType(tree.var, elemtype, tree.var.name);
|
||||||
Type inferredType = chk.checkLocalVarType(jcVariableDecl, tree.elementType, jcVariableDecl.name);
|
setSyntheticVariableType(tree.var, inferredType);
|
||||||
setSyntheticVariableType(jcVariableDecl, inferredType);
|
|
||||||
}
|
|
||||||
attribStat(jcVariableDecl, loopEnv);
|
|
||||||
chk.checkType(tree.expr.pos(), tree.elementType, jcVariableDecl.sym.type);
|
|
||||||
|
|
||||||
loopEnv.tree = tree; // before, we were not in loop!
|
|
||||||
attribStat(tree.body, loopEnv);
|
|
||||||
} else {
|
|
||||||
Assert.check(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.PATTERN);
|
|
||||||
JCRecordPattern jcRecordPattern = (JCRecordPattern) tree.varOrRecordPattern;
|
|
||||||
|
|
||||||
attribExpr(jcRecordPattern, loopEnv, tree.elementType);
|
|
||||||
|
|
||||||
// for(<pattern> x : xs) { y }
|
|
||||||
// we include x's bindings when true in y
|
|
||||||
// we don't do anything with x's bindings when false
|
|
||||||
|
|
||||||
MatchBindings forWithRecordPatternBindings = matchBindings;
|
|
||||||
Env<AttrContext> recordPatternEnv = bindingEnv(loopEnv, forWithRecordPatternBindings.bindingsWhenTrue);
|
|
||||||
|
|
||||||
Type clazztype = jcRecordPattern.type;
|
|
||||||
|
|
||||||
checkCastablePattern(tree.expr.pos(), tree.elementType, clazztype);
|
|
||||||
|
|
||||||
recordPatternEnv.tree = tree; // before, we were not in loop!
|
|
||||||
try {
|
|
||||||
attribStat(tree.body, recordPatternEnv);
|
|
||||||
} finally {
|
|
||||||
recordPatternEnv.info.scope.leave();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
attribStat(tree.var, loopEnv);
|
||||||
|
chk.checkType(tree.expr.pos(), elemtype, tree.var.sym.type);
|
||||||
|
loopEnv.tree = tree; // before, we were not in loop!
|
||||||
|
attribStat(tree.body, loopEnv);
|
||||||
result = null;
|
result = null;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
@ -1717,7 +1689,8 @@ public class Attr extends JCTree.Visitor {
|
|||||||
}
|
}
|
||||||
MatchBindings currentBindings = null;
|
MatchBindings currentBindings = null;
|
||||||
boolean wasUnconditionalPattern = hasUnconditionalPattern;
|
boolean wasUnconditionalPattern = hasUnconditionalPattern;
|
||||||
for (JCCaseLabel label : c.labels) {
|
for (List<JCCaseLabel> labels = c.labels; labels.nonEmpty(); labels = labels.tail) {
|
||||||
|
JCCaseLabel label = labels.head;
|
||||||
if (label instanceof JCConstantCaseLabel constLabel) {
|
if (label instanceof JCConstantCaseLabel constLabel) {
|
||||||
JCExpression expr = constLabel.expr;
|
JCExpression expr = constLabel.expr;
|
||||||
if (TreeInfo.isNull(expr)) {
|
if (TreeInfo.isNull(expr)) {
|
||||||
@ -1731,7 +1704,11 @@ public class Attr extends JCTree.Visitor {
|
|||||||
} else if (enumSwitch) {
|
} else if (enumSwitch) {
|
||||||
Symbol sym = enumConstant(expr, seltype);
|
Symbol sym = enumConstant(expr, seltype);
|
||||||
if (sym == null) {
|
if (sym == null) {
|
||||||
log.error(expr.pos(), Errors.EnumLabelMustBeUnqualifiedEnum);
|
if (allowPatternSwitch) {
|
||||||
|
attribTree(expr, switchEnv, caseLabelResultInfo(seltype));
|
||||||
|
} else {
|
||||||
|
log.error(expr.pos(), Errors.EnumLabelMustBeUnqualifiedEnum);
|
||||||
|
}
|
||||||
} else if (!constants.add(sym)) {
|
} else if (!constants.add(sym)) {
|
||||||
log.error(label.pos(), Errors.DuplicateCaseLabel);
|
log.error(label.pos(), Errors.DuplicateCaseLabel);
|
||||||
}
|
}
|
||||||
@ -1747,17 +1724,14 @@ public class Attr extends JCTree.Visitor {
|
|||||||
rs.basicLogResolveHelper = prevResolveHelper;
|
rs.basicLogResolveHelper = prevResolveHelper;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ResultInfo valTypInfo = new ResultInfo(KindSelector.VAL_TYP,
|
Type pattype = attribTree(expr, switchEnv, caseLabelResultInfo(seltype));
|
||||||
!seltype.hasTag(ERROR) ? seltype
|
|
||||||
: Type.noType);
|
|
||||||
Type pattype = attribTree(expr, switchEnv, valTypInfo);
|
|
||||||
if (!pattype.hasTag(ERROR)) {
|
if (!pattype.hasTag(ERROR)) {
|
||||||
if (pattype.constValue() == null) {
|
if (pattype.constValue() == null) {
|
||||||
Symbol s = TreeInfo.symbol(expr);
|
Symbol s = TreeInfo.symbol(expr);
|
||||||
if (s != null && s.kind == TYP && allowPatternSwitch) {
|
if (s != null && s.kind == TYP && allowPatternSwitch) {
|
||||||
log.error(expr.pos(),
|
log.error(expr.pos(),
|
||||||
Errors.PatternExpected);
|
Errors.PatternExpected);
|
||||||
} else {
|
} else if ((s != null && !s.isEnum()) || !allowPatternSwitch) {
|
||||||
log.error(expr.pos(),
|
log.error(expr.pos(),
|
||||||
(stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq));
|
(stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq));
|
||||||
}
|
}
|
||||||
@ -1786,8 +1760,8 @@ public class Attr extends JCTree.Visitor {
|
|||||||
}
|
}
|
||||||
checkCastablePattern(pat.pos(), seltype, primaryType);
|
checkCastablePattern(pat.pos(), seltype, primaryType);
|
||||||
Type patternType = types.erasure(primaryType);
|
Type patternType = types.erasure(primaryType);
|
||||||
JCExpression guard = patternlabel.guard;
|
JCExpression guard = c.guard;
|
||||||
if (guard != null) {
|
if (labels.tail.isEmpty() && guard != null) {
|
||||||
MatchBindings afterPattern = matchBindings;
|
MatchBindings afterPattern = matchBindings;
|
||||||
Env<AttrContext> bodyEnv = bindingEnv(switchEnv, matchBindings.bindingsWhenTrue);
|
Env<AttrContext> bodyEnv = bindingEnv(switchEnv, matchBindings.bindingsWhenTrue);
|
||||||
try {
|
try {
|
||||||
@ -1801,7 +1775,7 @@ public class Attr extends JCTree.Visitor {
|
|||||||
log.error(guard.pos(), Errors.GuardHasConstantExpressionFalse);
|
log.error(guard.pos(), Errors.GuardHasConstantExpressionFalse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean unguarded = TreeInfo.unguardedCaseLabel(label) && !pat.hasTag(RECORDPATTERN);
|
boolean unguarded = TreeInfo.unguardedCase(c) && !pat.hasTag(RECORDPATTERN);
|
||||||
boolean unconditional =
|
boolean unconditional =
|
||||||
unguarded &&
|
unguarded &&
|
||||||
!patternType.isErroneous() &&
|
!patternType.isErroneous() &&
|
||||||
@ -1854,6 +1828,11 @@ public class Attr extends JCTree.Visitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// where
|
// where
|
||||||
|
private ResultInfo caseLabelResultInfo(Type seltype) {
|
||||||
|
return new ResultInfo(KindSelector.VAL_TYP,
|
||||||
|
!seltype.hasTag(ERROR) ? seltype
|
||||||
|
: Type.noType);
|
||||||
|
}
|
||||||
/** Add any variables defined in stats to the switch scope. */
|
/** Add any variables defined in stats to the switch scope. */
|
||||||
private static void addVars(List<JCStatement> stats, WriteableScope switchScope) {
|
private static void addVars(List<JCStatement> stats, WriteableScope switchScope) {
|
||||||
for (;stats.nonEmpty(); stats = stats.tail) {
|
for (;stats.nonEmpty(); stats = stats.tail) {
|
||||||
@ -4101,7 +4080,6 @@ public class Attr extends JCTree.Visitor {
|
|||||||
Type clazztype;
|
Type clazztype;
|
||||||
JCTree typeTree;
|
JCTree typeTree;
|
||||||
if (tree.pattern.getTag() == BINDINGPATTERN ||
|
if (tree.pattern.getTag() == BINDINGPATTERN ||
|
||||||
tree.pattern.getTag() == PARENTHESIZEDPATTERN ||
|
|
||||||
tree.pattern.getTag() == RECORDPATTERN) {
|
tree.pattern.getTag() == RECORDPATTERN) {
|
||||||
attribExpr(tree.pattern, env, exprtype);
|
attribExpr(tree.pattern, env, exprtype);
|
||||||
clazztype = tree.pattern.type;
|
clazztype = tree.pattern.type;
|
||||||
@ -4109,9 +4087,8 @@ public class Attr extends JCTree.Visitor {
|
|||||||
!exprtype.isErroneous() && !clazztype.isErroneous() &&
|
!exprtype.isErroneous() && !clazztype.isErroneous() &&
|
||||||
tree.pattern.getTag() != RECORDPATTERN) {
|
tree.pattern.getTag() != RECORDPATTERN) {
|
||||||
if (!allowUnconditionalPatternsInstanceOf) {
|
if (!allowUnconditionalPatternsInstanceOf) {
|
||||||
log.error(tree.pos(), Errors.InstanceofPatternNoSubtype(exprtype, clazztype));
|
log.error(DiagnosticFlag.SOURCE_LEVEL, tree.pos(),
|
||||||
} else if (preview.isPreview(Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF)) {
|
Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF.error(this.sourceName));
|
||||||
preview.warnPreview(tree.pattern.pos(), Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
typeTree = TreeInfo.primaryPatternTypeTree((JCPattern) tree.pattern);
|
typeTree = TreeInfo.primaryPatternTypeTree((JCPattern) tree.pattern);
|
||||||
@ -4253,11 +4230,6 @@ public class Attr extends JCTree.Visitor {
|
|||||||
matchBindings = new MatchBindings(outBindings.toList(), List.nil());
|
matchBindings = new MatchBindings(outBindings.toList(), List.nil());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
|
|
||||||
attribExpr(tree.pattern, env);
|
|
||||||
result = tree.type = tree.pattern.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitIndexed(JCArrayAccess tree) {
|
public void visitIndexed(JCArrayAccess tree) {
|
||||||
Type owntype = types.createErrorType(tree.type);
|
Type owntype = types.createErrorType(tree.type);
|
||||||
Type atype = attribExpr(tree.indexed, env);
|
Type atype = attribExpr(tree.indexed, env);
|
||||||
|
@ -4626,7 +4626,7 @@ public class Check {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
void checkSwitchCaseLabelDominated(List<JCCase> cases) {
|
void checkSwitchCaseLabelDominated(List<JCCase> cases) {
|
||||||
List<JCCaseLabel> caseLabels = List.nil();
|
List<Pair<JCCase, JCCaseLabel>> caseLabels = List.nil();
|
||||||
boolean seenDefault = false;
|
boolean seenDefault = false;
|
||||||
boolean seenDefaultLabel = false;
|
boolean seenDefaultLabel = false;
|
||||||
boolean warnDominatedByDefault = false;
|
boolean warnDominatedByDefault = false;
|
||||||
@ -4653,7 +4653,9 @@ public class Check {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Type currentType = labelType(label);
|
Type currentType = labelType(label);
|
||||||
for (JCCaseLabel testCaseLabel : caseLabels) {
|
for (Pair<JCCase, JCCaseLabel> caseAndLabel : caseLabels) {
|
||||||
|
JCCase testCase = caseAndLabel.fst;
|
||||||
|
JCCaseLabel testCaseLabel = caseAndLabel.snd;
|
||||||
Type testType = labelType(testCaseLabel);
|
Type testType = labelType(testCaseLabel);
|
||||||
if (types.isSubtype(currentType, testType) &&
|
if (types.isSubtype(currentType, testType) &&
|
||||||
!currentType.hasTag(ERROR) && !testType.hasTag(ERROR)) {
|
!currentType.hasTag(ERROR) && !testType.hasTag(ERROR)) {
|
||||||
@ -4663,7 +4665,7 @@ public class Check {
|
|||||||
dominated |= !(testCaseLabel instanceof JCConstantCaseLabel);
|
dominated |= !(testCaseLabel instanceof JCConstantCaseLabel);
|
||||||
} else if (label instanceof JCPatternCaseLabel patternCL &&
|
} else if (label instanceof JCPatternCaseLabel patternCL &&
|
||||||
testCaseLabel instanceof JCPatternCaseLabel testPatternCaseLabel &&
|
testCaseLabel instanceof JCPatternCaseLabel testPatternCaseLabel &&
|
||||||
TreeInfo.unguardedCaseLabel(testCaseLabel)) {
|
TreeInfo.unguardedCase(testCase)) {
|
||||||
dominated = patternDominated(testPatternCaseLabel.pat,
|
dominated = patternDominated(testPatternCaseLabel.pat,
|
||||||
patternCL.pat);
|
patternCL.pat);
|
||||||
}
|
}
|
||||||
@ -4672,7 +4674,7 @@ public class Check {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
caseLabels = caseLabels.prepend(label);
|
caseLabels = caseLabels.prepend(Pair.of(c, label));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4697,12 +4699,6 @@ public class Check {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (existingPattern instanceof JCParenthesizedPattern parenthesized) {
|
|
||||||
existingPattern = parenthesized.pattern;
|
|
||||||
}
|
|
||||||
while (currentPattern instanceof JCParenthesizedPattern parenthesized) {
|
|
||||||
currentPattern = parenthesized.pattern;
|
|
||||||
}
|
|
||||||
if (currentPattern instanceof JCBindingPattern) {
|
if (currentPattern instanceof JCBindingPattern) {
|
||||||
return existingPattern instanceof JCBindingPattern;
|
return existingPattern instanceof JCBindingPattern;
|
||||||
} else if (currentPattern instanceof JCRecordPattern currentRecordPattern) {
|
} else if (currentPattern instanceof JCRecordPattern currentRecordPattern) {
|
||||||
|
@ -32,13 +32,11 @@ import java.util.Map.Entry;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.StreamSupport;
|
|
||||||
|
|
||||||
|
import com.sun.source.tree.CaseTree;
|
||||||
import com.sun.source.tree.LambdaExpressionTree.BodyKind;
|
import com.sun.source.tree.LambdaExpressionTree.BodyKind;
|
||||||
import com.sun.tools.javac.code.*;
|
import com.sun.tools.javac.code.*;
|
||||||
import com.sun.tools.javac.code.Scope.WriteableScope;
|
import com.sun.tools.javac.code.Scope.WriteableScope;
|
||||||
import com.sun.tools.javac.code.Source.Feature;
|
|
||||||
import com.sun.tools.javac.resources.CompilerProperties.Errors;
|
import com.sun.tools.javac.resources.CompilerProperties.Errors;
|
||||||
import com.sun.tools.javac.resources.CompilerProperties.Warnings;
|
import com.sun.tools.javac.resources.CompilerProperties.Warnings;
|
||||||
import com.sun.tools.javac.tree.*;
|
import com.sun.tools.javac.tree.*;
|
||||||
@ -52,15 +50,22 @@ import com.sun.tools.javac.tree.JCTree.*;
|
|||||||
|
|
||||||
import static com.sun.tools.javac.code.Flags.*;
|
import static com.sun.tools.javac.code.Flags.*;
|
||||||
import static com.sun.tools.javac.code.Flags.BLOCK;
|
import static com.sun.tools.javac.code.Flags.BLOCK;
|
||||||
|
import com.sun.tools.javac.code.Kinds.Kind;
|
||||||
import static com.sun.tools.javac.code.Kinds.Kind.*;
|
import static com.sun.tools.javac.code.Kinds.Kind.*;
|
||||||
import com.sun.tools.javac.code.Type.TypeVar;
|
import com.sun.tools.javac.code.Type.TypeVar;
|
||||||
import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
|
import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
|
||||||
import static com.sun.tools.javac.code.TypeTag.NONE;
|
|
||||||
import static com.sun.tools.javac.code.TypeTag.VOID;
|
import static com.sun.tools.javac.code.TypeTag.VOID;
|
||||||
import com.sun.tools.javac.code.Types.UniqueType;
|
|
||||||
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
|
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
|
||||||
import static com.sun.tools.javac.tree.JCTree.Tag.*;
|
import static com.sun.tools.javac.tree.JCTree.Tag.*;
|
||||||
import com.sun.tools.javac.util.JCDiagnostic.Fragment;
|
import com.sun.tools.javac.util.JCDiagnostic.Fragment;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.groupingBy;
|
||||||
|
|
||||||
/** This pass implements dataflow analysis for Java programs though
|
/** This pass implements dataflow analysis for Java programs though
|
||||||
* different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that
|
* different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that
|
||||||
@ -211,6 +216,7 @@ public class Flow {
|
|||||||
private final JCDiagnostic.Factory diags;
|
private final JCDiagnostic.Factory diags;
|
||||||
private Env<AttrContext> attrEnv;
|
private Env<AttrContext> attrEnv;
|
||||||
private Lint lint;
|
private Lint lint;
|
||||||
|
private final Infer infer;
|
||||||
|
|
||||||
public static Flow instance(Context context) {
|
public static Flow instance(Context context) {
|
||||||
Flow instance = context.get(flowKey);
|
Flow instance = context.get(flowKey);
|
||||||
@ -334,6 +340,7 @@ public class Flow {
|
|||||||
types = Types.instance(context);
|
types = Types.instance(context);
|
||||||
chk = Check.instance(context);
|
chk = Check.instance(context);
|
||||||
lint = Lint.instance(context);
|
lint = Lint.instance(context);
|
||||||
|
infer = Infer.instance(context);
|
||||||
rs = Resolve.instance(context);
|
rs = Resolve.instance(context);
|
||||||
diags = JCDiagnostic.Factory.instance(context);
|
diags = JCDiagnostic.Factory.instance(context);
|
||||||
Source source = Source.instance(context);
|
Source source = Source.instance(context);
|
||||||
@ -647,21 +654,7 @@ public class Flow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||||
if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) {
|
visitVarDef(tree.var);
|
||||||
visitVarDef(jcVariableDecl);
|
|
||||||
} else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
|
|
||||||
visitRecordPattern(jcRecordPattern);
|
|
||||||
|
|
||||||
Set<Symbol> coveredSymbols =
|
|
||||||
coveredSymbols(jcRecordPattern.pos(), List.of(jcRecordPattern));
|
|
||||||
|
|
||||||
boolean isExhaustive =
|
|
||||||
isExhaustive(jcRecordPattern.pos(), tree.elementType, coveredSymbols);
|
|
||||||
|
|
||||||
if (!isExhaustive) {
|
|
||||||
log.error(tree, Errors.ForeachNotExhaustiveOnType(jcRecordPattern.type, tree.elementType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||||
scan(tree.expr);
|
scan(tree.expr);
|
||||||
pendingExits = new ListBuffer<>();
|
pendingExits = new ListBuffer<>();
|
||||||
@ -705,8 +698,7 @@ public class Flow {
|
|||||||
tree.isExhaustive = tree.hasUnconditionalPattern ||
|
tree.isExhaustive = tree.hasUnconditionalPattern ||
|
||||||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases);
|
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases);
|
||||||
if (exhaustiveSwitch) {
|
if (exhaustiveSwitch) {
|
||||||
Set<Symbol> coveredSymbols = coveredSymbolsForCases(tree.pos(), tree.cases);
|
tree.isExhaustive |= exhausts(tree.selector, tree.cases);
|
||||||
tree.isExhaustive |= isExhaustive(tree.selector.pos(), tree.selector.type, coveredSymbols);
|
|
||||||
if (!tree.isExhaustive) {
|
if (!tree.isExhaustive) {
|
||||||
log.error(tree, Errors.NotExhaustiveStatement);
|
log.error(tree, Errors.NotExhaustiveStatement);
|
||||||
}
|
}
|
||||||
@ -740,10 +732,9 @@ public class Flow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Set<Symbol> coveredSymbols = coveredSymbolsForCases(tree.pos(), tree.cases);
|
|
||||||
tree.isExhaustive = tree.hasUnconditionalPattern ||
|
tree.isExhaustive = tree.hasUnconditionalPattern ||
|
||||||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) ||
|
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) ||
|
||||||
isExhaustive(tree.selector.pos(), tree.selector.type, coveredSymbols);
|
exhausts(tree.selector, tree.cases);
|
||||||
if (!tree.isExhaustive) {
|
if (!tree.isExhaustive) {
|
||||||
log.error(tree, Errors.NotExhaustive);
|
log.error(tree, Errors.NotExhaustive);
|
||||||
}
|
}
|
||||||
@ -751,211 +742,444 @@ public class Flow {
|
|||||||
alive = alive.or(resolveYields(tree, prevPendingExits));
|
alive = alive.or(resolveYields(tree, prevPendingExits));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Symbol> coveredSymbolsForCases(DiagnosticPosition pos,
|
sealed interface PatternDescription {
|
||||||
List<JCCase> cases) {
|
public static PatternDescription from(Types types, Type selectorType, JCPattern pattern) {
|
||||||
HashSet<JCTree> labelValues = cases.stream()
|
if (pattern instanceof JCBindingPattern binding) {
|
||||||
.flatMap(c -> c.labels.stream())
|
Type type = types.isSubtype(selectorType, binding.type)
|
||||||
.filter(TreeInfo::unguardedCaseLabel)
|
? selectorType : binding.type;
|
||||||
.filter(l -> !l.hasTag(DEFAULTCASELABEL))
|
return new BindingPattern(type);
|
||||||
.map(l -> l.hasTag(CONSTANTCASELABEL) ? ((JCConstantCaseLabel) l).expr
|
} else if (pattern instanceof JCRecordPattern record) {
|
||||||
: ((JCPatternCaseLabel) l).pat)
|
Type[] componentTypes = ((ClassSymbol) record.type.tsym).getRecordComponents()
|
||||||
.collect(Collectors.toCollection(HashSet::new));
|
.map(r -> types.memberType(record.type, r))
|
||||||
return coveredSymbols(pos, labelValues);
|
.toArray(s -> new Type[s]);
|
||||||
|
PatternDescription[] nestedDescriptions =
|
||||||
|
new PatternDescription[record.nested.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (List<JCPattern> it = record.nested;
|
||||||
|
it.nonEmpty();
|
||||||
|
it = it.tail, i++) {
|
||||||
|
nestedDescriptions[i] = PatternDescription.from(types, componentTypes[i], it.head);
|
||||||
|
}
|
||||||
|
return new RecordPattern(record.type, componentTypes, nestedDescriptions);
|
||||||
|
} else {
|
||||||
|
throw Assert.error();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Symbol> coveredSymbols(DiagnosticPosition pos,
|
record BindingPattern(Type type) implements PatternDescription {
|
||||||
Iterable<? extends JCTree> labels) {
|
@Override
|
||||||
Set<Symbol> coveredSymbols = new HashSet<>();
|
public int hashCode() {
|
||||||
Map<UniqueType, List<JCRecordPattern>> deconstructionPatternsByType = new HashMap<>();
|
return type.tsym.hashCode();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof BindingPattern other &&
|
||||||
|
type.tsym == other.type.tsym;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return type.tsym + " _";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (JCTree labelValue : labels) {
|
record RecordPattern(Type recordType, int _hashCode, Type[] fullComponentTypes, PatternDescription... nested) implements PatternDescription {
|
||||||
switch (labelValue.getTag()) {
|
|
||||||
case BINDINGPATTERN, PARENTHESIZEDPATTERN -> {
|
public RecordPattern(Type recordType, Type[] fullComponentTypes, PatternDescription[] nested) {
|
||||||
Type primaryPatternType = TreeInfo.primaryPatternType((JCPattern) labelValue);
|
this(recordType, hashCode(-1, recordType, nested), fullComponentTypes, nested);
|
||||||
if (!primaryPatternType.hasTag(NONE)) {
|
}
|
||||||
coveredSymbols.add(primaryPatternType.tsym);
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return _hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof RecordPattern other &&
|
||||||
|
recordType.tsym == other.recordType.tsym &&
|
||||||
|
Arrays.equals(nested, other.nested);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode(int excludeComponent) {
|
||||||
|
return hashCode(excludeComponent, recordType, nested);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int hashCode(int excludeComponent, Type recordType, PatternDescription... nested) {
|
||||||
|
int hash = 5;
|
||||||
|
hash = 41 * hash + recordType.tsym.hashCode();
|
||||||
|
for (int i = 0; i < nested.length; i++) {
|
||||||
|
if (i != excludeComponent) {
|
||||||
|
hash = 41 * hash + nested[i].hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return recordType.tsym + "(" + Arrays.stream(nested)
|
||||||
|
.map(pd -> pd.toString())
|
||||||
|
.collect(Collectors.joining(", ")) + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean exhausts(JCExpression selector, List<JCCase> cases) {
|
||||||
|
Set<PatternDescription> patternSet = new HashSet<>();
|
||||||
|
Map<Symbol, Set<Symbol>> enum2Constants = new HashMap<>();
|
||||||
|
for (JCCase c : cases) {
|
||||||
|
if (!TreeInfo.unguardedCase(c))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (var l : c.labels) {
|
||||||
|
if (l instanceof JCPatternCaseLabel patternLabel) {
|
||||||
|
for (Type component : components(selector.type)) {
|
||||||
|
patternSet.add(PatternDescription.from(types, component, patternLabel.pat));
|
||||||
}
|
}
|
||||||
}
|
} else if (l instanceof JCConstantCaseLabel constantLabel) {
|
||||||
case RECORDPATTERN -> {
|
Symbol s = TreeInfo.symbol(constantLabel.expr);
|
||||||
JCRecordPattern dpat = (JCRecordPattern) labelValue;
|
if (s != null && s.isEnum()) {
|
||||||
UniqueType type = new UniqueType(dpat.type, types);
|
enum2Constants.computeIfAbsent(s.owner, x -> {
|
||||||
List<JCRecordPattern> augmentedPatterns =
|
Set<Symbol> result = new HashSet<>();
|
||||||
deconstructionPatternsByType.getOrDefault(type, List.nil())
|
s.owner.members()
|
||||||
.prepend(dpat);
|
.getSymbols(sym -> sym.kind == Kind.VAR && sym.isEnum())
|
||||||
|
.forEach(result::add);
|
||||||
deconstructionPatternsByType.put(type, augmentedPatterns);
|
return result;
|
||||||
}
|
}).remove(s);
|
||||||
|
|
||||||
default -> {
|
|
||||||
Assert.check(labelValue instanceof JCExpression, labelValue.getTag().name());
|
|
||||||
JCExpression expr = (JCExpression) labelValue;
|
|
||||||
if (expr.hasTag(IDENT) && ((JCIdent) expr).sym.isEnum())
|
|
||||||
coveredSymbols.add(((JCIdent) expr).sym);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Entry<UniqueType, List<JCRecordPattern>> e : deconstructionPatternsByType.entrySet()) {
|
|
||||||
if (e.getValue().stream().anyMatch(r -> r.nested.size() != r.record.getRecordComponents().size())) {
|
|
||||||
coveredSymbols.add(syms.errSymbol);
|
|
||||||
} else if (coversDeconstructionFromComponent(pos, e.getKey().type, e.getValue(), 0)) {
|
|
||||||
coveredSymbols.add(e.getKey().type.tsym);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return coveredSymbols;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean coversDeconstructionFromComponent(DiagnosticPosition pos,
|
|
||||||
Type recordType,
|
|
||||||
List<JCRecordPattern> deconstructionPatterns,
|
|
||||||
int component) {
|
|
||||||
//Given a set of record patterns for the same record, and a starting component,
|
|
||||||
//this method checks, whether the nested patterns for the components are exhaustive,
|
|
||||||
//i.e. represent all possible combinations.
|
|
||||||
//This is done by categorizing the patterns based on the type covered by the given
|
|
||||||
//starting component.
|
|
||||||
//For each such category, it is then checked if the nested patterns starting at the next
|
|
||||||
//component are exhaustive, by recursivelly invoking this method. If these nested patterns
|
|
||||||
//are exhaustive, the given covered type is accepted.
|
|
||||||
//All such covered types are then checked whether they cover the declared type of
|
|
||||||
//the starting component's declaration. If yes, the given set of patterns starting at
|
|
||||||
//the given component cover the given record exhaustivelly, and true is returned.
|
|
||||||
List<? extends RecordComponent> components =
|
|
||||||
deconstructionPatterns.head.record.getRecordComponents();
|
|
||||||
|
|
||||||
if (components.size() == component) {
|
|
||||||
//no components remain to be checked:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//for the first tested component, gather symbols covered by the nested patterns:
|
|
||||||
Type instantiatedComponentType = types.memberType(recordType, components.get(component));
|
|
||||||
List<JCPattern> nestedComponentPatterns = deconstructionPatterns.map(d -> d.nested.get(component));
|
|
||||||
Set<Symbol> coveredSymbolsForComponent = coveredSymbols(pos,
|
|
||||||
nestedComponentPatterns);
|
|
||||||
|
|
||||||
//for each of the symbols covered by the starting component, find all deconstruction patterns
|
|
||||||
//that have the given type, or its supertype, as a type of the starting nested pattern:
|
|
||||||
Map<Symbol, List<JCRecordPattern>> coveredSymbol2Patterns = new HashMap<>();
|
|
||||||
|
|
||||||
for (JCRecordPattern deconstructionPattern : deconstructionPatterns) {
|
|
||||||
JCPattern nestedPattern = deconstructionPattern.nested.get(component);
|
|
||||||
Symbol componentPatternType;
|
|
||||||
switch (nestedPattern.getTag()) {
|
|
||||||
case BINDINGPATTERN, PARENTHESIZEDPATTERN -> {
|
|
||||||
Type primaryPatternType =
|
|
||||||
TreeInfo.primaryPatternType(nestedPattern);
|
|
||||||
componentPatternType = primaryPatternType.tsym;
|
|
||||||
}
|
|
||||||
case RECORDPATTERN -> {
|
|
||||||
componentPatternType = ((JCRecordPattern) nestedPattern).record;
|
|
||||||
}
|
|
||||||
default -> {
|
|
||||||
throw Assert.error("Unexpected tree kind: " + nestedPattern.getTag());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Symbol currentType : coveredSymbolsForComponent) {
|
|
||||||
if (types.isSubtype(types.erasure(currentType.type),
|
|
||||||
types.erasure(componentPatternType.type))) {
|
|
||||||
coveredSymbol2Patterns.put(currentType,
|
|
||||||
coveredSymbol2Patterns.getOrDefault(currentType,
|
|
||||||
List.nil())
|
|
||||||
.prepend(deconstructionPattern));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check the components following the starting component, for each of the covered symbol,
|
|
||||||
//if they are exhaustive. If yes, the given covered symbol should be part of the following
|
|
||||||
//exhaustiveness check:
|
|
||||||
Set<Symbol> covered = new HashSet<>();
|
|
||||||
|
|
||||||
for (Entry<Symbol, List<JCRecordPattern>> e : coveredSymbol2Patterns.entrySet()) {
|
|
||||||
if (coversDeconstructionFromComponent(pos, recordType, e.getValue(), component + 1)) {
|
|
||||||
covered.add(e.getKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//verify whether the filtered symbols cover the given record's declared type:
|
|
||||||
return isExhaustive(pos, instantiatedComponentType, covered);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void transitiveCovers(DiagnosticPosition pos, Type seltype, Set<Symbol> covered) {
|
|
||||||
List<Symbol> todo = List.from(covered);
|
|
||||||
while (todo.nonEmpty()) {
|
|
||||||
Symbol sym = todo.head;
|
|
||||||
todo = todo.tail;
|
|
||||||
switch (sym.kind) {
|
|
||||||
case VAR -> {
|
|
||||||
Iterable<Symbol> constants = sym.owner
|
|
||||||
.members()
|
|
||||||
.getSymbols(s -> s.isEnum() &&
|
|
||||||
s.kind == VAR);
|
|
||||||
boolean hasAll = StreamSupport.stream(constants.spliterator(), false)
|
|
||||||
.allMatch(covered::contains);
|
|
||||||
|
|
||||||
if (hasAll && covered.add(sym.owner)) {
|
|
||||||
todo = todo.prepend(sym.owner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case TYP -> {
|
|
||||||
for (Type sup : types.directSupertypes(sym.type)) {
|
|
||||||
if (sup.tsym.kind == TYP) {
|
|
||||||
if (isTransitivelyCovered(pos, seltype, sup.tsym, covered) &&
|
|
||||||
covered.add(sup.tsym)) {
|
|
||||||
todo = todo.prepend(sup.tsym);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
for (Entry<Symbol, Set<Symbol>> e : enum2Constants.entrySet()) {
|
||||||
|
if (e.getValue().isEmpty()) {
|
||||||
private boolean isTransitivelyCovered(DiagnosticPosition pos, Type seltype,
|
patternSet.add(new BindingPattern(e.getKey().type));
|
||||||
Symbol sealed, Set<Symbol> covered) {
|
}
|
||||||
|
}
|
||||||
|
List<PatternDescription> patterns = List.from(patternSet);
|
||||||
try {
|
try {
|
||||||
if (covered.stream().anyMatch(c -> sealed.isSubClass(c, types)))
|
boolean repeat = true;
|
||||||
return true;
|
while (repeat) {
|
||||||
if (sealed.kind == TYP && sealed.isAbstract() && sealed.isSealed()) {
|
List<PatternDescription> updatedPatterns;
|
||||||
return ((ClassSymbol) sealed).permitted
|
updatedPatterns = reduceBindingPatterns(selector.type, patterns);
|
||||||
.stream()
|
updatedPatterns = reduceNestedPatterns(updatedPatterns);
|
||||||
.filter(s -> {
|
updatedPatterns = reduceRecordPatterns(updatedPatterns);
|
||||||
return types.isCastable(seltype, s.type/*, types.noWarnings*/);
|
repeat = updatedPatterns != patterns;
|
||||||
})
|
patterns = updatedPatterns;
|
||||||
.allMatch(s -> isTransitivelyCovered(pos, seltype, s, covered));
|
if (checkCovered(selector.type, patterns)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return checkCovered(selector.type, patterns);
|
||||||
} catch (CompletionFailure cf) {
|
} catch (CompletionFailure cf) {
|
||||||
chk.completionError(pos, cf);
|
chk.completionError(selector.pos(), cf);
|
||||||
return true;
|
return true; //error recovery
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isExhaustive(DiagnosticPosition pos, Type seltype, Set<Symbol> covered) {
|
private boolean checkCovered(Type seltype, List<PatternDescription> patterns) {
|
||||||
transitiveCovers(pos, seltype, covered);
|
for (Type seltypeComponent : components(seltype)) {
|
||||||
|
for (PatternDescription pd : patterns) {
|
||||||
|
if (pd instanceof BindingPattern bp &&
|
||||||
|
types.isSubtype(seltypeComponent, types.erasure(bp.type))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Type> components(Type seltype) {
|
||||||
return switch (seltype.getTag()) {
|
return switch (seltype.getTag()) {
|
||||||
case CLASS -> {
|
case CLASS -> {
|
||||||
if (seltype.isCompound()) {
|
if (seltype.isCompound()) {
|
||||||
if (seltype.isIntersection()) {
|
if (seltype.isIntersection()) {
|
||||||
yield ((Type.IntersectionClassType) seltype).getComponents()
|
yield ((Type.IntersectionClassType) seltype).getComponents()
|
||||||
.stream()
|
.stream()
|
||||||
.anyMatch(t -> isExhaustive(pos, t, covered));
|
.flatMap(t -> components(t).stream())
|
||||||
|
.collect(List.collector());
|
||||||
}
|
}
|
||||||
yield false;
|
yield List.nil();
|
||||||
}
|
}
|
||||||
yield covered.stream()
|
yield List.of(types.erasure(seltype));
|
||||||
.filter(coveredSym -> coveredSym.kind == TYP)
|
|
||||||
.anyMatch(coveredSym -> types.isSubtype(types.erasure(seltype),
|
|
||||||
types.erasure(coveredSym.type)));
|
|
||||||
}
|
|
||||||
case TYPEVAR -> isExhaustive(pos, ((TypeVar) seltype).getUpperBound(), covered);
|
|
||||||
default -> {
|
|
||||||
yield covered.contains(types.erasure(seltype).tsym);
|
|
||||||
}
|
}
|
||||||
|
case TYPEVAR -> components(((TypeVar) seltype).getUpperBound());
|
||||||
|
default -> List.of(types.erasure(seltype));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* In a set of patterns, search for a sub-set of binding patterns that
|
||||||
|
* in combination exhaust their sealed supertype. If such a sub-set
|
||||||
|
* is found, it is removed, and replaced with a binding pattern
|
||||||
|
* for the sealed supertype.
|
||||||
|
*/
|
||||||
|
private List<PatternDescription> reduceBindingPatterns(Type selectorType, List<PatternDescription> patterns) {
|
||||||
|
Set<Symbol> existingBindings = patterns.stream()
|
||||||
|
.filter(pd -> pd instanceof BindingPattern)
|
||||||
|
.map(pd -> ((BindingPattern) pd).type.tsym)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
for (PatternDescription pdOne : patterns) {
|
||||||
|
if (pdOne instanceof BindingPattern bpOne) {
|
||||||
|
Set<PatternDescription> toRemove = new HashSet<>();
|
||||||
|
Set<PatternDescription> toAdd = new HashSet<>();
|
||||||
|
|
||||||
|
for (Type sup : types.directSupertypes(bpOne.type)) {
|
||||||
|
ClassSymbol clazz = (ClassSymbol) sup.tsym;
|
||||||
|
|
||||||
|
if (clazz.isSealed() && clazz.isAbstract() &&
|
||||||
|
//if a binding pattern for clazz already exists, no need to analyze it again:
|
||||||
|
!existingBindings.contains(clazz)) {
|
||||||
|
ListBuffer<PatternDescription> bindings = new ListBuffer<>();
|
||||||
|
//do not reduce to types unrelated to the selector type:
|
||||||
|
Type clazzErasure = types.erasure(clazz.type);
|
||||||
|
if (components(selectorType).stream()
|
||||||
|
.map(types::erasure)
|
||||||
|
.noneMatch(c -> types.isSubtype(clazzErasure, c))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Symbol> permitted = allPermittedSubTypes(clazz, csym -> {
|
||||||
|
Type instantiated;
|
||||||
|
if (csym.type.allparams().isEmpty()) {
|
||||||
|
instantiated = csym.type;
|
||||||
|
} else {
|
||||||
|
instantiated = infer.instantiatePatternType(selectorType, csym);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instantiated != null && types.isCastable(selectorType, instantiated);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (PatternDescription pdOther : patterns) {
|
||||||
|
if (pdOther instanceof BindingPattern bpOther) {
|
||||||
|
boolean reduces = false;
|
||||||
|
Set<Symbol> currentPermittedSubTypes =
|
||||||
|
allPermittedSubTypes((ClassSymbol) bpOther.type.tsym, s -> true);
|
||||||
|
|
||||||
|
PERMITTED: for (Iterator<Symbol> it = permitted.iterator(); it.hasNext();) {
|
||||||
|
Symbol perm = it.next();
|
||||||
|
|
||||||
|
for (Symbol currentPermitted : currentPermittedSubTypes) {
|
||||||
|
if (types.isSubtype(types.erasure(currentPermitted.type),
|
||||||
|
types.erasure(perm.type))) {
|
||||||
|
it.remove();
|
||||||
|
continue PERMITTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (types.isSubtype(types.erasure(perm.type),
|
||||||
|
types.erasure(bpOther.type))) {
|
||||||
|
it.remove();
|
||||||
|
reduces = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reduces) {
|
||||||
|
bindings.append(pdOther);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permitted.isEmpty()) {
|
||||||
|
toRemove.addAll(bindings);
|
||||||
|
toAdd.add(new BindingPattern(clazz.type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!toAdd.isEmpty() || !toRemove.isEmpty()) {
|
||||||
|
for (PatternDescription pd : toRemove) {
|
||||||
|
patterns = List.filter(patterns, pd);
|
||||||
|
}
|
||||||
|
for (PatternDescription pd : toAdd) {
|
||||||
|
patterns = patterns.prepend(pd);
|
||||||
|
}
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Symbol> allPermittedSubTypes(ClassSymbol root, Predicate<ClassSymbol> accept) {
|
||||||
|
Set<Symbol> permitted = new HashSet<>();
|
||||||
|
List<ClassSymbol> permittedSubtypesClosure = List.of(root);
|
||||||
|
|
||||||
|
while (permittedSubtypesClosure.nonEmpty()) {
|
||||||
|
ClassSymbol current = permittedSubtypesClosure.head;
|
||||||
|
|
||||||
|
permittedSubtypesClosure = permittedSubtypesClosure.tail;
|
||||||
|
|
||||||
|
if (current.isSealed() && current.isAbstract()) {
|
||||||
|
for (Symbol sym : current.permitted) {
|
||||||
|
ClassSymbol csym = (ClassSymbol) sym;
|
||||||
|
|
||||||
|
if (accept.test(csym)) {
|
||||||
|
permittedSubtypesClosure = permittedSubtypesClosure.prepend(csym);
|
||||||
|
permitted.add(csym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return permitted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Among the set of patterns, find sub-set of patterns such:
|
||||||
|
* $record($prefix$, $nested, $suffix$)
|
||||||
|
* Where $record, $prefix$ and $suffix$ is the same for each pattern
|
||||||
|
* in the set, and the patterns only differ in one "column" in
|
||||||
|
* the $nested pattern.
|
||||||
|
* Then, the set of $nested patterns is taken, and passed recursively
|
||||||
|
* to reduceNestedPatterns and to reduceBindingPatterns, to
|
||||||
|
* simplify the pattern. If that succeeds, the original found sub-set
|
||||||
|
* of patterns is replaced with a new set of patterns of the form:
|
||||||
|
* $record($prefix$, $resultOfReduction, $suffix$)
|
||||||
|
*/
|
||||||
|
private List<PatternDescription> reduceNestedPatterns(List<PatternDescription> patterns) {
|
||||||
|
/* implementation note:
|
||||||
|
* finding a sub-set of patterns that only differ in a single
|
||||||
|
* column is time-consuming task, so this method speeds it up by:
|
||||||
|
* - group the patterns by their record class
|
||||||
|
* - for each column (nested pattern) do:
|
||||||
|
* -- group patterns by their hash
|
||||||
|
* -- in each such by-hash group, find sub-sets that only differ in
|
||||||
|
* the chosen column, and then call reduceBindingPatterns and reduceNestedPatterns
|
||||||
|
* on patterns in the chosen column, as described above
|
||||||
|
*/
|
||||||
|
var groupByRecordClass =
|
||||||
|
patterns.stream()
|
||||||
|
.filter(pd -> pd instanceof RecordPattern)
|
||||||
|
.map(pd -> (RecordPattern) pd)
|
||||||
|
.collect(groupingBy(pd -> (ClassSymbol) pd.recordType.tsym));
|
||||||
|
|
||||||
|
for (var e : groupByRecordClass.entrySet()) {
|
||||||
|
int nestedPatternsCount = e.getKey().getRecordComponents().size();
|
||||||
|
|
||||||
|
for (int mismatchingCandidate = 0;
|
||||||
|
mismatchingCandidate < nestedPatternsCount;
|
||||||
|
mismatchingCandidate++) {
|
||||||
|
int mismatchingCandidateFin = mismatchingCandidate;
|
||||||
|
var groupByHashes =
|
||||||
|
e.getValue()
|
||||||
|
.stream()
|
||||||
|
//error recovery, ignore patterns with incorrect number of nested patterns:
|
||||||
|
.filter(pd -> pd.nested.length == nestedPatternsCount)
|
||||||
|
.collect(groupingBy(pd -> pd.hashCode(mismatchingCandidateFin)));
|
||||||
|
for (var candidates : groupByHashes.values()) {
|
||||||
|
var candidatesArr = candidates.toArray(RecordPattern[]::new);
|
||||||
|
|
||||||
|
for (int firstCandidate = 0;
|
||||||
|
firstCandidate < candidatesArr.length;
|
||||||
|
firstCandidate++) {
|
||||||
|
RecordPattern rpOne = candidatesArr[firstCandidate];
|
||||||
|
ListBuffer<RecordPattern> join = new ListBuffer<>();
|
||||||
|
|
||||||
|
join.append(rpOne);
|
||||||
|
|
||||||
|
NEXT_PATTERN: for (int nextCandidate = 0;
|
||||||
|
nextCandidate < candidatesArr.length;
|
||||||
|
nextCandidate++) {
|
||||||
|
if (firstCandidate == nextCandidate) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordPattern rpOther = candidatesArr[nextCandidate];
|
||||||
|
if (rpOne.recordType.tsym == rpOther.recordType.tsym) {
|
||||||
|
for (int i = 0; i < rpOne.nested.length; i++) {
|
||||||
|
if (i != mismatchingCandidate &&
|
||||||
|
!rpOne.nested[i].equals(rpOther.nested[i])) {
|
||||||
|
continue NEXT_PATTERN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
join.append(rpOther);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var nestedPatterns = join.stream().map(rp -> rp.nested[mismatchingCandidateFin]).collect(List.collector());
|
||||||
|
var updatedPatterns = reduceNestedPatterns(nestedPatterns);
|
||||||
|
|
||||||
|
updatedPatterns = reduceRecordPatterns(updatedPatterns);
|
||||||
|
updatedPatterns = reduceBindingPatterns(rpOne.fullComponentTypes()[mismatchingCandidateFin], updatedPatterns);
|
||||||
|
|
||||||
|
if (nestedPatterns != updatedPatterns) {
|
||||||
|
ListBuffer<PatternDescription> result = new ListBuffer<>();
|
||||||
|
Set<PatternDescription> toRemove = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||||
|
|
||||||
|
toRemove.addAll(join);
|
||||||
|
|
||||||
|
for (PatternDescription p : patterns) {
|
||||||
|
if (!toRemove.contains(p)) {
|
||||||
|
result.append(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PatternDescription nested : updatedPatterns) {
|
||||||
|
PatternDescription[] newNested =
|
||||||
|
Arrays.copyOf(rpOne.nested, rpOne.nested.length);
|
||||||
|
newNested[mismatchingCandidateFin] = nested;
|
||||||
|
result.append(new RecordPattern(rpOne.recordType(),
|
||||||
|
rpOne.fullComponentTypes(),
|
||||||
|
newNested));
|
||||||
|
}
|
||||||
|
return result.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* In the set of patterns, find those for which, given:
|
||||||
|
* $record($nested1, $nested2, ...)
|
||||||
|
* all the $nestedX pattern cover the given record component,
|
||||||
|
* and replace those with a simple binding pattern over $record.
|
||||||
|
*/
|
||||||
|
private List<PatternDescription> reduceRecordPatterns(List<PatternDescription> patterns) {
|
||||||
|
var newPatterns = new ListBuffer<PatternDescription>();
|
||||||
|
boolean modified = false;
|
||||||
|
for (PatternDescription pd : patterns) {
|
||||||
|
if (pd instanceof RecordPattern rpOne) {
|
||||||
|
PatternDescription reducedPattern = reduceRecordPattern(rpOne);
|
||||||
|
if (reducedPattern != rpOne) {
|
||||||
|
newPatterns.append(reducedPattern);
|
||||||
|
modified = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newPatterns.append(pd);
|
||||||
|
}
|
||||||
|
return modified ? newPatterns.toList() : patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PatternDescription reduceRecordPattern(PatternDescription pattern) {
|
||||||
|
if (pattern instanceof RecordPattern rpOne) {
|
||||||
|
Type[] componentType = rpOne.fullComponentTypes();
|
||||||
|
//error recovery, ignore patterns with incorrect number of nested patterns:
|
||||||
|
if (componentType.length != rpOne.nested.length) {
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
PatternDescription[] reducedNestedPatterns = null;
|
||||||
|
boolean covered = true;
|
||||||
|
for (int i = 0; i < componentType.length; i++) {
|
||||||
|
PatternDescription newNested = reduceRecordPattern(rpOne.nested[i]);
|
||||||
|
if (newNested != rpOne.nested[i]) {
|
||||||
|
if (reducedNestedPatterns == null) {
|
||||||
|
reducedNestedPatterns = Arrays.copyOf(rpOne.nested, rpOne.nested.length);
|
||||||
|
}
|
||||||
|
reducedNestedPatterns[i] = newNested;
|
||||||
|
}
|
||||||
|
|
||||||
|
covered &= newNested instanceof BindingPattern bp &&
|
||||||
|
types.isSubtype(types.erasure(componentType[i]), types.erasure(bp.type));
|
||||||
|
}
|
||||||
|
if (covered) {
|
||||||
|
return new BindingPattern(rpOne.recordType);
|
||||||
|
} else if (reducedNestedPatterns != null) {
|
||||||
|
return new RecordPattern(rpOne.recordType, rpOne.fullComponentTypes(), reducedNestedPatterns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
|
||||||
public void visitTry(JCTry tree) {
|
public void visitTry(JCTry tree) {
|
||||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||||
pendingExits = new ListBuffer<>();
|
pendingExits = new ListBuffer<>();
|
||||||
@ -1374,11 +1598,7 @@ public class Flow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||||
if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) {
|
visitVarDef(tree.var);
|
||||||
visitVarDef(jcVariableDecl);
|
|
||||||
} else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
|
|
||||||
visitRecordPattern(jcRecordPattern);
|
|
||||||
}
|
|
||||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||||
scan(tree.expr);
|
scan(tree.expr);
|
||||||
pendingExits = new ListBuffer<>();
|
pendingExits = new ListBuffer<>();
|
||||||
@ -2177,10 +2397,6 @@ public class Flow {
|
|||||||
|
|
||||||
void scanPattern(JCTree tree) {
|
void scanPattern(JCTree tree) {
|
||||||
scan(tree);
|
scan(tree);
|
||||||
if (inits.isReset()) {
|
|
||||||
inits.assign(initsWhenTrue);
|
|
||||||
uninits.assign(uninitsWhenTrue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse)
|
/** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse)
|
||||||
@ -2559,6 +2775,8 @@ public class Flow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||||
|
visitVarDef(tree.var);
|
||||||
|
|
||||||
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
ListBuffer<PendingExit> prevPendingExits = pendingExits;
|
||||||
FlowKind prevFlowKind = flowKind;
|
FlowKind prevFlowKind = flowKind;
|
||||||
flowKind = FlowKind.NORMAL;
|
flowKind = FlowKind.NORMAL;
|
||||||
@ -2567,13 +2785,7 @@ public class Flow {
|
|||||||
final Bits initsStart = new Bits(inits);
|
final Bits initsStart = new Bits(inits);
|
||||||
final Bits uninitsStart = new Bits(uninits);
|
final Bits uninitsStart = new Bits(uninits);
|
||||||
|
|
||||||
if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) {
|
letInit(tree.pos(), tree.var.sym);
|
||||||
visitVarDef(jcVariableDecl);
|
|
||||||
letInit(tree.pos(), jcVariableDecl.sym);
|
|
||||||
} else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
|
|
||||||
visitRecordPattern(jcRecordPattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingExits = new ListBuffer<>();
|
pendingExits = new ListBuffer<>();
|
||||||
int prevErrors = log.nerrors;
|
int prevErrors = log.nerrors;
|
||||||
do {
|
do {
|
||||||
@ -2625,17 +2837,10 @@ public class Flow {
|
|||||||
for (JCCaseLabel pat : c.labels) {
|
for (JCCaseLabel pat : c.labels) {
|
||||||
scanPattern(pat);
|
scanPattern(pat);
|
||||||
}
|
}
|
||||||
if (l.head.stats.isEmpty() &&
|
scan(c.guard);
|
||||||
l.tail.nonEmpty() &&
|
if (inits.isReset()) {
|
||||||
l.tail.head.labels.size() == 1 &&
|
inits.assign(initsWhenTrue);
|
||||||
TreeInfo.isNullCaseLabel(l.tail.head.labels.head)) {
|
uninits.assign(uninitsWhenTrue);
|
||||||
//handling:
|
|
||||||
//case Integer i:
|
|
||||||
//case null:
|
|
||||||
//joining these two cases together - processing Integer i pattern,
|
|
||||||
//but statements from case null:
|
|
||||||
l = l.tail;
|
|
||||||
c = l.head;
|
|
||||||
}
|
}
|
||||||
scan(c.stats);
|
scan(c.stats);
|
||||||
if (c.completesNormally && c.caseKind == JCCase.RULE) {
|
if (c.completesNormally && c.caseKind == JCCase.RULE) {
|
||||||
@ -3028,12 +3233,6 @@ public class Flow {
|
|||||||
initParam(tree.var);
|
initParam(tree.var);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
|
||||||
scan(tree.pat);
|
|
||||||
scan(tree.guard);
|
|
||||||
}
|
|
||||||
|
|
||||||
void referenced(Symbol sym) {
|
void referenced(Symbol sym) {
|
||||||
unrefdResources.remove(sym);
|
unrefdResources.remove(sym);
|
||||||
}
|
}
|
||||||
@ -3104,6 +3303,7 @@ public class Flow {
|
|||||||
class CaptureAnalyzer extends BaseAnalyzer {
|
class CaptureAnalyzer extends BaseAnalyzer {
|
||||||
|
|
||||||
JCTree currentTree; //local class or lambda
|
JCTree currentTree; //local class or lambda
|
||||||
|
WriteableScope declaredInsideGuard;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void markDead() {
|
void markDead() {
|
||||||
@ -3117,7 +3317,7 @@ public class Flow {
|
|||||||
sym.pos < currentTree.getStartPosition()) {
|
sym.pos < currentTree.getStartPosition()) {
|
||||||
switch (currentTree.getTag()) {
|
switch (currentTree.getTag()) {
|
||||||
case CLASSDEF:
|
case CLASSDEF:
|
||||||
case PATTERNCASELABEL:
|
case CASE:
|
||||||
case LAMBDA:
|
case LAMBDA:
|
||||||
if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) {
|
if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) {
|
||||||
reportEffectivelyFinalError(pos, sym);
|
reportEffectivelyFinalError(pos, sym);
|
||||||
@ -3131,15 +3331,20 @@ public class Flow {
|
|||||||
tree = TreeInfo.skipParens(tree);
|
tree = TreeInfo.skipParens(tree);
|
||||||
if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) {
|
if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) {
|
||||||
Symbol sym = TreeInfo.symbol(tree);
|
Symbol sym = TreeInfo.symbol(tree);
|
||||||
if (currentTree != null &&
|
if (currentTree != null) {
|
||||||
sym.kind == VAR &&
|
|
||||||
sym.owner.kind == MTH &&
|
|
||||||
((VarSymbol)sym).pos < currentTree.getStartPosition()) {
|
|
||||||
switch (currentTree.getTag()) {
|
switch (currentTree.getTag()) {
|
||||||
case CLASSDEF:
|
case CLASSDEF, LAMBDA -> {
|
||||||
case CASE:
|
if (sym.kind == VAR &&
|
||||||
case LAMBDA:
|
sym.owner.kind == MTH &&
|
||||||
reportEffectivelyFinalError(tree, sym);
|
((VarSymbol)sym).pos < currentTree.getStartPosition()) {
|
||||||
|
reportEffectivelyFinalError(tree, sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case CASE -> {
|
||||||
|
if (!declaredInsideGuard.includes(sym)) {
|
||||||
|
log.error(tree.pos(), Errors.CannotAssignNotDeclaredGuard(sym));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3148,7 +3353,7 @@ public class Flow {
|
|||||||
void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) {
|
void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) {
|
||||||
Fragment subKey = switch (currentTree.getTag()) {
|
Fragment subKey = switch (currentTree.getTag()) {
|
||||||
case LAMBDA -> Fragments.Lambda;
|
case LAMBDA -> Fragments.Lambda;
|
||||||
case PATTERNCASELABEL -> Fragments.Guard;
|
case CASE -> Fragments.Guard;
|
||||||
case CLASSDEF -> Fragments.InnerCls;
|
case CLASSDEF -> Fragments.InnerCls;
|
||||||
default -> throw new AssertionError("Unexpected tree kind: " + currentTree.getTag());
|
default -> throw new AssertionError("Unexpected tree kind: " + currentTree.getTag());
|
||||||
};
|
};
|
||||||
@ -3188,20 +3393,21 @@ public class Flow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
|
public void visitCase(JCCase tree) {
|
||||||
scan(tree.pattern);
|
scan(tree.labels);
|
||||||
}
|
if (tree.guard != null) {
|
||||||
|
JCTree prevTree = currentTree;
|
||||||
@Override
|
WriteableScope prevDeclaredInsideGuard = declaredInsideGuard;
|
||||||
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
try {
|
||||||
scan(tree.pat);
|
currentTree = tree;
|
||||||
JCTree prevTree = currentTree;
|
declaredInsideGuard = WriteableScope.create(attrEnv.enclClass.sym);
|
||||||
try {
|
scan(tree.guard);
|
||||||
currentTree = tree;
|
} finally {
|
||||||
scan(tree.guard);
|
currentTree = prevTree;
|
||||||
} finally {
|
declaredInsideGuard = prevDeclaredInsideGuard;
|
||||||
currentTree = prevTree;
|
}
|
||||||
}
|
}
|
||||||
|
scan(tree.stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -3256,6 +3462,14 @@ public class Flow {
|
|||||||
super.visitTry(tree);
|
super.visitTry(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitVarDef(JCVariableDecl tree) {
|
||||||
|
if (declaredInsideGuard != null) {
|
||||||
|
declaredInsideGuard.enter(tree.sym);
|
||||||
|
}
|
||||||
|
super.visitVarDef(tree);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitYield(JCYield tree) {
|
public void visitYield(JCYield tree) {
|
||||||
scan(tree.value);
|
scan(tree.value);
|
||||||
|
@ -703,7 +703,7 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
JCBreak br = make.Break(null);
|
JCBreak br = make.Break(null);
|
||||||
breaks.add(br);
|
breaks.add(br);
|
||||||
List<JCStatement> stmts = entry.getValue().append(br).toList();
|
List<JCStatement> stmts = entry.getValue().append(br).toList();
|
||||||
cases.add(make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(make.Literal(entry.getKey()))), stmts, null));
|
cases.add(make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(make.Literal(entry.getKey()))), null, stmts, null));
|
||||||
}
|
}
|
||||||
JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
|
JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
|
||||||
for (JCBreak br : breaks) {
|
for (JCBreak br : breaks) {
|
||||||
|
@ -28,7 +28,6 @@ package com.sun.tools.javac.comp;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.sun.source.tree.EnhancedForLoopTree;
|
|
||||||
import com.sun.tools.javac.code.*;
|
import com.sun.tools.javac.code.*;
|
||||||
import com.sun.tools.javac.code.Kinds.KindSelector;
|
import com.sun.tools.javac.code.Kinds.KindSelector;
|
||||||
import com.sun.tools.javac.code.Scope.WriteableScope;
|
import com.sun.tools.javac.code.Scope.WriteableScope;
|
||||||
@ -3558,18 +3557,13 @@ public class Lower extends TreeTranslator {
|
|||||||
Type elemtype = types.elemtype(tree.expr.type);
|
Type elemtype = types.elemtype(tree.expr.type);
|
||||||
JCExpression loopvarinit = make.Indexed(make.Ident(arraycache),
|
JCExpression loopvarinit = make.Indexed(make.Ident(arraycache),
|
||||||
make.Ident(index)).setType(elemtype);
|
make.Ident(index)).setType(elemtype);
|
||||||
|
JCVariableDecl loopvardef = (JCVariableDecl)make.VarDef(tree.var.mods,
|
||||||
Assert.check(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE);
|
tree.var.name,
|
||||||
JCVariableDecl jcVariableDecl = (JCVariableDecl) tree.varOrRecordPattern;
|
tree.var.vartype,
|
||||||
|
loopvarinit).setType(tree.var.type);
|
||||||
JCVariableDecl loopvardef = (JCVariableDecl)make.VarDef(jcVariableDecl.mods,
|
loopvardef.sym = tree.var.sym;
|
||||||
jcVariableDecl.name,
|
|
||||||
jcVariableDecl.vartype,
|
|
||||||
loopvarinit).setType(jcVariableDecl.type);
|
|
||||||
loopvardef.sym = jcVariableDecl.sym;
|
|
||||||
JCBlock body = make.
|
JCBlock body = make.
|
||||||
Block(0, List.of(loopvardef, tree.body));
|
Block(0, List.of(loopvardef, tree.body));
|
||||||
|
|
||||||
|
|
||||||
result = translate(make.
|
result = translate(make.
|
||||||
ForLoop(loopinit,
|
ForLoop(loopinit,
|
||||||
@ -3648,26 +3642,22 @@ public class Lower extends TreeTranslator {
|
|||||||
itvar.type,
|
itvar.type,
|
||||||
List.nil());
|
List.nil());
|
||||||
JCExpression vardefinit = make.App(make.Select(make.Ident(itvar), next));
|
JCExpression vardefinit = make.App(make.Select(make.Ident(itvar), next));
|
||||||
|
if (tree.var.type.isPrimitive())
|
||||||
Assert.check(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE);
|
|
||||||
|
|
||||||
JCVariableDecl var = (JCVariableDecl) tree.varOrRecordPattern;
|
|
||||||
if (var.type.isPrimitive())
|
|
||||||
vardefinit = make.TypeCast(types.cvarUpperBound(iteratorTarget), vardefinit);
|
vardefinit = make.TypeCast(types.cvarUpperBound(iteratorTarget), vardefinit);
|
||||||
else
|
else
|
||||||
vardefinit = make.TypeCast(var.type, vardefinit);
|
vardefinit = make.TypeCast(tree.var.type, vardefinit);
|
||||||
JCVariableDecl indexDef = (JCVariableDecl) make.VarDef(var.mods,
|
JCVariableDecl indexDef = (JCVariableDecl)make.VarDef(tree.var.mods,
|
||||||
var.name,
|
tree.var.name,
|
||||||
var.vartype,
|
tree.var.vartype,
|
||||||
vardefinit).setType(var.type);
|
vardefinit).setType(tree.var.type);
|
||||||
indexDef.sym = var.sym;
|
indexDef.sym = tree.var.sym;
|
||||||
JCBlock body = make.Block(0, List.of(indexDef, tree.body));
|
JCBlock body = make.Block(0, List.of(indexDef, tree.body));
|
||||||
body.endpos = TreeInfo.endPos(tree.body);
|
body.endpos = TreeInfo.endPos(tree.body);
|
||||||
result = translate(make.
|
result = translate(make.
|
||||||
ForLoop(List.of(init),
|
ForLoop(List.of(init),
|
||||||
cond,
|
cond,
|
||||||
List.nil(),
|
List.nil(),
|
||||||
body));
|
body));
|
||||||
patchTargets(body, tree, result);
|
patchTargets(body, tree, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3753,7 +3743,7 @@ public class Lower extends TreeTranslator {
|
|||||||
List<JCExpression> params = matchException ? List.of(makeNull(), makeNull())
|
List<JCExpression> params = matchException ? List.of(makeNull(), makeNull())
|
||||||
: List.nil();
|
: List.nil();
|
||||||
JCThrow thr = make.Throw(makeNewClass(exception, params));
|
JCThrow thr = make.Throw(makeNewClass(exception, params));
|
||||||
JCCase c = make.Case(JCCase.STATEMENT, List.of(make.DefaultCaseLabel()), List.of(thr), null);
|
JCCase c = make.Case(JCCase.STATEMENT, List.of(make.DefaultCaseLabel()), null, List.of(thr), null);
|
||||||
cases = cases.prepend(c);
|
cases = cases.prepend(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3780,6 +3770,7 @@ public class Lower extends TreeTranslator {
|
|||||||
while (patterns.tail.nonEmpty()) {
|
while (patterns.tail.nonEmpty()) {
|
||||||
convertedCases.append(make_at(c.pos()).Case(JCCase.STATEMENT,
|
convertedCases.append(make_at(c.pos()).Case(JCCase.STATEMENT,
|
||||||
List.of(patterns.head),
|
List.of(patterns.head),
|
||||||
|
null,
|
||||||
List.nil(),
|
List.nil(),
|
||||||
null));
|
null));
|
||||||
patterns = patterns.tail;
|
patterns = patterns.tail;
|
||||||
@ -3874,7 +3865,7 @@ public class Lower extends TreeTranslator {
|
|||||||
VarSymbol label = (VarSymbol)TreeInfo.symbol(((JCConstantCaseLabel) c.labels.head).expr);
|
VarSymbol label = (VarSymbol)TreeInfo.symbol(((JCConstantCaseLabel) c.labels.head).expr);
|
||||||
pat = map.caseValue(label);
|
pat = map.caseValue(label);
|
||||||
}
|
}
|
||||||
newCases.append(make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(pat)), c.stats, null));
|
newCases.append(make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(pat)), null, c.stats, null));
|
||||||
} else {
|
} else {
|
||||||
newCases.append(c);
|
newCases.append(c);
|
||||||
}
|
}
|
||||||
@ -4046,6 +4037,7 @@ public class Lower extends TreeTranslator {
|
|||||||
|
|
||||||
caseBuffer.append(make.Case(JCCase.STATEMENT,
|
caseBuffer.append(make.Case(JCCase.STATEMENT,
|
||||||
List.of(make.ConstantCaseLabel(make.Literal(hashCode))),
|
List.of(make.ConstantCaseLabel(make.Literal(hashCode))),
|
||||||
|
null,
|
||||||
lb.toList(),
|
lb.toList(),
|
||||||
null));
|
null));
|
||||||
}
|
}
|
||||||
@ -4081,6 +4073,7 @@ public class Lower extends TreeTranslator {
|
|||||||
|
|
||||||
lb.append(make.Case(JCCase.STATEMENT, caseExpr == null ? List.of(make.DefaultCaseLabel())
|
lb.append(make.Case(JCCase.STATEMENT, caseExpr == null ? List.of(make.DefaultCaseLabel())
|
||||||
: List.of(make.ConstantCaseLabel(caseExpr)),
|
: List.of(make.ConstantCaseLabel(caseExpr)),
|
||||||
|
null,
|
||||||
oneCase.stats, null));
|
oneCase.stats, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ public class MatchBindingsComputer extends TreeScanner {
|
|||||||
public MatchBindings finishBindings(JCTree tree, MatchBindings matchBindings) {
|
public MatchBindings finishBindings(JCTree tree, MatchBindings matchBindings) {
|
||||||
switch (tree.getTag()) {
|
switch (tree.getTag()) {
|
||||||
case NOT: case AND: case OR: case BINDINGPATTERN:
|
case NOT: case AND: case OR: case BINDINGPATTERN:
|
||||||
case PARENTHESIZEDPATTERN: case TYPETEST:
|
case TYPETEST:
|
||||||
case PARENS: case RECORDPATTERN:
|
case PARENS: case RECORDPATTERN:
|
||||||
case CONDEXPR: //error recovery:
|
case CONDEXPR: //error recovery:
|
||||||
return matchBindings;
|
return matchBindings;
|
||||||
|
@ -957,10 +957,6 @@ class ThisEscapeAnalyzer extends TreeScanner {
|
|||||||
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitRecordPattern(JCRecordPattern that) {
|
public void visitRecordPattern(JCRecordPattern that) {
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,8 @@ import com.sun.tools.javac.code.Symbol;
|
|||||||
import com.sun.tools.javac.code.Symbol.BindingSymbol;
|
import com.sun.tools.javac.code.Symbol.BindingSymbol;
|
||||||
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
||||||
import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
|
import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
|
||||||
|
import com.sun.tools.javac.code.Symbol.DynamicVarSymbol;
|
||||||
|
import com.sun.tools.javac.code.Symbol.MethodHandleSymbol;
|
||||||
import com.sun.tools.javac.code.Symbol.VarSymbol;
|
import com.sun.tools.javac.code.Symbol.VarSymbol;
|
||||||
import com.sun.tools.javac.code.Symtab;
|
import com.sun.tools.javac.code.Symtab;
|
||||||
import com.sun.tools.javac.code.Type.ClassType;
|
import com.sun.tools.javac.code.Type.ClassType;
|
||||||
@ -55,7 +57,6 @@ import com.sun.tools.javac.tree.JCTree.JCSwitch;
|
|||||||
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
|
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCBindingPattern;
|
import com.sun.tools.javac.tree.JCTree.JCBindingPattern;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
|
import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCThrow;
|
|
||||||
import com.sun.tools.javac.tree.JCTree.Tag;
|
import com.sun.tools.javac.tree.JCTree.Tag;
|
||||||
import com.sun.tools.javac.tree.TreeMaker;
|
import com.sun.tools.javac.tree.TreeMaker;
|
||||||
import com.sun.tools.javac.tree.TreeTranslator;
|
import com.sun.tools.javac.tree.TreeTranslator;
|
||||||
@ -94,7 +95,6 @@ import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
|
|||||||
import com.sun.tools.javac.tree.JCTree.JCLambda;
|
import com.sun.tools.javac.tree.JCTree.JCLambda;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
|
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCNewClass;
|
import com.sun.tools.javac.tree.JCTree.JCNewClass;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCParenthesizedPattern;
|
|
||||||
import com.sun.tools.javac.tree.JCTree.JCPattern;
|
import com.sun.tools.javac.tree.JCTree.JCPattern;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCPatternCaseLabel;
|
import com.sun.tools.javac.tree.JCTree.JCPatternCaseLabel;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCRecordPattern;
|
import com.sun.tools.javac.tree.JCTree.JCRecordPattern;
|
||||||
@ -105,6 +105,8 @@ import com.sun.tools.javac.tree.JCTree.LetExpr;
|
|||||||
import com.sun.tools.javac.tree.TreeInfo;
|
import com.sun.tools.javac.tree.TreeInfo;
|
||||||
import com.sun.tools.javac.tree.TreeScanner;
|
import com.sun.tools.javac.tree.TreeScanner;
|
||||||
import com.sun.tools.javac.util.Assert;
|
import com.sun.tools.javac.util.Assert;
|
||||||
|
import com.sun.tools.javac.util.JCDiagnostic;
|
||||||
|
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
|
||||||
import com.sun.tools.javac.util.List;
|
import com.sun.tools.javac.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,8 +198,7 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
@Override
|
@Override
|
||||||
public void visitTypeTest(JCInstanceOf tree) {
|
public void visitTypeTest(JCInstanceOf tree) {
|
||||||
if (tree.pattern instanceof JCPattern pattern) {
|
if (tree.pattern instanceof JCPattern pattern) {
|
||||||
//first, resolve any parenthesized and record patterns:
|
//first, resolve any record patterns:
|
||||||
pattern = TreeInfo.skipParens(pattern);
|
|
||||||
JCExpression extraConditions = null;
|
JCExpression extraConditions = null;
|
||||||
if (pattern instanceof JCRecordPattern recordPattern) {
|
if (pattern instanceof JCRecordPattern recordPattern) {
|
||||||
UnrolledRecordPattern unrolledRecordPattern = unrollRecordPattern(recordPattern);
|
UnrolledRecordPattern unrolledRecordPattern = unrollRecordPattern(recordPattern);
|
||||||
@ -280,11 +281,6 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
|
|
||||||
result = translate(tree.pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitRecordPattern(JCRecordPattern tree) {
|
public void visitRecordPattern(JCRecordPattern tree) {
|
||||||
//record patterns should be resolved by the constructs that use them.
|
//record patterns should be resolved by the constructs that use them.
|
||||||
@ -314,7 +310,7 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
while (components.nonEmpty()) {
|
while (components.nonEmpty()) {
|
||||||
RecordComponent component = components.head;
|
RecordComponent component = components.head;
|
||||||
Type componentType = types.erasure(nestedFullComponentTypes.head);
|
Type componentType = types.erasure(nestedFullComponentTypes.head);
|
||||||
JCPattern nestedPattern = TreeInfo.skipParens(nestedPatterns.head);
|
JCPattern nestedPattern = nestedPatterns.head;
|
||||||
JCBindingPattern nestedBinding;
|
JCBindingPattern nestedBinding;
|
||||||
boolean allowNull;
|
boolean allowNull;
|
||||||
if (nestedPattern instanceof JCRecordPattern nestedRecordPattern) {
|
if (nestedPattern instanceof JCRecordPattern nestedRecordPattern) {
|
||||||
@ -390,8 +386,6 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
Type seltype = selector.type.hasTag(BOT)
|
Type seltype = selector.type.hasTag(BOT)
|
||||||
? syms.objectType
|
? syms.objectType
|
||||||
: selector.type;
|
: selector.type;
|
||||||
Assert.check(preview.isEnabled());
|
|
||||||
Assert.check(preview.usesPreview(env.toplevel.sourcefile));
|
|
||||||
|
|
||||||
//rewrite pattern matching switches, performed in several steps:
|
//rewrite pattern matching switches, performed in several steps:
|
||||||
//1. record patterns are unrolled into a binding pattern and guards using unrollRecordPattern
|
//1. record patterns are unrolled into a binding pattern and guards using unrollRecordPattern
|
||||||
@ -436,16 +430,19 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
//note the selector is evaluated only once and stored in a temporary variable
|
//note the selector is evaluated only once and stored in a temporary variable
|
||||||
ListBuffer<JCCase> newCases = new ListBuffer<>();
|
ListBuffer<JCCase> newCases = new ListBuffer<>();
|
||||||
for (List<JCCase> c = cases; c.nonEmpty(); c = c.tail) {
|
for (List<JCCase> c = cases; c.nonEmpty(); c = c.tail) {
|
||||||
c.head.labels = c.head.labels.map(l -> {
|
JCCase cse = c.head;
|
||||||
|
cse.labels = cse.labels.map(l -> {
|
||||||
if (l instanceof JCPatternCaseLabel patternLabel) {
|
if (l instanceof JCPatternCaseLabel patternLabel) {
|
||||||
JCPattern pattern = TreeInfo.skipParens(patternLabel.pat);
|
JCPattern pattern = patternLabel.pat;
|
||||||
if (pattern instanceof JCRecordPattern recordPattern) {
|
if (pattern instanceof JCRecordPattern recordPattern) {
|
||||||
UnrolledRecordPattern deconstructed = unrollRecordPattern(recordPattern);
|
UnrolledRecordPattern deconstructed = unrollRecordPattern(recordPattern);
|
||||||
JCExpression guard = deconstructed.newGuard();
|
JCExpression guard = deconstructed.newGuard();
|
||||||
if (patternLabel.guard != null) {
|
if (cse.guard != null) {
|
||||||
guard = mergeConditions(guard, patternLabel.guard);
|
cse.guard = mergeConditions(guard, cse.guard);
|
||||||
|
} else {
|
||||||
|
cse.guard = guard;
|
||||||
}
|
}
|
||||||
return make.PatternCaseLabel(deconstructed.primaryPattern(), guard);
|
return make.PatternCaseLabel(deconstructed.primaryPattern());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return l;
|
return l;
|
||||||
@ -534,8 +531,8 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
try {
|
try {
|
||||||
currentValue = temp;
|
currentValue = temp;
|
||||||
JCExpression test = (JCExpression) this.<JCTree>translate(label.pat);
|
JCExpression test = (JCExpression) this.<JCTree>translate(label.pat);
|
||||||
if (label.guard != null) {
|
if (c.guard != null) {
|
||||||
JCExpression guard = translate(label.guard);
|
JCExpression guard = translate(c.guard);
|
||||||
test = makeBinary(Tag.AND, test, guard);
|
test = makeBinary(Tag.AND, test, guard);
|
||||||
}
|
}
|
||||||
c.stats = translate(c.stats);
|
c.stats = translate(c.stats);
|
||||||
@ -716,12 +713,12 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
replaceNested.scan(accummulated);
|
replaceNested.scan(accummulated);
|
||||||
JCExpression newGuard;
|
JCExpression newGuard;
|
||||||
JCInstanceOf instanceofCheck;
|
JCInstanceOf instanceofCheck;
|
||||||
if (accummulatedFirstLabel.guard instanceof JCBinary binOp) {
|
if (accummulated.guard instanceof JCBinary binOp) {
|
||||||
newGuard = binOp.rhs;
|
newGuard = binOp.rhs;
|
||||||
instanceofCheck = (JCInstanceOf) binOp.lhs;
|
instanceofCheck = (JCInstanceOf) binOp.lhs;
|
||||||
} else {
|
} else {
|
||||||
newGuard = null;
|
newGuard = null;
|
||||||
instanceofCheck = (JCInstanceOf) accummulatedFirstLabel.guard;
|
instanceofCheck = (JCInstanceOf) accummulated.guard;
|
||||||
}
|
}
|
||||||
JCBindingPattern binding = (JCBindingPattern) instanceofCheck.pattern;
|
JCBindingPattern binding = (JCBindingPattern) instanceofCheck.pattern;
|
||||||
hasUnconditional =
|
hasUnconditional =
|
||||||
@ -730,11 +727,12 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
List<JCCaseLabel> newLabel;
|
List<JCCaseLabel> newLabel;
|
||||||
if (hasUnconditional) {
|
if (hasUnconditional) {
|
||||||
newLabel = List.of(make.ConstantCaseLabel(makeNull()),
|
newLabel = List.of(make.ConstantCaseLabel(makeNull()),
|
||||||
make.PatternCaseLabel(binding, newGuard));
|
make.PatternCaseLabel(binding));
|
||||||
} else {
|
} else {
|
||||||
newLabel = List.of(make.PatternCaseLabel(binding, newGuard));
|
newLabel = List.of(make.PatternCaseLabel(binding));
|
||||||
}
|
}
|
||||||
nestedCases.add(make.Case(CaseKind.STATEMENT, newLabel, accummulated.stats, null));
|
nestedCases.add(make.Case(CaseKind.STATEMENT, newLabel, newGuard,
|
||||||
|
accummulated.stats, null));
|
||||||
lastGuard = newGuard;
|
lastGuard = newGuard;
|
||||||
}
|
}
|
||||||
if (lastGuard != null || !hasUnconditional) {
|
if (lastGuard != null || !hasUnconditional) {
|
||||||
@ -745,6 +743,7 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
? List.of(make.DefaultCaseLabel())
|
? List.of(make.DefaultCaseLabel())
|
||||||
: List.of(make.ConstantCaseLabel(makeNull()),
|
: List.of(make.ConstantCaseLabel(makeNull()),
|
||||||
make.DefaultCaseLabel()),
|
make.DefaultCaseLabel()),
|
||||||
|
null,
|
||||||
List.of(continueSwitch),
|
List.of(continueSwitch),
|
||||||
null));
|
null));
|
||||||
}
|
}
|
||||||
@ -752,9 +751,9 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
newSwitch.patternSwitch = true;
|
newSwitch.patternSwitch = true;
|
||||||
JCPatternCaseLabel leadingTest =
|
JCPatternCaseLabel leadingTest =
|
||||||
(JCPatternCaseLabel) accummulator.first().labels.head;
|
(JCPatternCaseLabel) accummulator.first().labels.head;
|
||||||
leadingTest.guard = null;
|
|
||||||
result.add(make.Case(CaseKind.STATEMENT,
|
result.add(make.Case(CaseKind.STATEMENT,
|
||||||
List.of(leadingTest),
|
List.of(leadingTest),
|
||||||
|
null,
|
||||||
List.of(newSwitch),
|
List.of(newSwitch),
|
||||||
null));
|
null));
|
||||||
} else {
|
} else {
|
||||||
@ -776,14 +775,14 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
|
|
||||||
if (c.head.labels.size() == 1 &&
|
if (c.head.labels.size() == 1 &&
|
||||||
c.head.labels.head instanceof JCPatternCaseLabel patternLabel) {
|
c.head.labels.head instanceof JCPatternCaseLabel patternLabel) {
|
||||||
if (patternLabel.guard instanceof JCBinary binOp &&
|
if (c.head.guard instanceof JCBinary binOp &&
|
||||||
binOp.lhs instanceof JCInstanceOf instanceofCheck &&
|
binOp.lhs instanceof JCInstanceOf instanceofCheck &&
|
||||||
instanceofCheck.pattern instanceof JCBindingPattern binding) {
|
instanceofCheck.pattern instanceof JCBindingPattern binding) {
|
||||||
currentBinding = ((JCBindingPattern) patternLabel.pat).var.sym;
|
currentBinding = ((JCBindingPattern) patternLabel.pat).var.sym;
|
||||||
currentNullable = instanceofCheck.allowNull;
|
currentNullable = instanceofCheck.allowNull;
|
||||||
currentNestedExpression = instanceofCheck.expr;
|
currentNestedExpression = instanceofCheck.expr;
|
||||||
currentNestedBinding = binding.var.sym;
|
currentNestedBinding = binding.var.sym;
|
||||||
} else if (patternLabel.guard instanceof JCInstanceOf instanceofCheck &&
|
} else if (c.head.guard instanceof JCInstanceOf instanceofCheck &&
|
||||||
instanceofCheck.pattern instanceof JCBindingPattern binding) {
|
instanceofCheck.pattern instanceof JCBindingPattern binding) {
|
||||||
currentBinding = ((JCBindingPattern) patternLabel.pat).var.sym;
|
currentBinding = ((JCBindingPattern) patternLabel.pat).var.sym;
|
||||||
currentNullable = instanceofCheck.allowNull;
|
currentNullable = instanceofCheck.allowNull;
|
||||||
@ -835,10 +834,15 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
} else {
|
} else {
|
||||||
return (LoadableConstant) principalType;
|
return (LoadableConstant) principalType;
|
||||||
}
|
}
|
||||||
} else if (l.hasTag(Tag.CONSTANTCASELABEL)&& !TreeInfo.isNullCaseLabel(l)) {
|
} else if (l.hasTag(Tag.CONSTANTCASELABEL) && !TreeInfo.isNullCaseLabel(l)) {
|
||||||
JCExpression expr = ((JCConstantCaseLabel) l).expr;
|
JCExpression expr = ((JCConstantCaseLabel) l).expr;
|
||||||
if ((expr.type.tsym.flags_field & Flags.ENUM) != 0) {
|
Symbol sym = TreeInfo.symbol(expr);
|
||||||
return LoadableConstant.String(((JCIdent) expr).name.toString());
|
if (sym != null && sym.isEnum() && sym.kind == Kind.VAR) {
|
||||||
|
if (selector.tsym.isEnum()) {
|
||||||
|
return LoadableConstant.String(sym.getSimpleName().toString());
|
||||||
|
} else {
|
||||||
|
return createEnumDesc(l.pos(), (ClassSymbol) sym.owner, sym.getSimpleName());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Assert.checkNonNull(expr.type.constValue());
|
Assert.checkNonNull(expr.type.constValue());
|
||||||
|
|
||||||
@ -854,6 +858,38 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LoadableConstant createEnumDesc(DiagnosticPosition pos, ClassSymbol enumClass, Name constant) {
|
||||||
|
MethodSymbol classDesc = rs.resolveInternalMethod(pos, env, syms.classDescType, names.of, List.of(syms.stringType), List.nil());
|
||||||
|
MethodSymbol enumDesc = rs.resolveInternalMethod(pos, env, syms.enumDescType, names.of, List.of(syms.classDescType, syms.stringType), List.nil());
|
||||||
|
return invokeMethodWrapper(pos,
|
||||||
|
enumDesc.asHandle(),
|
||||||
|
invokeMethodWrapper(pos,
|
||||||
|
classDesc.asHandle(),
|
||||||
|
LoadableConstant.String(enumClass.flatname.toString())),
|
||||||
|
LoadableConstant.String(constant.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoadableConstant invokeMethodWrapper(DiagnosticPosition pos, MethodHandleSymbol toCall, LoadableConstant... params) {
|
||||||
|
List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
|
||||||
|
syms.stringType,
|
||||||
|
new ClassType(syms.classType.getEnclosingType(),
|
||||||
|
List.of(syms.botType), //XXX - botType
|
||||||
|
syms.classType.tsym),
|
||||||
|
syms.methodHandleType,
|
||||||
|
types.makeArrayType(syms.objectType));
|
||||||
|
|
||||||
|
MethodSymbol bsm = rs.resolveInternalMethod(pos, env, syms.constantBootstrapsType,
|
||||||
|
names.invoke, bsm_staticArgs, List.nil());
|
||||||
|
|
||||||
|
LoadableConstant[] actualParams = new LoadableConstant[params.length + 1];
|
||||||
|
|
||||||
|
actualParams[0] = toCall;
|
||||||
|
|
||||||
|
System.arraycopy(params, 0, actualParams, 1, params.length);
|
||||||
|
|
||||||
|
return new DynamicVarSymbol(bsm.name, bsm.owner, bsm.asHandle(), toCall.getReturnType(), actualParams);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitBinary(JCBinary tree) {
|
public void visitBinary(JCBinary tree) {
|
||||||
bindingContext = new BasicBindingContext();
|
bindingContext = new BasicBindingContext();
|
||||||
@ -898,77 +934,6 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitForeachLoop(JCTree.JCEnhancedForLoop tree) {
|
|
||||||
bindingContext = new BasicBindingContext();
|
|
||||||
VarSymbol prevCurrentValue = currentValue;
|
|
||||||
try {
|
|
||||||
if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
|
|
||||||
/**
|
|
||||||
* A statement of the form
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* for (<pattern> : coll ) stmt ;
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* (where coll implements {@code Iterable<R>}) gets translated to
|
|
||||||
*
|
|
||||||
* <pre>{@code
|
|
||||||
* for (<type-of-coll-item> N$temp : coll) {
|
|
||||||
* switch (N$temp) {
|
|
||||||
* case <pattern>: stmt;
|
|
||||||
* case null: throw new MatchException();
|
|
||||||
* }
|
|
||||||
* }</pre>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
Type selectorType = types.classBound(tree.elementType);
|
|
||||||
|
|
||||||
currentValue = new VarSymbol(Flags.FINAL | Flags.SYNTHETIC,
|
|
||||||
names.fromString("patt" + tree.pos + target.syntheticNameChar() + "temp"),
|
|
||||||
selectorType,
|
|
||||||
currentMethodSym);
|
|
||||||
|
|
||||||
JCStatement newForVariableDeclaration =
|
|
||||||
make.at(tree.pos).VarDef(currentValue, null).setType(selectorType);
|
|
||||||
|
|
||||||
List<JCExpression> nestedNPEParams = List.of(makeNull());
|
|
||||||
JCNewClass nestedNPE = makeNewClass(syms.nullPointerExceptionType, nestedNPEParams);
|
|
||||||
|
|
||||||
List<JCExpression> matchExParams = List.of(makeNull(), nestedNPE);
|
|
||||||
JCThrow thr = make.Throw(makeNewClass(syms.matchExceptionType, matchExParams));
|
|
||||||
|
|
||||||
JCCase caseNull = make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(makeNull())), List.of(thr), null);
|
|
||||||
|
|
||||||
JCCase casePattern = make.Case(CaseTree.CaseKind.STATEMENT,
|
|
||||||
List.of(make.PatternCaseLabel(jcRecordPattern, null)),
|
|
||||||
List.of(translate(tree.body)),
|
|
||||||
null);
|
|
||||||
|
|
||||||
JCSwitch switchBody =
|
|
||||||
make.Switch(make.Ident(currentValue).setType(selectorType),
|
|
||||||
List.of(caseNull, casePattern));
|
|
||||||
|
|
||||||
switchBody.patternSwitch = true;
|
|
||||||
|
|
||||||
// re-using the same node to eliminate the need to re-patch targets (break/continue)
|
|
||||||
tree.varOrRecordPattern = newForVariableDeclaration.setType(selectorType);
|
|
||||||
tree.expr = translate(tree.expr);
|
|
||||||
tree.body = translate(switchBody);
|
|
||||||
|
|
||||||
JCTree.JCEnhancedForLoop newForEach = tree;
|
|
||||||
|
|
||||||
result = bindingContext.decorateStatement(newForEach);
|
|
||||||
} else {
|
|
||||||
super.visitForeachLoop(tree);
|
|
||||||
result = bindingContext.decorateStatement(tree);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
currentValue = prevCurrentValue;
|
|
||||||
bindingContext.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitWhileLoop(JCWhileLoop tree) {
|
public void visitWhileLoop(JCWhileLoop tree) {
|
||||||
bindingContext = new BasicBindingContext();
|
bindingContext = new BasicBindingContext();
|
||||||
|
@ -512,7 +512,7 @@ public class TransTypes extends TreeTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||||
tree.varOrRecordPattern = translate(tree.varOrRecordPattern, null);
|
tree.var = translate(tree.var, null);
|
||||||
Type iterableType = tree.expr.type;
|
Type iterableType = tree.expr.type;
|
||||||
tree.expr = translate(tree.expr, erasure(tree.expr.type));
|
tree.expr = translate(tree.expr, erasure(tree.expr.type));
|
||||||
if (types.elemtype(tree.expr.type) == null)
|
if (types.elemtype(tree.expr.type) == null)
|
||||||
@ -551,6 +551,7 @@ public class TransTypes extends TreeTranslator {
|
|||||||
|
|
||||||
public void visitCase(JCCase tree) {
|
public void visitCase(JCCase tree) {
|
||||||
tree.labels = translate(tree.labels, null);
|
tree.labels = translate(tree.labels, null);
|
||||||
|
tree.guard = translate(tree.guard, syms.booleanType);
|
||||||
tree.stats = translate(tree.stats);
|
tree.stats = translate(tree.stats);
|
||||||
result = tree;
|
result = tree;
|
||||||
}
|
}
|
||||||
@ -569,7 +570,6 @@ public class TransTypes extends TreeTranslator {
|
|||||||
@Override
|
@Override
|
||||||
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
||||||
tree.pat = translate(tree.pat, null);
|
tree.pat = translate(tree.pat, null);
|
||||||
tree.guard = translate(tree.guard, syms.booleanType);
|
|
||||||
result = tree;
|
result = tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,12 +584,6 @@ public class TransTypes extends TreeTranslator {
|
|||||||
result = retype(tree, tree.type, pt);
|
result = retype(tree, tree.type, pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
|
|
||||||
tree.pattern = translate(tree.pattern, null);
|
|
||||||
result = tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitRecordPattern(JCRecordPattern tree) {
|
public void visitRecordPattern(JCRecordPattern tree) {
|
||||||
tree.fullComponentTypes = tree.record.getRecordComponents()
|
tree.fullComponentTypes = tree.record.getRecordComponents()
|
||||||
.map(rc -> types.memberType(tree.type, rc));
|
.map(rc -> types.memberType(tree.type, rc));
|
||||||
|
@ -297,7 +297,9 @@ public class TreeDiffer extends TreeScanner {
|
|||||||
@Override
|
@Override
|
||||||
public void visitCase(JCCase tree) {
|
public void visitCase(JCCase tree) {
|
||||||
JCCase that = (JCCase) parameter;
|
JCCase that = (JCCase) parameter;
|
||||||
result = scan(tree.labels, that.labels) && scan(tree.stats, that.stats);
|
result = scan(tree.labels, that.labels) &&
|
||||||
|
scan(tree.guard, that.guard) &&
|
||||||
|
scan(tree.stats, that.stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -309,7 +311,7 @@ public class TreeDiffer extends TreeScanner {
|
|||||||
@Override
|
@Override
|
||||||
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
||||||
JCPatternCaseLabel that = (JCPatternCaseLabel) parameter;
|
JCPatternCaseLabel that = (JCPatternCaseLabel) parameter;
|
||||||
result = scan(tree.pat, that.pat) && scan(tree.guard, that.guard);
|
result = scan(tree.pat, that.pat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -388,7 +390,7 @@ public class TreeDiffer extends TreeScanner {
|
|||||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||||
JCEnhancedForLoop that = (JCEnhancedForLoop) parameter;
|
JCEnhancedForLoop that = (JCEnhancedForLoop) parameter;
|
||||||
result =
|
result =
|
||||||
scan(tree.varOrRecordPattern, that.varOrRecordPattern)
|
scan(tree.var, that.var)
|
||||||
&& scan(tree.expr, that.expr)
|
&& scan(tree.expr, that.expr)
|
||||||
&& scan(tree.body, that.body);
|
&& scan(tree.body, that.body);
|
||||||
}
|
}
|
||||||
|
@ -293,7 +293,7 @@ implements CRTFlags {
|
|||||||
|
|
||||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||||
SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
|
SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
|
||||||
sr.mergeWith(csp(tree.varOrRecordPattern));
|
sr.mergeWith(csp(tree.var));
|
||||||
sr.mergeWith(csp(tree.expr));
|
sr.mergeWith(csp(tree.expr));
|
||||||
sr.mergeWith(csp(tree.body));
|
sr.mergeWith(csp(tree.body));
|
||||||
result = sr;
|
result = sr;
|
||||||
@ -323,6 +323,7 @@ implements CRTFlags {
|
|||||||
public void visitCase(JCCase tree) {
|
public void visitCase(JCCase tree) {
|
||||||
SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
|
SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
|
||||||
sr.mergeWith(csp(tree.labels));
|
sr.mergeWith(csp(tree.labels));
|
||||||
|
sr.mergeWith(csp(tree.guard));
|
||||||
sr.mergeWith(csp(tree.stats));
|
sr.mergeWith(csp(tree.stats));
|
||||||
result = sr;
|
result = sr;
|
||||||
}
|
}
|
||||||
@ -343,7 +344,6 @@ implements CRTFlags {
|
|||||||
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
||||||
SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
|
SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
|
||||||
sr.mergeWith(csp(tree.pat));
|
sr.mergeWith(csp(tree.pat));
|
||||||
sr.mergeWith(csp(tree.guard));
|
|
||||||
result = sr;
|
result = sr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -941,6 +941,15 @@ public class ClassWriter extends ClassFile {
|
|||||||
*/
|
*/
|
||||||
void writeBootstrapMethods() {
|
void writeBootstrapMethods() {
|
||||||
int alenIdx = writeAttr(names.BootstrapMethods);
|
int alenIdx = writeAttr(names.BootstrapMethods);
|
||||||
|
int lastBootstrapMethods;
|
||||||
|
do {
|
||||||
|
lastBootstrapMethods = poolWriter.bootstrapMethods.size();
|
||||||
|
for (BsmKey bsmKey : java.util.List.copyOf(poolWriter.bootstrapMethods.keySet())) {
|
||||||
|
for (LoadableConstant arg : bsmKey.staticArgs) {
|
||||||
|
poolWriter.putConstant(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (lastBootstrapMethods < poolWriter.bootstrapMethods.size());
|
||||||
databuf.appendChar(poolWriter.bootstrapMethods.size());
|
databuf.appendChar(poolWriter.bootstrapMethods.size());
|
||||||
for (BsmKey bsmKey : poolWriter.bootstrapMethods.keySet()) {
|
for (BsmKey bsmKey : poolWriter.bootstrapMethods.keySet()) {
|
||||||
//write BSM handle
|
//write BSM handle
|
||||||
|
@ -1535,11 +1535,6 @@ public class JavaCompiler {
|
|||||||
super.visitRecordPattern(that);
|
super.visitRecordPattern(that);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void visitParenthesizedPattern(JCTree.JCParenthesizedPattern tree) {
|
|
||||||
hasPatterns = true;
|
|
||||||
super.visitParenthesizedPattern(tree);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void visitSwitch(JCSwitch tree) {
|
public void visitSwitch(JCSwitch tree) {
|
||||||
hasPatterns |= tree.patternSwitch;
|
hasPatterns |= tree.patternSwitch;
|
||||||
super.visitSwitch(tree);
|
super.visitSwitch(tree);
|
||||||
|
@ -830,48 +830,52 @@ public class JavacParser implements Parser {
|
|||||||
|
|
||||||
/** parses patterns.
|
/** parses patterns.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType,
|
public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType,
|
||||||
boolean allowVar, boolean checkGuard) {
|
boolean allowVar, boolean checkGuard) {
|
||||||
JCPattern pattern;
|
JCPattern pattern;
|
||||||
if (token.kind == LPAREN && parsedType == null) {
|
mods = mods != null ? mods : optFinal(0);
|
||||||
//parenthesized pattern:
|
JCExpression e;
|
||||||
int startPos = token.pos;
|
if (parsedType == null) {
|
||||||
accept(LPAREN);
|
boolean var = token.kind == IDENTIFIER && token.name() == names.var;
|
||||||
JCPattern p = parsePattern(token.pos, null, null, true, false);
|
e = unannotatedType(allowVar, TYPE | NOLAMBDA);
|
||||||
accept(RPAREN);
|
if (var) {
|
||||||
pattern = toP(F.at(startPos).ParenthesizedPattern(p));
|
e = null;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
mods = mods != null ? mods : optFinal(0);
|
e = parsedType;
|
||||||
JCExpression e;
|
}
|
||||||
if (parsedType == null) {
|
if (token.kind == LPAREN) {
|
||||||
boolean var = token.kind == IDENTIFIER && token.name() == names.var;
|
//deconstruction pattern:
|
||||||
e = unannotatedType(allowVar, TYPE | NOLAMBDA);
|
checkSourceLevel(Feature.RECORD_PATTERNS);
|
||||||
if (var) {
|
ListBuffer<JCPattern> nested = new ListBuffer<>();
|
||||||
e = null;
|
if (!peekToken(RPAREN)) {
|
||||||
}
|
do {
|
||||||
} else {
|
|
||||||
e = parsedType;
|
|
||||||
}
|
|
||||||
if (token.kind == LPAREN) {
|
|
||||||
//deconstruction pattern:
|
|
||||||
checkSourceLevel(Feature.RECORD_PATTERNS);
|
|
||||||
ListBuffer<JCPattern> nested = new ListBuffer<>();
|
|
||||||
if (!peekToken(RPAREN)) {
|
|
||||||
do {
|
|
||||||
nextToken();
|
|
||||||
JCPattern nestedPattern = parsePattern(token.pos, null, null, true, false);
|
|
||||||
nested.append(nestedPattern);
|
|
||||||
} while (token.kind == COMMA);
|
|
||||||
} else {
|
|
||||||
nextToken();
|
nextToken();
|
||||||
}
|
JCPattern nestedPattern = parsePattern(token.pos, null, null, true, false);
|
||||||
accept(RPAREN);
|
nested.append(nestedPattern);
|
||||||
pattern = toP(F.at(pos).RecordPattern(e, nested.toList()));
|
} while (token.kind == COMMA);
|
||||||
} else {
|
} else {
|
||||||
//type test pattern:
|
nextToken();
|
||||||
JCVariableDecl var = toP(F.at(token.pos).VarDef(mods, ident(), e, null));
|
|
||||||
pattern = toP(F.at(pos).BindingPattern(var));
|
|
||||||
}
|
}
|
||||||
|
accept(RPAREN);
|
||||||
|
pattern = toP(F.at(pos).RecordPattern(e, nested.toList()));
|
||||||
|
if (mods.annotations.nonEmpty()) {
|
||||||
|
log.error(mods.annotations.head.pos(), Errors.RecordPatternsAnnotationsNotAllowed);
|
||||||
|
}
|
||||||
|
new TreeScanner() {
|
||||||
|
@Override
|
||||||
|
public void visitAnnotatedType(JCAnnotatedType tree) {
|
||||||
|
log.error(tree.pos(), Errors.RecordPatternsAnnotationsNotAllowed);
|
||||||
|
}
|
||||||
|
}.scan(e);
|
||||||
|
} else {
|
||||||
|
//type test pattern:
|
||||||
|
JCVariableDecl var = toP(F.at(token.pos).VarDef(mods, ident(), e, null));
|
||||||
|
if (e == null) {
|
||||||
|
var.startPos = pos;
|
||||||
|
}
|
||||||
|
pattern = toP(F.at(pos).BindingPattern(var));
|
||||||
}
|
}
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
@ -1620,6 +1624,7 @@ public class JavacParser implements Parser {
|
|||||||
allowDefault = TreeInfo.isNullCaseLabel(label);
|
allowDefault = TreeInfo.isNullCaseLabel(label);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
JCExpression guard = parseGuard(pats.last());
|
||||||
List<JCStatement> stats = null;
|
List<JCStatement> stats = null;
|
||||||
JCTree body = null;
|
JCTree body = null;
|
||||||
CaseTree.CaseKind kind;
|
CaseTree.CaseKind kind;
|
||||||
@ -1645,7 +1650,7 @@ public class JavacParser implements Parser {
|
|||||||
kind = JCCase.STATEMENT;
|
kind = JCCase.STATEMENT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
caseExprs.append(toP(F.at(casePos).Case(kind, pats.toList(), stats, body)));
|
caseExprs.append(toP(F.at(casePos).Case(kind, pats.toList(), guard, stats, body)));
|
||||||
return caseExprs.toList();
|
return caseExprs.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2910,47 +2915,25 @@ public class JavacParser implements Parser {
|
|||||||
case FOR: {
|
case FOR: {
|
||||||
nextToken();
|
nextToken();
|
||||||
accept(LPAREN);
|
accept(LPAREN);
|
||||||
JCTree pattern;
|
List<JCStatement> inits = token.kind == SEMI ? List.nil() : forInit();
|
||||||
|
if (inits.length() == 1 &&
|
||||||
ForInitResult initResult = analyzeForInit();
|
inits.head.hasTag(VARDEF) &&
|
||||||
|
((JCVariableDecl) inits.head).init == null &&
|
||||||
if (initResult == ForInitResult.RecordPattern) {
|
token.kind == COLON) {
|
||||||
int patternPos = token.pos;
|
JCVariableDecl var = (JCVariableDecl)inits.head;
|
||||||
JCModifiers mods = optFinal(0);
|
|
||||||
int typePos = token.pos;
|
|
||||||
JCExpression type = unannotatedType(false);
|
|
||||||
|
|
||||||
pattern = parsePattern(patternPos, mods, type, false, false);
|
|
||||||
|
|
||||||
if (pattern != null) {
|
|
||||||
checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
|
|
||||||
}
|
|
||||||
accept(COLON);
|
accept(COLON);
|
||||||
JCExpression expr = parseExpression();
|
JCExpression expr = parseExpression();
|
||||||
accept(RPAREN);
|
accept(RPAREN);
|
||||||
JCStatement body = parseStatementAsBlock();
|
JCStatement body = parseStatementAsBlock();
|
||||||
return F.at(pos).ForeachLoop(pattern, expr, body);
|
return F.at(pos).ForeachLoop(var, expr, body);
|
||||||
} else {
|
} else {
|
||||||
List<JCStatement> inits = token.kind == SEMI ? List.nil() : forInit();
|
accept(SEMI);
|
||||||
if (inits.length() == 1 &&
|
JCExpression cond = token.kind == SEMI ? null : parseExpression();
|
||||||
inits.head.hasTag(VARDEF) &&
|
accept(SEMI);
|
||||||
((JCVariableDecl) inits.head).init == null &&
|
List<JCExpressionStatement> steps = token.kind == RPAREN ? List.nil() : forUpdate();
|
||||||
token.kind == COLON) {
|
accept(RPAREN);
|
||||||
JCVariableDecl var = (JCVariableDecl) inits.head;
|
JCStatement body = parseStatementAsBlock();
|
||||||
accept(COLON);
|
return F.at(pos).ForLoop(inits, cond, steps, body);
|
||||||
JCExpression expr = parseExpression();
|
|
||||||
accept(RPAREN);
|
|
||||||
JCStatement body = parseStatementAsBlock();
|
|
||||||
return F.at(pos).ForeachLoop(var, expr, body);
|
|
||||||
} else {
|
|
||||||
accept(SEMI);
|
|
||||||
JCExpression cond = token.kind == SEMI ? null : parseExpression();
|
|
||||||
accept(SEMI);
|
|
||||||
List<JCExpressionStatement> steps = token.kind == RPAREN ? List.nil() : forUpdate();
|
|
||||||
accept(RPAREN);
|
|
||||||
JCStatement body = parseStatementAsBlock();
|
|
||||||
return F.at(pos).ForLoop(inits, cond, steps, body);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case WHILE: {
|
case WHILE: {
|
||||||
@ -3067,91 +3050,6 @@ public class JavacParser implements Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ForInitResult {
|
|
||||||
LocalVarDecl,
|
|
||||||
RecordPattern
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("fallthrough")
|
|
||||||
ForInitResult analyzeForInit() {
|
|
||||||
boolean inType = false;
|
|
||||||
boolean inSelectionAndParenthesis = false;
|
|
||||||
int typeParameterPossibleStart = -1;
|
|
||||||
outer: for (int lookahead = 0; ; lookahead++) {
|
|
||||||
TokenKind tk = S.token(lookahead).kind;
|
|
||||||
switch (tk) {
|
|
||||||
case DOT:
|
|
||||||
if (inType) break; // in qualified type
|
|
||||||
case COMMA:
|
|
||||||
typeParameterPossibleStart = lookahead;
|
|
||||||
break;
|
|
||||||
case QUES:
|
|
||||||
// "?" only allowed in a type parameter position - otherwise it's an expression
|
|
||||||
if (typeParameterPossibleStart == lookahead - 1) break;
|
|
||||||
else return ForInitResult.LocalVarDecl;
|
|
||||||
case EXTENDS: case SUPER: case AMP:
|
|
||||||
case GTGTGT: case GTGT: case GT:
|
|
||||||
case FINAL: case ELLIPSIS:
|
|
||||||
break;
|
|
||||||
case BYTE: case SHORT: case INT: case LONG: case FLOAT:
|
|
||||||
case DOUBLE: case BOOLEAN: case CHAR: case VOID:
|
|
||||||
if (peekToken(lookahead, IDENTIFIER)) {
|
|
||||||
return inSelectionAndParenthesis ? ForInitResult.RecordPattern
|
|
||||||
: ForInitResult.LocalVarDecl;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LPAREN:
|
|
||||||
if (lookahead != 0 && inType) {
|
|
||||||
inSelectionAndParenthesis = true;
|
|
||||||
inType = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RPAREN:
|
|
||||||
// a method call in the init part or a record pattern?
|
|
||||||
if (inSelectionAndParenthesis) {
|
|
||||||
if (peekToken(lookahead, DOT) ||
|
|
||||||
peekToken(lookahead, SEMI) ||
|
|
||||||
peekToken(lookahead, ARROW)) {
|
|
||||||
return ForInitResult.LocalVarDecl;
|
|
||||||
}
|
|
||||||
else if(peekToken(lookahead, COLON)) {
|
|
||||||
return ForInitResult.RecordPattern;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UNDERSCORE:
|
|
||||||
case ASSERT:
|
|
||||||
case ENUM:
|
|
||||||
case IDENTIFIER:
|
|
||||||
if (lookahead == 0) {
|
|
||||||
inType = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MONKEYS_AT: {
|
|
||||||
int prevLookahead = lookahead;
|
|
||||||
lookahead = skipAnnotation(lookahead);
|
|
||||||
if (typeParameterPossibleStart == prevLookahead - 1) {
|
|
||||||
// move possible start of type param after the anno
|
|
||||||
typeParameterPossibleStart = lookahead;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LBRACKET:
|
|
||||||
if (peekToken(lookahead, RBRACKET)) {
|
|
||||||
return inSelectionAndParenthesis ? ForInitResult.RecordPattern
|
|
||||||
: ForInitResult.LocalVarDecl;
|
|
||||||
}
|
|
||||||
return ForInitResult.LocalVarDecl;
|
|
||||||
case LT:
|
|
||||||
typeParameterPossibleStart = lookahead;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
//this includes EOF
|
|
||||||
return ForInitResult.LocalVarDecl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JCStatement parseStatement() {
|
public JCStatement parseStatement() {
|
||||||
return parseStatementAsBlock();
|
return parseStatementAsBlock();
|
||||||
@ -3234,6 +3132,7 @@ public class JavacParser implements Parser {
|
|||||||
checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS);
|
checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS);
|
||||||
allowDefault = TreeInfo.isNullCaseLabel(label);
|
allowDefault = TreeInfo.isNullCaseLabel(label);
|
||||||
};
|
};
|
||||||
|
JCExpression guard = parseGuard(pats.last());
|
||||||
CaseTree.CaseKind caseKind;
|
CaseTree.CaseKind caseKind;
|
||||||
JCTree body = null;
|
JCTree body = null;
|
||||||
if (token.kind == ARROW) {
|
if (token.kind == ARROW) {
|
||||||
@ -3251,7 +3150,7 @@ public class JavacParser implements Parser {
|
|||||||
caseKind = JCCase.STATEMENT;
|
caseKind = JCCase.STATEMENT;
|
||||||
stats = blockStatements();
|
stats = blockStatements();
|
||||||
}
|
}
|
||||||
c = F.at(pos).Case(caseKind, pats.toList(), stats, body);
|
c = F.at(pos).Case(caseKind, pats.toList(), guard, stats, body);
|
||||||
if (stats.isEmpty())
|
if (stats.isEmpty())
|
||||||
storeEnd(c, S.prevToken().endPos);
|
storeEnd(c, S.prevToken().endPos);
|
||||||
return cases.append(c).toList();
|
return cases.append(c).toList();
|
||||||
@ -3259,6 +3158,7 @@ public class JavacParser implements Parser {
|
|||||||
case DEFAULT: {
|
case DEFAULT: {
|
||||||
nextToken();
|
nextToken();
|
||||||
JCCaseLabel defaultPattern = toP(F.at(pos).DefaultCaseLabel());
|
JCCaseLabel defaultPattern = toP(F.at(pos).DefaultCaseLabel());
|
||||||
|
JCExpression guard = parseGuard(defaultPattern);
|
||||||
CaseTree.CaseKind caseKind;
|
CaseTree.CaseKind caseKind;
|
||||||
JCTree body = null;
|
JCTree body = null;
|
||||||
if (token.kind == ARROW) {
|
if (token.kind == ARROW) {
|
||||||
@ -3276,7 +3176,7 @@ public class JavacParser implements Parser {
|
|||||||
caseKind = JCCase.STATEMENT;
|
caseKind = JCCase.STATEMENT;
|
||||||
stats = blockStatements();
|
stats = blockStatements();
|
||||||
}
|
}
|
||||||
c = F.at(pos).Case(caseKind, List.of(defaultPattern), stats, body);
|
c = F.at(pos).Case(caseKind, List.of(defaultPattern), guard, stats, body);
|
||||||
if (stats.isEmpty())
|
if (stats.isEmpty())
|
||||||
storeEnd(c, S.prevToken().endPos);
|
storeEnd(c, S.prevToken().endPos);
|
||||||
return cases.append(c).toList();
|
return cases.append(c).toList();
|
||||||
@ -3304,12 +3204,7 @@ public class JavacParser implements Parser {
|
|||||||
if (pattern) {
|
if (pattern) {
|
||||||
checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
|
checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
|
||||||
JCPattern p = parsePattern(patternPos, mods, null, false, true);
|
JCPattern p = parsePattern(patternPos, mods, null, false, true);
|
||||||
JCExpression guard = null;
|
return toP(F.at(patternPos).PatternCaseLabel(p));
|
||||||
if (token.kind == IDENTIFIER && token.name() == names.when) {
|
|
||||||
nextToken();
|
|
||||||
guard = term(EXPR | NOLAMBDA);
|
|
||||||
}
|
|
||||||
return toP(F.at(patternPos).PatternCaseLabel(p, guard));
|
|
||||||
} else {
|
} else {
|
||||||
JCExpression expr = term(EXPR | NOLAMBDA);
|
JCExpression expr = term(EXPR | NOLAMBDA);
|
||||||
return toP(F.at(patternPos).ConstantCaseLabel(expr));
|
return toP(F.at(patternPos).ConstantCaseLabel(expr));
|
||||||
@ -3319,6 +3214,22 @@ public class JavacParser implements Parser {
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JCExpression parseGuard(JCCaseLabel label) {
|
||||||
|
JCExpression guard = null;
|
||||||
|
|
||||||
|
if (token.kind == IDENTIFIER && token.name() == names.when) {
|
||||||
|
int pos = token.pos;
|
||||||
|
|
||||||
|
nextToken();
|
||||||
|
guard = term(EXPR | NOLAMBDA);
|
||||||
|
|
||||||
|
if (!(label instanceof JCPatternCaseLabel)) {
|
||||||
|
guard = syntaxError(pos, List.of(guard), Errors.GuardNotAllowed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return guard;
|
||||||
|
}
|
||||||
@SuppressWarnings("fallthrough")
|
@SuppressWarnings("fallthrough")
|
||||||
PatternResult analyzePattern(int lookahead) {
|
PatternResult analyzePattern(int lookahead) {
|
||||||
int typeDepth = 0;
|
int typeDepth = 0;
|
||||||
|
@ -528,9 +528,16 @@ compiler.err.duplicate.unconditional.pattern=\
|
|||||||
compiler.err.unconditional.pattern.and.default=\
|
compiler.err.unconditional.pattern.and.default=\
|
||||||
switch has both an unconditional pattern and a default label
|
switch has both an unconditional pattern and a default label
|
||||||
|
|
||||||
|
compiler.err.guard.not.allowed=\
|
||||||
|
guards are only allowed for case with a pattern
|
||||||
|
|
||||||
compiler.err.guard.has.constant.expression.false=\
|
compiler.err.guard.has.constant.expression.false=\
|
||||||
this case label has a guard that is a constant expression with value ''false''
|
this case label has a guard that is a constant expression with value ''false''
|
||||||
|
|
||||||
|
# 0: symbol
|
||||||
|
compiler.err.cannot.assign.not.declared.guard=\
|
||||||
|
cannot assign to {0}, as it was not declared inside the guard
|
||||||
|
|
||||||
# 0: type, 1: type
|
# 0: type, 1: type
|
||||||
compiler.err.constant.label.not.compatible=\
|
compiler.err.constant.label.not.compatible=\
|
||||||
constant label of type {0} is not compatible with switch selector type {1}
|
constant label of type {0} is not compatible with switch selector type {1}
|
||||||
@ -619,10 +626,6 @@ compiler.err.foreach.not.applicable.to.type=\
|
|||||||
required: {1}\n\
|
required: {1}\n\
|
||||||
found: {0}
|
found: {0}
|
||||||
|
|
||||||
# 0: type, 1: type
|
|
||||||
compiler.err.foreach.not.exhaustive.on.type=\
|
|
||||||
Pattern {0} is not exhaustive on {1}
|
|
||||||
|
|
||||||
compiler.err.fp.number.too.large=\
|
compiler.err.fp.number.too.large=\
|
||||||
floating-point number too large
|
floating-point number too large
|
||||||
|
|
||||||
@ -1500,10 +1503,6 @@ compiler.misc.varargs.trustme.on.reifiable.varargs=\
|
|||||||
compiler.err.instanceof.reifiable.not.safe=\
|
compiler.err.instanceof.reifiable.not.safe=\
|
||||||
{0} cannot be safely cast to {1}
|
{0} cannot be safely cast to {1}
|
||||||
|
|
||||||
# 0: type, 1: type
|
|
||||||
compiler.err.instanceof.pattern.no.subtype=\
|
|
||||||
expression type {0} is a subtype of pattern type {1}
|
|
||||||
|
|
||||||
# 0: symbol
|
# 0: symbol
|
||||||
compiler.misc.varargs.trustme.on.non.varargs.meth=\
|
compiler.misc.varargs.trustme.on.non.varargs.meth=\
|
||||||
Method {0} is not a varargs method.
|
Method {0} is not a varargs method.
|
||||||
@ -3854,6 +3853,9 @@ compiler.err.instance.initializer.not.allowed.in.records=\
|
|||||||
compiler.err.static.declaration.not.allowed.in.inner.classes=\
|
compiler.err.static.declaration.not.allowed.in.inner.classes=\
|
||||||
static declarations not allowed in inner classes
|
static declarations not allowed in inner classes
|
||||||
|
|
||||||
|
compiler.err.record.patterns.annotations.not.allowed=\
|
||||||
|
annotations not allowed on record patterns
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
# messages previously at javac.properties
|
# messages previously at javac.properties
|
||||||
|
|
||||||
|
@ -240,7 +240,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
|||||||
/** Patterns.
|
/** Patterns.
|
||||||
*/
|
*/
|
||||||
BINDINGPATTERN,
|
BINDINGPATTERN,
|
||||||
PARENTHESIZEDPATTERN,
|
|
||||||
RECORDPATTERN,
|
RECORDPATTERN,
|
||||||
|
|
||||||
/* Case labels.
|
/* Case labels.
|
||||||
@ -1222,13 +1221,11 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
|||||||
* The enhanced for loop.
|
* The enhanced for loop.
|
||||||
*/
|
*/
|
||||||
public static class JCEnhancedForLoop extends JCStatement implements EnhancedForLoopTree {
|
public static class JCEnhancedForLoop extends JCStatement implements EnhancedForLoopTree {
|
||||||
public JCTree varOrRecordPattern;
|
public JCVariableDecl var;
|
||||||
public JCExpression expr;
|
public JCExpression expr;
|
||||||
public JCStatement body;
|
public JCStatement body;
|
||||||
public Type elementType;
|
protected JCEnhancedForLoop(JCVariableDecl var, JCExpression expr, JCStatement body) {
|
||||||
|
this.var = var;
|
||||||
protected JCEnhancedForLoop(JCTree varOrRecordPattern, JCExpression expr, JCStatement body) {
|
|
||||||
this.varOrRecordPattern = varOrRecordPattern;
|
|
||||||
this.expr = expr;
|
this.expr = expr;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
@ -1238,11 +1235,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
|||||||
@DefinedBy(Api.COMPILER_TREE)
|
@DefinedBy(Api.COMPILER_TREE)
|
||||||
public Kind getKind() { return Kind.ENHANCED_FOR_LOOP; }
|
public Kind getKind() { return Kind.ENHANCED_FOR_LOOP; }
|
||||||
@DefinedBy(Api.COMPILER_TREE)
|
@DefinedBy(Api.COMPILER_TREE)
|
||||||
public JCVariableDecl getVariable() {
|
public JCVariableDecl getVariable() { return var; }
|
||||||
return varOrRecordPattern instanceof JCVariableDecl var ? var : null;
|
|
||||||
}
|
|
||||||
@DefinedBy(Api.COMPILER_TREE)
|
|
||||||
public JCTree getVariableOrRecordPattern() { return varOrRecordPattern; }
|
|
||||||
@DefinedBy(Api.COMPILER_TREE)
|
@DefinedBy(Api.COMPILER_TREE)
|
||||||
public JCExpression getExpression() { return expr; }
|
public JCExpression getExpression() { return expr; }
|
||||||
@DefinedBy(Api.COMPILER_TREE)
|
@DefinedBy(Api.COMPILER_TREE)
|
||||||
@ -1255,10 +1248,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
|||||||
public Tag getTag() {
|
public Tag getTag() {
|
||||||
return FOREACHLOOP;
|
return FOREACHLOOP;
|
||||||
}
|
}
|
||||||
@Override @DefinedBy(Api.COMPILER_TREE)
|
|
||||||
public EnhancedForLoopTree.DeclarationKind getDeclarationKind() {
|
|
||||||
return varOrRecordPattern.hasTag(VARDEF) ? DeclarationKind.VARIABLE : DeclarationKind.PATTERN;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1334,15 +1323,18 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
|||||||
public static final CaseKind RULE = CaseKind.RULE;
|
public static final CaseKind RULE = CaseKind.RULE;
|
||||||
public final CaseKind caseKind;
|
public final CaseKind caseKind;
|
||||||
public List<JCCaseLabel> labels;
|
public List<JCCaseLabel> labels;
|
||||||
|
public JCExpression guard;
|
||||||
public List<JCStatement> stats;
|
public List<JCStatement> stats;
|
||||||
public JCTree body;
|
public JCTree body;
|
||||||
public boolean completesNormally;
|
public boolean completesNormally;
|
||||||
protected JCCase(CaseKind caseKind, List<JCCaseLabel> labels,
|
protected JCCase(CaseKind caseKind, List<JCCaseLabel> labels,
|
||||||
|
JCExpression guard,
|
||||||
List<JCStatement> stats, JCTree body) {
|
List<JCStatement> stats, JCTree body) {
|
||||||
Assert.checkNonNull(labels);
|
Assert.checkNonNull(labels);
|
||||||
Assert.check(labels.isEmpty() || labels.head != null);
|
Assert.check(labels.isEmpty() || labels.head != null);
|
||||||
this.caseKind = caseKind;
|
this.caseKind = caseKind;
|
||||||
this.labels = labels;
|
this.labels = labels;
|
||||||
|
this.guard = guard;
|
||||||
this.stats = stats;
|
this.stats = stats;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
@ -1365,6 +1357,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
|||||||
@Override @DefinedBy(Api.COMPILER_TREE)
|
@Override @DefinedBy(Api.COMPILER_TREE)
|
||||||
public List<JCCaseLabel> getLabels() { return labels; }
|
public List<JCCaseLabel> getLabels() { return labels; }
|
||||||
@Override @DefinedBy(Api.COMPILER_TREE)
|
@Override @DefinedBy(Api.COMPILER_TREE)
|
||||||
|
public JCExpression getGuard() { return guard; }
|
||||||
|
@Override @DefinedBy(Api.COMPILER_TREE)
|
||||||
public List<JCStatement> getStatements() {
|
public List<JCStatement> getStatements() {
|
||||||
return caseKind == CaseKind.STATEMENT ? stats : null;
|
return caseKind == CaseKind.STATEMENT ? stats : null;
|
||||||
}
|
}
|
||||||
@ -2252,10 +2246,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
|||||||
|
|
||||||
@DefinedBy(Api.COMPILER_TREE)
|
@DefinedBy(Api.COMPILER_TREE)
|
||||||
public JCExpression getExpression() { return expr; }
|
public JCExpression getExpression() { return expr; }
|
||||||
@DefinedBy(Api.COMPILER_TREE)
|
|
||||||
public TestKind getTestKind() {
|
|
||||||
return pattern instanceof JCPatternCaseLabel ? TestKind.PATTERN : TestKind.TYPE;
|
|
||||||
}
|
|
||||||
@Override @DefinedBy(Api.COMPILER_TREE)
|
@Override @DefinedBy(Api.COMPILER_TREE)
|
||||||
public <R,D> R accept(TreeVisitor<R,D> v, D d) {
|
public <R,D> R accept(TreeVisitor<R,D> v, D d) {
|
||||||
return v.visitInstanceOf(this, d);
|
return v.visitInstanceOf(this, d);
|
||||||
@ -2378,11 +2368,9 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
|||||||
implements PatternCaseLabelTree {
|
implements PatternCaseLabelTree {
|
||||||
|
|
||||||
public JCPattern pat;
|
public JCPattern pat;
|
||||||
public JCExpression guard;
|
|
||||||
|
|
||||||
protected JCPatternCaseLabel(JCPattern pat, JCExpression guard) {
|
protected JCPatternCaseLabel(JCPattern pat) {
|
||||||
this.pat = pat;
|
this.pat = pat;
|
||||||
this.guard = guard;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override @DefinedBy(Api.COMPILER_TREE)
|
@Override @DefinedBy(Api.COMPILER_TREE)
|
||||||
@ -2390,11 +2378,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
|||||||
return pat;
|
return pat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override @DefinedBy(Api.COMPILER_TREE)
|
|
||||||
public JCExpression getGuard() {
|
|
||||||
return guard;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(Visitor v) {
|
public void accept(Visitor v) {
|
||||||
v.visitPatternCaseLabel(this);
|
v.visitPatternCaseLabel(this);
|
||||||
@ -2418,41 +2401,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class JCParenthesizedPattern extends JCPattern
|
|
||||||
implements ParenthesizedPatternTree {
|
|
||||||
public JCPattern pattern;
|
|
||||||
|
|
||||||
public JCParenthesizedPattern(JCPattern pattern) {
|
|
||||||
this.pattern = pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override @DefinedBy(Api.COMPILER_TREE)
|
|
||||||
public PatternTree getPattern() {
|
|
||||||
return pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(Visitor v) {
|
|
||||||
v.visitParenthesizedPattern(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@DefinedBy(Api.COMPILER_TREE)
|
|
||||||
public Kind getKind() {
|
|
||||||
return Kind.PARENTHESIZED_PATTERN;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@DefinedBy(Api.COMPILER_TREE)
|
|
||||||
public <R, D> R accept(TreeVisitor<R, D> v, D d) {
|
|
||||||
return v.visitParenthesizedPattern(this, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Tag getTag() {
|
|
||||||
return PARENTHESIZEDPATTERN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class JCRecordPattern extends JCPattern
|
public static class JCRecordPattern extends JCPattern
|
||||||
implements DeconstructionPatternTree {
|
implements DeconstructionPatternTree {
|
||||||
public JCExpression deconstructor;
|
public JCExpression deconstructor;
|
||||||
@ -3487,11 +3435,11 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
|||||||
JCExpression cond,
|
JCExpression cond,
|
||||||
List<JCExpressionStatement> step,
|
List<JCExpressionStatement> step,
|
||||||
JCStatement body);
|
JCStatement body);
|
||||||
JCEnhancedForLoop ForeachLoop(JCTree var, JCExpression expr, JCStatement body);
|
JCEnhancedForLoop ForeachLoop(JCVariableDecl var, JCExpression expr, JCStatement body);
|
||||||
JCLabeledStatement Labelled(Name label, JCStatement body);
|
JCLabeledStatement Labelled(Name label, JCStatement body);
|
||||||
JCSwitch Switch(JCExpression selector, List<JCCase> cases);
|
JCSwitch Switch(JCExpression selector, List<JCCase> cases);
|
||||||
JCSwitchExpression SwitchExpression(JCExpression selector, List<JCCase> cases);
|
JCSwitchExpression SwitchExpression(JCExpression selector, List<JCCase> cases);
|
||||||
JCCase Case(CaseTree.CaseKind caseKind, List<JCCaseLabel> labels,
|
JCCase Case(CaseTree.CaseKind caseKind, List<JCCaseLabel> labels, JCExpression guard,
|
||||||
List<JCStatement> stats, JCTree body);
|
List<JCStatement> stats, JCTree body);
|
||||||
JCSynchronized Synchronized(JCExpression lock, JCBlock body);
|
JCSynchronized Synchronized(JCExpression lock, JCBlock body);
|
||||||
JCTry Try(JCBlock body, List<JCCatch> catchers, JCBlock finalizer);
|
JCTry Try(JCBlock body, List<JCCatch> catchers, JCBlock finalizer);
|
||||||
@ -3601,7 +3549,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
|
|||||||
public void visitDefaultCaseLabel(JCDefaultCaseLabel that) { visitTree(that); }
|
public void visitDefaultCaseLabel(JCDefaultCaseLabel that) { visitTree(that); }
|
||||||
public void visitConstantCaseLabel(JCConstantCaseLabel that) { visitTree(that); }
|
public void visitConstantCaseLabel(JCConstantCaseLabel that) { visitTree(that); }
|
||||||
public void visitPatternCaseLabel(JCPatternCaseLabel that) { visitTree(that); }
|
public void visitPatternCaseLabel(JCPatternCaseLabel that) { visitTree(that); }
|
||||||
public void visitParenthesizedPattern(JCParenthesizedPattern that) { visitTree(that); }
|
|
||||||
public void visitRecordPattern(JCRecordPattern that) { visitTree(that); }
|
public void visitRecordPattern(JCRecordPattern that) { visitTree(that); }
|
||||||
public void visitIndexed(JCArrayAccess that) { visitTree(that); }
|
public void visitIndexed(JCArrayAccess that) { visitTree(that); }
|
||||||
public void visitSelect(JCFieldAccess that) { visitTree(that); }
|
public void visitSelect(JCFieldAccess that) { visitTree(that); }
|
||||||
|
@ -814,7 +814,7 @@ public class Pretty extends JCTree.Visitor {
|
|||||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||||
try {
|
try {
|
||||||
print("for (");
|
print("for (");
|
||||||
printExpr(tree.varOrRecordPattern);
|
printExpr(tree.var);
|
||||||
print(" : ");
|
print(" : ");
|
||||||
printExpr(tree.expr);
|
printExpr(tree.expr);
|
||||||
print(") ");
|
print(") ");
|
||||||
@ -862,6 +862,10 @@ public class Pretty extends JCTree.Visitor {
|
|||||||
print("case ");
|
print("case ");
|
||||||
printExprs(tree.labels);
|
printExprs(tree.labels);
|
||||||
}
|
}
|
||||||
|
if (tree.guard != null) {
|
||||||
|
print(" when ");
|
||||||
|
print(tree.guard);
|
||||||
|
}
|
||||||
if (tree.caseKind == JCCase.STATEMENT) {
|
if (tree.caseKind == JCCase.STATEMENT) {
|
||||||
print(':');
|
print(':');
|
||||||
println();
|
println();
|
||||||
@ -904,10 +908,6 @@ public class Pretty extends JCTree.Visitor {
|
|||||||
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
||||||
try {
|
try {
|
||||||
print(tree.pat);
|
print(tree.pat);
|
||||||
if (tree.guard != null) {
|
|
||||||
print(" when ");
|
|
||||||
print(tree.guard);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
}
|
}
|
||||||
@ -941,17 +941,6 @@ public class Pretty extends JCTree.Visitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitParenthesizedPattern(JCParenthesizedPattern patt) {
|
|
||||||
try {
|
|
||||||
print('(');
|
|
||||||
printExpr(patt.pattern);
|
|
||||||
print(')');
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new UncheckedIOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitRecordPattern(JCRecordPattern tree) {
|
public void visitRecordPattern(JCRecordPattern tree) {
|
||||||
try {
|
try {
|
||||||
|
@ -153,6 +153,7 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
|
|||||||
public JCTree visitCase(CaseTree node, P p) {
|
public JCTree visitCase(CaseTree node, P p) {
|
||||||
JCCase t = (JCCase) node;
|
JCCase t = (JCCase) node;
|
||||||
List<JCCaseLabel> labels = copy(t.labels, p);
|
List<JCCaseLabel> labels = copy(t.labels, p);
|
||||||
|
JCExpression guard = copy(t.guard, p);
|
||||||
List<JCStatement> stats = copy(t.stats, p);
|
List<JCStatement> stats = copy(t.stats, p);
|
||||||
JCTree body;
|
JCTree body;
|
||||||
if (node.getCaseKind() == CaseTree.CaseKind.RULE) {
|
if (node.getCaseKind() == CaseTree.CaseKind.RULE) {
|
||||||
@ -161,7 +162,7 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
|
|||||||
} else {
|
} else {
|
||||||
body = null;
|
body = null;
|
||||||
}
|
}
|
||||||
return M.at(t.pos).Case(t.caseKind, labels, stats, body);
|
return M.at(t.pos).Case(t.caseKind, labels, guard, stats, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DefinedBy(Api.COMPILER_TREE)
|
@DefinedBy(Api.COMPILER_TREE)
|
||||||
@ -223,10 +224,10 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
|
|||||||
@DefinedBy(Api.COMPILER_TREE)
|
@DefinedBy(Api.COMPILER_TREE)
|
||||||
public JCTree visitEnhancedForLoop(EnhancedForLoopTree node, P p) {
|
public JCTree visitEnhancedForLoop(EnhancedForLoopTree node, P p) {
|
||||||
JCEnhancedForLoop t = (JCEnhancedForLoop) node;
|
JCEnhancedForLoop t = (JCEnhancedForLoop) node;
|
||||||
JCTree varOrRecordPattern = copy(t.varOrRecordPattern, p);
|
JCVariableDecl var = copy(t.var, p);
|
||||||
JCExpression expr = copy(t.expr, p);
|
JCExpression expr = copy(t.expr, p);
|
||||||
JCStatement body = copy(t.body, p);
|
JCStatement body = copy(t.body, p);
|
||||||
return M.at(t.pos).ForeachLoop(varOrRecordPattern, expr, body);
|
return M.at(t.pos).ForeachLoop(var, expr, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DefinedBy(Api.COMPILER_TREE)
|
@DefinedBy(Api.COMPILER_TREE)
|
||||||
@ -505,13 +506,6 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
|
|||||||
return M.at(t.pos).BindingPattern(var);
|
return M.at(t.pos).BindingPattern(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DefinedBy(Api.COMPILER_TREE)
|
|
||||||
public JCTree visitParenthesizedPattern(ParenthesizedPatternTree node, P p) {
|
|
||||||
JCParenthesizedPattern t = (JCParenthesizedPattern) node;
|
|
||||||
JCPattern pattern = copy(t.pattern, p);
|
|
||||||
return M.at(t.pos).ParenthesizedPattern(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
@DefinedBy(Api.COMPILER_TREE)
|
@DefinedBy(Api.COMPILER_TREE)
|
||||||
public JCTree visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) {
|
public JCTree visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) {
|
||||||
JCDefaultCaseLabel t = (JCDefaultCaseLabel) node;
|
JCDefaultCaseLabel t = (JCDefaultCaseLabel) node;
|
||||||
@ -529,8 +523,7 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
|
|||||||
public JCTree visitPatternCaseLabel(PatternCaseLabelTree node, P p) {
|
public JCTree visitPatternCaseLabel(PatternCaseLabelTree node, P p) {
|
||||||
JCPatternCaseLabel t = (JCPatternCaseLabel) node;
|
JCPatternCaseLabel t = (JCPatternCaseLabel) node;
|
||||||
JCPattern pat = copy(t.pat, p);
|
JCPattern pat = copy(t.pat, p);
|
||||||
JCExpression guard = copy(t.guard, p);
|
return M.at(t.pos).PatternCaseLabel(pat);
|
||||||
return M.at(t.pos).PatternCaseLabel(pat, guard);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DefinedBy(Api.COMPILER_TREE)
|
@DefinedBy(Api.COMPILER_TREE)
|
||||||
|
@ -648,10 +648,6 @@ public class TreeInfo {
|
|||||||
return getEndPos(((JCWhileLoop) tree).body, endPosTable);
|
return getEndPos(((JCWhileLoop) tree).body, endPosTable);
|
||||||
case ANNOTATED_TYPE:
|
case ANNOTATED_TYPE:
|
||||||
return getEndPos(((JCAnnotatedType) tree).underlyingType, endPosTable);
|
return getEndPos(((JCAnnotatedType) tree).underlyingType, endPosTable);
|
||||||
case PARENTHESIZEDPATTERN: {
|
|
||||||
JCParenthesizedPattern node = (JCParenthesizedPattern) tree;
|
|
||||||
return getEndPos(node.pattern, endPosTable);
|
|
||||||
}
|
|
||||||
case ERRONEOUS: {
|
case ERRONEOUS: {
|
||||||
JCErroneous node = (JCErroneous)tree;
|
JCErroneous node = (JCErroneous)tree;
|
||||||
if (node.errs != null && node.errs.nonEmpty())
|
if (node.errs != null && node.errs.nonEmpty())
|
||||||
@ -855,15 +851,6 @@ public class TreeInfo {
|
|||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Skip parens and return the enclosed expression
|
|
||||||
*/
|
|
||||||
public static JCPattern skipParens(JCPattern tree) {
|
|
||||||
while (tree.hasTag(PARENTHESIZEDPATTERN)) {
|
|
||||||
tree = ((JCParenthesizedPattern) tree).pattern;
|
|
||||||
}
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return the types of a list of trees.
|
/** Return the types of a list of trees.
|
||||||
*/
|
*/
|
||||||
public static List<Type> types(List<? extends JCTree> trees) {
|
public static List<Type> types(List<? extends JCTree> trees) {
|
||||||
@ -1358,7 +1345,6 @@ public class TreeInfo {
|
|||||||
public static Type primaryPatternType(JCTree pat) {
|
public static Type primaryPatternType(JCTree pat) {
|
||||||
return switch (pat.getTag()) {
|
return switch (pat.getTag()) {
|
||||||
case BINDINGPATTERN -> pat.type;
|
case BINDINGPATTERN -> pat.type;
|
||||||
case PARENTHESIZEDPATTERN -> primaryPatternType(((JCParenthesizedPattern) pat).pattern);
|
|
||||||
case RECORDPATTERN -> ((JCRecordPattern) pat).type;
|
case RECORDPATTERN -> ((JCRecordPattern) pat).type;
|
||||||
default -> throw new AssertionError();
|
default -> throw new AssertionError();
|
||||||
};
|
};
|
||||||
@ -1367,7 +1353,6 @@ public class TreeInfo {
|
|||||||
public static JCTree primaryPatternTypeTree(JCTree pat) {
|
public static JCTree primaryPatternTypeTree(JCTree pat) {
|
||||||
return switch (pat.getTag()) {
|
return switch (pat.getTag()) {
|
||||||
case BINDINGPATTERN -> ((JCBindingPattern) pat).var.vartype;
|
case BINDINGPATTERN -> ((JCBindingPattern) pat).var.vartype;
|
||||||
case PARENTHESIZEDPATTERN -> primaryPatternTypeTree(((JCParenthesizedPattern) pat).pattern);
|
|
||||||
case RECORDPATTERN -> ((JCRecordPattern) pat).deconstructor;
|
case RECORDPATTERN -> ((JCRecordPattern) pat).deconstructor;
|
||||||
default -> throw new AssertionError();
|
default -> throw new AssertionError();
|
||||||
};
|
};
|
||||||
@ -1380,11 +1365,8 @@ public class TreeInfo {
|
|||||||
.anyMatch(l -> TreeInfo.isNullCaseLabel(l));
|
.anyMatch(l -> TreeInfo.isNullCaseLabel(l));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean unguardedCaseLabel(JCCaseLabel cse) {
|
public static boolean unguardedCase(JCCase cse) {
|
||||||
if (!cse.hasTag(PATTERNCASELABEL)) {
|
JCExpression guard = cse.guard;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
JCExpression guard = ((JCPatternCaseLabel) cse).guard;
|
|
||||||
if (guard == null) {
|
if (guard == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -274,8 +274,8 @@ public class TreeMaker implements JCTree.Factory {
|
|||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JCEnhancedForLoop ForeachLoop(JCTree varOrRecordPattern, JCExpression expr, JCStatement body) {
|
public JCEnhancedForLoop ForeachLoop(JCVariableDecl var, JCExpression expr, JCStatement body) {
|
||||||
JCEnhancedForLoop tree = new JCEnhancedForLoop(varOrRecordPattern, expr, body);
|
JCEnhancedForLoop tree = new JCEnhancedForLoop(var, expr, body);
|
||||||
tree.pos = pos;
|
tree.pos = pos;
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
@ -293,8 +293,8 @@ public class TreeMaker implements JCTree.Factory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public JCCase Case(CaseTree.CaseKind caseKind, List<JCCaseLabel> labels,
|
public JCCase Case(CaseTree.CaseKind caseKind, List<JCCaseLabel> labels,
|
||||||
List<JCStatement> stats, JCTree body) {
|
JCExpression guard, List<JCStatement> stats, JCTree body) {
|
||||||
JCCase tree = new JCCase(caseKind, labels, stats, body);
|
JCCase tree = new JCCase(caseKind, labels, guard, stats, body);
|
||||||
tree.pos = pos;
|
tree.pos = pos;
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
@ -501,14 +501,8 @@ public class TreeMaker implements JCTree.Factory {
|
|||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JCPatternCaseLabel PatternCaseLabel(JCPattern pat, JCExpression guard) {
|
public JCPatternCaseLabel PatternCaseLabel(JCPattern pat) {
|
||||||
JCPatternCaseLabel tree = new JCPatternCaseLabel(pat, guard);
|
JCPatternCaseLabel tree = new JCPatternCaseLabel(pat);
|
||||||
tree.pos = pos;
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JCParenthesizedPattern ParenthesizedPattern(JCPattern pattern) {
|
|
||||||
JCParenthesizedPattern tree = new JCParenthesizedPattern(pattern);
|
|
||||||
tree.pos = pos;
|
tree.pos = pos;
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ public class TreeScanner extends Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||||
scan(tree.varOrRecordPattern);
|
scan(tree.var);
|
||||||
scan(tree.expr);
|
scan(tree.expr);
|
||||||
scan(tree.body);
|
scan(tree.body);
|
||||||
}
|
}
|
||||||
@ -178,6 +178,7 @@ public class TreeScanner extends Visitor {
|
|||||||
|
|
||||||
public void visitCase(JCCase tree) {
|
public void visitCase(JCCase tree) {
|
||||||
scan(tree.labels);
|
scan(tree.labels);
|
||||||
|
scan(tree.guard);
|
||||||
scan(tree.stats);
|
scan(tree.stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,12 +320,6 @@ public class TreeScanner extends Visitor {
|
|||||||
@Override
|
@Override
|
||||||
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
||||||
scan(tree.pat);
|
scan(tree.pat);
|
||||||
scan(tree.guard);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
|
|
||||||
scan(tree.pattern);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -189,7 +189,7 @@ public class TreeTranslator extends JCTree.Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||||
tree.varOrRecordPattern = translate(tree.varOrRecordPattern);
|
tree.var = translate(tree.var);
|
||||||
tree.expr = translate(tree.expr);
|
tree.expr = translate(tree.expr);
|
||||||
tree.body = translate(tree.body);
|
tree.body = translate(tree.body);
|
||||||
result = tree;
|
result = tree;
|
||||||
@ -208,6 +208,7 @@ public class TreeTranslator extends JCTree.Visitor {
|
|||||||
|
|
||||||
public void visitCase(JCCase tree) {
|
public void visitCase(JCCase tree) {
|
||||||
tree.labels = translate(tree.labels);
|
tree.labels = translate(tree.labels);
|
||||||
|
tree.guard = translate(tree.guard);
|
||||||
tree.stats = translate(tree.stats);
|
tree.stats = translate(tree.stats);
|
||||||
result = tree;
|
result = tree;
|
||||||
}
|
}
|
||||||
@ -377,13 +378,6 @@ public class TreeTranslator extends JCTree.Visitor {
|
|||||||
@Override
|
@Override
|
||||||
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
|
||||||
tree.pat = translate(tree.pat);
|
tree.pat = translate(tree.pat);
|
||||||
tree.guard = translate(tree.guard);
|
|
||||||
result = tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
|
|
||||||
tree.pattern = translate(tree.pattern);
|
|
||||||
result = tree;
|
result = tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,9 +92,11 @@ public class Names {
|
|||||||
public final Name hasNext;
|
public final Name hasNext;
|
||||||
public final Name hashCode;
|
public final Name hashCode;
|
||||||
public final Name init;
|
public final Name init;
|
||||||
|
public final Name invoke;
|
||||||
public final Name iterator;
|
public final Name iterator;
|
||||||
public final Name length;
|
public final Name length;
|
||||||
public final Name next;
|
public final Name next;
|
||||||
|
public final Name of;
|
||||||
public final Name ordinal;
|
public final Name ordinal;
|
||||||
public final Name provider;
|
public final Name provider;
|
||||||
public final Name serialVersionUID;
|
public final Name serialVersionUID;
|
||||||
@ -221,6 +223,7 @@ public class Names {
|
|||||||
// pattern switches
|
// pattern switches
|
||||||
public final Name typeSwitch;
|
public final Name typeSwitch;
|
||||||
public final Name enumSwitch;
|
public final Name enumSwitch;
|
||||||
|
public final Name enumConstant;
|
||||||
|
|
||||||
// templated string
|
// templated string
|
||||||
public final Name process;
|
public final Name process;
|
||||||
@ -282,9 +285,11 @@ public class Names {
|
|||||||
hasNext = fromString("hasNext");
|
hasNext = fromString("hasNext");
|
||||||
hashCode = fromString("hashCode");
|
hashCode = fromString("hashCode");
|
||||||
init = fromString("<init>");
|
init = fromString("<init>");
|
||||||
|
invoke = fromString("invoke");
|
||||||
iterator = fromString("iterator");
|
iterator = fromString("iterator");
|
||||||
length = fromString("length");
|
length = fromString("length");
|
||||||
next = fromString("next");
|
next = fromString("next");
|
||||||
|
of = fromString("of");
|
||||||
ordinal = fromString("ordinal");
|
ordinal = fromString("ordinal");
|
||||||
provider = fromString("provider");
|
provider = fromString("provider");
|
||||||
serialVersionUID = fromString("serialVersionUID");
|
serialVersionUID = fromString("serialVersionUID");
|
||||||
@ -415,6 +420,7 @@ public class Names {
|
|||||||
// pattern switches
|
// pattern switches
|
||||||
typeSwitch = fromString("typeSwitch");
|
typeSwitch = fromString("typeSwitch");
|
||||||
enumSwitch = fromString("enumSwitch");
|
enumSwitch = fromString("enumSwitch");
|
||||||
|
enumConstant = fromString("enumConstant");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Name.Table createTable(Options options) {
|
protected Name.Table createTable(Options options) {
|
||||||
|
@ -24,15 +24,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.lang.Enum.EnumDesc;
|
||||||
|
import java.lang.constant.ClassDesc;
|
||||||
import java.lang.invoke.CallSite;
|
import java.lang.invoke.CallSite;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.runtime.SwitchBootstraps;
|
import java.lang.runtime.SwitchBootstraps;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
import static org.testng.Assert.assertFalse;
|
||||||
|
import static org.testng.Assert.assertTrue;
|
||||||
import static org.testng.Assert.fail;
|
import static org.testng.Assert.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -158,6 +163,28 @@ public class SwitchBootstrapsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSwitchLabelTypes() throws Throwable {
|
||||||
|
enum E {A}
|
||||||
|
try {
|
||||||
|
testType(E.A, 0, -1, E.A);
|
||||||
|
fail("Didn't get the expected exception.");
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
//OK, expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSwitchQualifiedEnum() throws Throwable {
|
||||||
|
enum E {A, B, C}
|
||||||
|
Object[] labels = new Object[] {
|
||||||
|
EnumDesc.of(ClassDesc.of(E.class.getName()), "A"),
|
||||||
|
EnumDesc.of(ClassDesc.of(E.class.getName()), "B"),
|
||||||
|
EnumDesc.of(ClassDesc.of(E.class.getName()), "C")
|
||||||
|
};
|
||||||
|
testType(E.A, 0, 0, labels);
|
||||||
|
testType(E.B, 0, 1, labels);
|
||||||
|
testType(E.C, 0, 2, labels);
|
||||||
|
}
|
||||||
|
|
||||||
public void testNullLabels() throws Throwable {
|
public void testNullLabels() throws Throwable {
|
||||||
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
|
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
|
||||||
try {
|
try {
|
||||||
@ -188,4 +215,100 @@ public class SwitchBootstrapsTest {
|
|||||||
//OK
|
//OK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static AtomicBoolean enumInitialized = new AtomicBoolean();
|
||||||
|
public void testEnumInitialization1() throws Throwable {
|
||||||
|
enumInitialized.set(false);
|
||||||
|
|
||||||
|
enum E {
|
||||||
|
A;
|
||||||
|
|
||||||
|
static {
|
||||||
|
enumInitialized.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodType enumSwitchType = MethodType.methodType(int.class, E.class, int.class);
|
||||||
|
|
||||||
|
CallSite invocation = (CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, new Object[] {"A"});
|
||||||
|
assertFalse(enumInitialized.get());
|
||||||
|
assertEquals(invocation.dynamicInvoker().invoke(null, 0), -1);
|
||||||
|
assertFalse(enumInitialized.get());
|
||||||
|
E e = E.A;
|
||||||
|
assertTrue(enumInitialized.get());
|
||||||
|
assertEquals(invocation.dynamicInvoker().invoke(e, 0), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEnumInitialization2() throws Throwable {
|
||||||
|
enumInitialized.set(false);
|
||||||
|
|
||||||
|
enum E {
|
||||||
|
A;
|
||||||
|
|
||||||
|
static {
|
||||||
|
enumInitialized.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
|
||||||
|
Object[] labels = new Object[] {
|
||||||
|
EnumDesc.of(ClassDesc.of(E.class.getName()), "A"),
|
||||||
|
"test"
|
||||||
|
};
|
||||||
|
CallSite invocation = (CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels);
|
||||||
|
assertFalse(enumInitialized.get());
|
||||||
|
assertEquals(invocation.dynamicInvoker().invoke(null, 0), -1);
|
||||||
|
assertFalse(enumInitialized.get());
|
||||||
|
assertEquals(invocation.dynamicInvoker().invoke("test", 0), 1);
|
||||||
|
assertFalse(enumInitialized.get());
|
||||||
|
E e = E.A;
|
||||||
|
assertTrue(enumInitialized.get());
|
||||||
|
assertEquals(invocation.dynamicInvoker().invoke(e, 0), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIncorrectEnumLabels() throws Throwable {
|
||||||
|
try {
|
||||||
|
testEnum(E1.B, 0, -1, "B", 1);
|
||||||
|
fail("Didn't get the expected exception.");
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
//OK
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
testEnum(E1.B, 0, -1, "B", null);
|
||||||
|
fail("Didn't get the expected exception.");
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
//OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIncorrectEnumStartIndex() throws Throwable {
|
||||||
|
try {
|
||||||
|
testEnum(E1.B, -1, -1, "B");
|
||||||
|
fail("Didn't get the expected exception.");
|
||||||
|
} catch (IndexOutOfBoundsException ex) {
|
||||||
|
//OK
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
testEnum(E1.B, 2, -1, "B");
|
||||||
|
fail("Didn't get the expected exception.");
|
||||||
|
} catch (IndexOutOfBoundsException ex) {
|
||||||
|
//OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIncorrectTypeStartIndex() throws Throwable {
|
||||||
|
try {
|
||||||
|
testType("", -1, -1, "");
|
||||||
|
fail("Didn't get the expected exception.");
|
||||||
|
} catch (IndexOutOfBoundsException ex) {
|
||||||
|
//OK
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
testType("", 2, -1, "");
|
||||||
|
fail("Didn't get the expected exception.");
|
||||||
|
} catch (IndexOutOfBoundsException ex) {
|
||||||
|
//OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -99,10 +99,7 @@ public class ConditionalExpressionResolvePending extends ComboInstance<Condition
|
|||||||
case "FALSE" -> False;
|
case "FALSE" -> False;
|
||||||
case "SNIPPET" -> snippet;
|
case "SNIPPET" -> snippet;
|
||||||
default -> throw new UnsupportedOperationException(pname);
|
default -> throw new UnsupportedOperationException(pname);
|
||||||
})
|
});
|
||||||
.withOption("--enable-preview")
|
|
||||||
.withOption("-source")
|
|
||||||
.withOption(String.valueOf(Runtime.version().feature()));
|
|
||||||
|
|
||||||
task.generate(result -> {
|
task.generate(result -> {
|
||||||
try {
|
try {
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
* @test
|
* @test
|
||||||
* @bug 8286797
|
* @bug 8286797
|
||||||
* @summary Guards of constant value false are not permitted
|
* @summary Guards of constant value false are not permitted
|
||||||
* @compile/fail/ref=T8286797.out --enable-preview -source ${jdk.version} -XDrawDiagnostics -XDshould-stop.at=FLOW T8286797.java
|
* @compile/fail/ref=T8286797.out -XDrawDiagnostics -XDshould-stop.at=FLOW T8286797.java
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class T8286797 {
|
public class T8286797 {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
T8286797.java:35:32: compiler.err.guard.has.constant.expression.false
|
T8286797.java:35:32: compiler.err.guard.has.constant.expression.false
|
||||||
T8286797.java:43:34: compiler.err.guard.has.constant.expression.false
|
T8286797.java:43:34: compiler.err.guard.has.constant.expression.false
|
||||||
- compiler.note.preview.filename: T8286797.java, DEFAULT
|
|
||||||
- compiler.note.preview.recompile
|
|
||||||
2 errors
|
2 errors
|
@ -25,7 +25,7 @@
|
|||||||
* @bug 8295447
|
* @bug 8295447
|
||||||
* @summary NullPointerException with invalid pattern matching construct in constructor call
|
* @summary NullPointerException with invalid pattern matching construct in constructor call
|
||||||
* @modules jdk.compiler
|
* @modules jdk.compiler
|
||||||
* @compile/fail/ref=T8295447.out -XDrawDiagnostics --enable-preview -source ${jdk.version} T8295447.java
|
* @compile/fail/ref=T8295447.out -XDrawDiagnostics T8295447.java
|
||||||
*/
|
*/
|
||||||
public class T8295447 {
|
public class T8295447 {
|
||||||
class Foo {
|
class Foo {
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
T8295447.java:33:29: compiler.err.deconstruction.pattern.only.records: T8295447.Foo
|
T8295447.java:33:29: compiler.err.deconstruction.pattern.only.records: T8295447.Foo
|
||||||
T8295447.java:37:29: compiler.err.deconstruction.pattern.only.records: T8295447.Foo
|
T8295447.java:37:29: compiler.err.deconstruction.pattern.only.records: T8295447.Foo
|
||||||
T8295447.java:44:44: compiler.err.deconstruction.pattern.only.records: java.awt.Point
|
T8295447.java:44:44: compiler.err.deconstruction.pattern.only.records: java.awt.Point
|
||||||
- compiler.note.preview.filename: T8295447.java, DEFAULT
|
|
||||||
- compiler.note.preview.recompile
|
|
||||||
3 errors
|
3 errors
|
@ -28,7 +28,6 @@
|
|||||||
* @modules java.compiler
|
* @modules java.compiler
|
||||||
* jdk.jdeps/com.sun.tools.javap
|
* jdk.jdeps/com.sun.tools.javap
|
||||||
* @build toolbox.JavapTask
|
* @build toolbox.JavapTask
|
||||||
* @enablePreview
|
|
||||||
* @run main Patterns
|
* @run main Patterns
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ public class RuleSwitchBreaks extends LineNumberTestBase {
|
|||||||
""",
|
""",
|
||||||
List.of(1, 3, 4, 5, 6, 7, 9, 11),
|
List.of(1, 3, 4, 5, 6, 7, 9, 11),
|
||||||
true,
|
true,
|
||||||
List.of("--enable-preview", "-source", System.getProperty("java.specification.version")),
|
List.of(),
|
||||||
"TestGuards")
|
"TestGuards")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -141,6 +141,7 @@ compiler.warn.invalid.path # this warning is genera
|
|||||||
compiler.err.invalid.path # this error is generated only in Windows systems
|
compiler.err.invalid.path # this error is generated only in Windows systems
|
||||||
compiler.note.multiple.elements # needs user code
|
compiler.note.multiple.elements # needs user code
|
||||||
compiler.err.preview.feature.disabled.classfile # preview feature support: needs compilation against classfile
|
compiler.err.preview.feature.disabled.classfile # preview feature support: needs compilation against classfile
|
||||||
|
compiler.warn.preview.feature.use # preview feature support: not generated currently
|
||||||
compiler.warn.preview.feature.use.classfile # preview feature support: needs compilation against classfile
|
compiler.warn.preview.feature.use.classfile # preview feature support: needs compilation against classfile
|
||||||
compiler.note.preview.plural.additional # preview feature support: diag test causes intermittent failures (see JDK-8201498)
|
compiler.note.preview.plural.additional # preview feature support: diag test causes intermittent failures (see JDK-8201498)
|
||||||
compiler.misc.bad.intersection.target.for.functional.expr # currently not generated, should be removed?
|
compiler.misc.bad.intersection.target.for.functional.expr # currently not generated, should be removed?
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -21,32 +21,14 @@
|
|||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
// key: compiler.err.cannot.assign.not.declared.guard
|
||||||
* @test
|
|
||||||
* @bug 8297602
|
|
||||||
* @summary Compiler crash with type annotation and generic record during pattern matching
|
|
||||||
* @enablePreview
|
|
||||||
* @compile --enable-preview -source ${jdk.version} -XDrawDiagnostics T8297602.java
|
|
||||||
*/
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
public class T8297602
|
|
||||||
{
|
|
||||||
void meth(Foo<Integer> p) {
|
|
||||||
switch(p) {
|
|
||||||
case Foo<@Annot(field = "") Integer>(): {}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (p instanceof Foo<@Annot(field = "") Integer>()) {
|
|
||||||
|
|
||||||
|
class CannotAssignNotDeclaredGuard {
|
||||||
|
void test(Object i) {
|
||||||
|
final boolean b;
|
||||||
|
switch (i) {
|
||||||
|
case Object o when b = true -> {}
|
||||||
|
default -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Target({ElementType.TYPE_USE})
|
|
||||||
@interface Annot {
|
|
||||||
String field();
|
|
||||||
}
|
|
||||||
|
|
||||||
record Foo<T>() { }
|
|
||||||
}
|
}
|
@ -25,9 +25,6 @@
|
|||||||
// key: compiler.misc.inner.cls
|
// key: compiler.misc.inner.cls
|
||||||
// key: compiler.misc.lambda
|
// key: compiler.misc.lambda
|
||||||
// key: compiler.misc.guard
|
// key: compiler.misc.guard
|
||||||
// key: compiler.misc.feature.pattern.switch
|
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class CantRefNonEffectivelyFinalVar {
|
class CantRefNonEffectivelyFinalVar {
|
||||||
void test() {
|
void test() {
|
||||||
|
@ -21,9 +21,9 @@
|
|||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// key: compiler.err.feature.not.supported.in.source
|
||||||
// key: compiler.misc.feature.case.null
|
// key: compiler.misc.feature.case.null
|
||||||
// key: compiler.warn.preview.feature.use
|
// options: -source 20 -Xlint:-options
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class CaseNull {
|
class CaseNull {
|
||||||
private void doSwitch(String s) {
|
private void doSwitch(String s) {
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.constant.label.not.compatible
|
// key: compiler.err.constant.label.not.compatible
|
||||||
// key: compiler.misc.feature.pattern.switch
|
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class ConstantLabelNotCompatible {
|
class ConstantLabelNotCompatible {
|
||||||
private void doSwitch(Object o) {
|
private void doSwitch(Object o) {
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.deconstruction.pattern.only.records
|
// key: compiler.err.deconstruction.pattern.only.records
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
|
||||||
// key: compiler.misc.feature.deconstruction.patterns
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class DeconstructionpatternOnlyRecords {
|
class DeconstructionpatternOnlyRecords {
|
||||||
public boolean deconstruction(Object o) {
|
public boolean deconstruction(Object o) {
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.default.label.not.allowed
|
// key: compiler.err.default.label.not.allowed
|
||||||
// key: compiler.misc.feature.pattern.switch
|
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class DefaultLabelNotAllowed {
|
class DefaultLabelNotAllowed {
|
||||||
private void doSwitch(Object o) {
|
private void doSwitch(Object o) {
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.duplicate.unconditional.pattern
|
// key: compiler.err.duplicate.unconditional.pattern
|
||||||
// key: compiler.misc.feature.pattern.switch
|
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class DuplicateUnconditionalPattern {
|
class DuplicateUnconditionalPattern {
|
||||||
private void doSwitch(Object o) {
|
private void doSwitch(Object o) {
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.enum.label.must.be.unqualified.enum
|
// key: compiler.err.enum.label.must.be.unqualified.enum
|
||||||
|
// options: --release 20
|
||||||
|
|
||||||
|
|
||||||
class EnumLabelUnqualified {
|
class EnumLabelUnqualified {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -21,20 +21,13 @@
|
|||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// key: compiler.err.feature.not.supported.in.source.plural
|
||||||
// key: compiler.misc.feature.deconstruction.patterns
|
// key: compiler.misc.feature.deconstruction.patterns
|
||||||
// key: compiler.misc.feature.pattern.switch
|
// options: -source 20 -Xlint:-options
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
|
||||||
// key: compiler.err.foreach.not.exhaustive.on.type
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
import java.util.List;
|
class FeatureDeconstructionPatterns {
|
||||||
|
Object o;
|
||||||
|
boolean b = o instanceof R(String s);
|
||||||
|
|
||||||
class ForeachNotExhaustive {
|
record R(Object o) {}
|
||||||
void m(List<Object> points) {
|
|
||||||
for (Point(var x, var y): points) {
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
record Point(Integer x, Integer y) { }
|
|
||||||
}
|
}
|
@ -21,11 +21,9 @@
|
|||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// key: compiler.err.feature.not.supported.in.source.plural
|
||||||
// key: compiler.misc.feature.unconditional.patterns.in.instanceof
|
// key: compiler.misc.feature.unconditional.patterns.in.instanceof
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
// options: -source 20 -Xlint:-options
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:-options,preview
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
class FeatureUnconditionalTypesInstanceof {
|
class FeatureUnconditionalTypesInstanceof {
|
||||||
String s;
|
String s;
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.flows.through.from.pattern
|
// key: compiler.err.flows.through.from.pattern
|
||||||
// key: compiler.misc.feature.pattern.switch
|
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class FlowsThroughToPattern {
|
class FlowsThroughToPattern {
|
||||||
private void doSwitch(Object o) {
|
private void doSwitch(Object o) {
|
||||||
|
@ -22,11 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.flows.through.to.pattern
|
// key: compiler.err.flows.through.to.pattern
|
||||||
// key: compiler.misc.feature.pattern.switch
|
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
|
||||||
// key: compiler.misc.feature.case.null
|
|
||||||
// key: compiler.warn.preview.feature.use
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class FlowsThroughToPattern {
|
class FlowsThroughToPattern {
|
||||||
private void doSwitch(Object o) {
|
private void doSwitch(Object o) {
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.guard.has.constant.expression.false
|
// key: compiler.err.guard.has.constant.expression.false
|
||||||
// key: compiler.misc.feature.pattern.switch
|
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class GuardHasConstantFalse {
|
class GuardHasConstantFalse {
|
||||||
private void doSwitch(Object o) {
|
private void doSwitch(Object o) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -21,10 +21,13 @@
|
|||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.instanceof.pattern.no.subtype
|
// key: compiler.err.guard.not.allowed
|
||||||
|
|
||||||
class InstanceofPatternNoSubtype {
|
class GuardNotAllowed {
|
||||||
boolean test(Object o) {
|
private void doSwitch(int i, boolean b) {
|
||||||
return o instanceof Object obj;
|
switch (i) {
|
||||||
|
case 0 when b -> {}
|
||||||
|
default -> {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.incorrect.number.of.nested.patterns
|
// key: compiler.err.incorrect.number.of.nested.patterns
|
||||||
// key: compiler.misc.feature.deconstruction.patterns
|
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class IncorrectNumberOfNestedPatterns {
|
class IncorrectNumberOfNestedPatterns {
|
||||||
private boolean t(Object o) {
|
private boolean t(Object o) {
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.invalid.case.label.combination
|
// key: compiler.err.invalid.case.label.combination
|
||||||
// key: compiler.misc.feature.case.null
|
|
||||||
// key: compiler.warn.preview.feature.use
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class InvalidCaseLabelCombination {
|
class InvalidCaseLabelCombination {
|
||||||
private void doSwitch(Integer i) {
|
private void doSwitch(Integer i) {
|
||||||
|
@ -23,9 +23,6 @@
|
|||||||
|
|
||||||
// key: compiler.misc.not.applicable.types
|
// key: compiler.misc.not.applicable.types
|
||||||
// key: compiler.err.prob.found.req
|
// key: compiler.err.prob.found.req
|
||||||
// key: compiler.note.preview.filename
|
|
||||||
// key: compiler.note.preview.recompile
|
|
||||||
// options: --enable-preview --source ${jdk.version}
|
|
||||||
|
|
||||||
class NotApplicableTypes {
|
class NotApplicableTypes {
|
||||||
void t(int i) {
|
void t(int i) {
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.not.exhaustive.statement
|
// key: compiler.err.not.exhaustive.statement
|
||||||
// key: compiler.note.preview.filename
|
|
||||||
// key: compiler.note.preview.recompile
|
|
||||||
// options: --enable-preview --source ${jdk.version}
|
|
||||||
|
|
||||||
class NotExhaustive {
|
class NotExhaustive {
|
||||||
void t(Object o) {
|
void t(Object o) {
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.pattern.dominated
|
// key: compiler.err.pattern.dominated
|
||||||
// key: compiler.misc.feature.pattern.switch
|
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class PatternDominated {
|
class PatternDominated {
|
||||||
private void doSwitch(Object o) {
|
private void doSwitch(Object o) {
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.pattern.expected
|
// key: compiler.err.pattern.expected
|
||||||
// key: compiler.note.preview.filename
|
|
||||||
// key: compiler.note.preview.recompile
|
|
||||||
// options: --enable-preview -source ${jdk.version}
|
|
||||||
|
|
||||||
class PatternSwitch {
|
class PatternSwitch {
|
||||||
private void doSwitch(Object o) {
|
private void doSwitch(Object o) {
|
||||||
|
@ -21,9 +21,9 @@
|
|||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// key: compiler.err.feature.not.supported.in.source.plural
|
||||||
// key: compiler.misc.feature.pattern.switch
|
// key: compiler.misc.feature.pattern.switch
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
// options: -source 20 -Xlint:-options
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class PatternSwitch {
|
class PatternSwitch {
|
||||||
private void doSwitch(Object o) {
|
private void doSwitch(Object o) {
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.pattern.type.cannot.infer
|
// key: compiler.err.pattern.type.cannot.infer
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
|
||||||
// key: compiler.misc.feature.deconstruction.patterns
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class PatternTypeCannotInfer {
|
class PatternTypeCannotInfer {
|
||||||
interface A<T> {}
|
interface A<T> {}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// key: compiler.err.record.patterns.annotations.not.allowed
|
||||||
|
|
||||||
|
class RecordPatternsAnnotationsNotAllowed {
|
||||||
|
|
||||||
|
public boolean test(Object o) {
|
||||||
|
return o instanceof @Deprecated R(String s);
|
||||||
|
}
|
||||||
|
|
||||||
|
record R(String s) {}
|
||||||
|
}
|
@ -22,9 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// key: compiler.err.unconditional.pattern.and.default
|
// key: compiler.err.unconditional.pattern.and.default
|
||||||
// key: compiler.misc.feature.pattern.switch
|
|
||||||
// key: compiler.warn.preview.feature.use.plural
|
|
||||||
// options: --enable-preview -source ${jdk.version} -Xlint:preview
|
|
||||||
|
|
||||||
class UnconditionalPatternAndDefault {
|
class UnconditionalPatternAndDefault {
|
||||||
private void doSwitch(Object o) {
|
private void doSwitch(Object o) {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* @bug 4936393 8050021
|
* @bug 4936393 8050021
|
||||||
* @summary enum switch case labels required to be unqualified.
|
* @summary enum switch case labels required to be unqualified.
|
||||||
* @author gafter
|
* @author gafter
|
||||||
* @compile/fail/ref=EnumSwitch2.out -XDrawDiagnostics EnumSwitch2.java
|
* @compile/fail/ref=EnumSwitch2.out -XDrawDiagnostics --release 20 EnumSwitch2.java
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum E1 { a, b, c, d, e }
|
enum E1 { a, b, c, d, e }
|
||||||
|
@ -722,7 +722,7 @@ public class DPrinter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||||
printTree("var", tree.varOrRecordPattern);
|
printTree("var", tree.var);
|
||||||
printTree("expr", tree.expr);
|
printTree("expr", tree.expr);
|
||||||
printTree("body", tree.body);
|
printTree("body", tree.body);
|
||||||
}
|
}
|
||||||
|
@ -2342,6 +2342,59 @@ public class JavacParserTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGuardRecovery() throws IOException {
|
||||||
|
String code = """
|
||||||
|
package t;
|
||||||
|
class Test {
|
||||||
|
private int t(Integer i, boolean b) {
|
||||||
|
switch (i) {
|
||||||
|
case 0 when b -> {}
|
||||||
|
case null when b -> {}
|
||||||
|
default when b -> {}
|
||||||
|
}
|
||||||
|
return switch (i) {
|
||||||
|
case 0 when b -> 0;
|
||||||
|
case null when b -> 0;
|
||||||
|
default when b -> 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}""";
|
||||||
|
DiagnosticCollector<JavaFileObject> coll =
|
||||||
|
new DiagnosticCollector<>();
|
||||||
|
JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll, null,
|
||||||
|
null, Arrays.asList(new MyFileObject(code)));
|
||||||
|
CompilationUnitTree cut = ct.parse().iterator().next();
|
||||||
|
new TreeScanner<Void, Void>() {
|
||||||
|
@Override
|
||||||
|
public Void visitCase(CaseTree node, Void p) {
|
||||||
|
assertNotNull(node.getGuard());
|
||||||
|
assertEquals("guard kind", Kind.ERRONEOUS, node.getGuard().getKind());
|
||||||
|
assertEquals("guard content",
|
||||||
|
List.of("b"),
|
||||||
|
((ErroneousTree) node.getGuard()).getErrorTrees()
|
||||||
|
.stream()
|
||||||
|
.map(t -> t.toString()).toList());
|
||||||
|
return super.visitCase(node, p);
|
||||||
|
}
|
||||||
|
}.scan(cut, null);
|
||||||
|
|
||||||
|
List<String> codes = new LinkedList<>();
|
||||||
|
|
||||||
|
for (Diagnostic<? extends JavaFileObject> d : coll.getDiagnostics()) {
|
||||||
|
codes.add(d.getLineNumber() + ":" + d.getColumnNumber() + ":" + d.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("testUsupportedTextBlock: " + codes,
|
||||||
|
List.of("5:20:compiler.err.guard.not.allowed",
|
||||||
|
"6:23:compiler.err.guard.not.allowed",
|
||||||
|
"7:21:compiler.err.guard.not.allowed",
|
||||||
|
"10:20:compiler.err.guard.not.allowed",
|
||||||
|
"11:23:compiler.err.guard.not.allowed",
|
||||||
|
"12:21:compiler.err.guard.not.allowed"),
|
||||||
|
codes);
|
||||||
|
}
|
||||||
|
|
||||||
void run(String[] args) throws Exception {
|
void run(String[] args) throws Exception {
|
||||||
int passed = 0, failed = 0;
|
int passed = 0, failed = 0;
|
||||||
final Pattern p = (args != null && args.length > 0)
|
final Pattern p = (args != null && args.length > 0)
|
||||||
|
42
test/langtools/tools/javac/patterns/AnnotationErrors.java
Normal file
42
test/langtools/tools/javac/patterns/AnnotationErrors.java
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* @test /nodynamiccopyright/
|
||||||
|
* @bug 8300543
|
||||||
|
* @summary Verify error related to annotations and patterns
|
||||||
|
* @compile/fail/ref=AnnotationErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW AnnotationErrors.java
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
public class AnnotationErrors {
|
||||||
|
|
||||||
|
private void test(Object o, G<String> g) {
|
||||||
|
boolean b1 = o instanceof @DA R(var s);
|
||||||
|
boolean b2 = o instanceof @DTA R(var s);
|
||||||
|
boolean b3 = o instanceof @TA R(var s);
|
||||||
|
boolean b5 = g instanceof G<@DTA String>(var s);
|
||||||
|
boolean b6 = g instanceof G<@TA String>(var s);
|
||||||
|
switch (o) {
|
||||||
|
case @DA R(var s) when b1 -> {}
|
||||||
|
case @DTA R(var s) when b1 -> {}
|
||||||
|
case @TA R(var s) when b1 -> {}
|
||||||
|
default -> {}
|
||||||
|
}
|
||||||
|
switch (g) {
|
||||||
|
case G<@DTA String>(var s) when b1 -> {}
|
||||||
|
case G<@TA String>(var s) when b1 -> {}
|
||||||
|
default -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record R(String s) {}
|
||||||
|
record G<T>(T t) {}
|
||||||
|
|
||||||
|
@Target(ElementType.LOCAL_VARIABLE)
|
||||||
|
@interface DA {}
|
||||||
|
@Target({ElementType.TYPE_USE, ElementType.LOCAL_VARIABLE})
|
||||||
|
@interface DTA {}
|
||||||
|
@Target(ElementType.TYPE_USE)
|
||||||
|
@interface TA {}
|
||||||
|
}
|
||||||
|
|
11
test/langtools/tools/javac/patterns/AnnotationErrors.out
Normal file
11
test/langtools/tools/javac/patterns/AnnotationErrors.out
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
AnnotationErrors.java:14:35: compiler.err.record.patterns.annotations.not.allowed
|
||||||
|
AnnotationErrors.java:15:35: compiler.err.record.patterns.annotations.not.allowed
|
||||||
|
AnnotationErrors.java:16:35: compiler.err.record.patterns.annotations.not.allowed
|
||||||
|
AnnotationErrors.java:17:37: compiler.err.record.patterns.annotations.not.allowed
|
||||||
|
AnnotationErrors.java:18:37: compiler.err.record.patterns.annotations.not.allowed
|
||||||
|
AnnotationErrors.java:20:18: compiler.err.record.patterns.annotations.not.allowed
|
||||||
|
AnnotationErrors.java:21:18: compiler.err.record.patterns.annotations.not.allowed
|
||||||
|
AnnotationErrors.java:22:18: compiler.err.record.patterns.annotations.not.allowed
|
||||||
|
AnnotationErrors.java:26:20: compiler.err.record.patterns.annotations.not.allowed
|
||||||
|
AnnotationErrors.java:27:20: compiler.err.record.patterns.annotations.not.allowed
|
||||||
|
10 errors
|
@ -85,16 +85,14 @@ public class CaseStructureTest extends ComboInstance<CaseStructureTest> {
|
|||||||
protected void doWork() throws Throwable {
|
protected void doWork() throws Throwable {
|
||||||
String labelSeparator = asCaseLabelElements ? ", " : ": case ";
|
String labelSeparator = asCaseLabelElements ? ", " : ": case ";
|
||||||
String labels = Arrays.stream(caseLabels).filter(l -> l != CaseLabel.NONE).map(l -> l.code).collect(Collectors.joining(labelSeparator, "case ", ": break;"));
|
String labels = Arrays.stream(caseLabels).filter(l -> l != CaseLabel.NONE).map(l -> l.code).collect(Collectors.joining(labelSeparator, "case ", ": break;"));
|
||||||
boolean hasDefault = Arrays.stream(caseLabels).anyMatch(l -> l == CaseLabel.DEFAULT || l == CaseLabel.TYPE_PATTERN || l == CaseLabel.PARENTHESIZED_PATTERN);
|
boolean hasDefault = Arrays.stream(caseLabels).anyMatch(l -> l == CaseLabel.DEFAULT || l == CaseLabel.TYPE_PATTERN);
|
||||||
|
|
||||||
ComboTask task = newCompilationTask()
|
ComboTask task = newCompilationTask()
|
||||||
.withSourceFromTemplate(MAIN_TEMPLATE.replace("#{CASES}", labels).replace("#{DEFAULT}", hasDefault ? "" : "default: break;"))
|
.withSourceFromTemplate(MAIN_TEMPLATE.replace("#{CASES}", labels).replace("#{DEFAULT}", hasDefault ? "" : "default: break;"));
|
||||||
.withOption("--enable-preview")
|
|
||||||
.withOption("-source").withOption(JAVA_VERSION);
|
|
||||||
|
|
||||||
task.generate(result -> {
|
task.generate(result -> {
|
||||||
boolean shouldPass = true;
|
boolean shouldPass = true;
|
||||||
long patternCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.TYPE_PATTERN || l == CaseLabel.PARENTHESIZED_PATTERN).count();
|
long patternCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.TYPE_PATTERN).count();
|
||||||
long constantCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.CONSTANT).count();
|
long constantCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.CONSTANT).count();
|
||||||
long nullCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.NULL).count();
|
long nullCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.NULL).count();
|
||||||
long defaultCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.DEFAULT).count();
|
long defaultCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.DEFAULT).count();
|
||||||
@ -136,7 +134,6 @@ public class CaseStructureTest extends ComboInstance<CaseStructureTest> {
|
|||||||
public enum CaseLabel implements ComboParameter {
|
public enum CaseLabel implements ComboParameter {
|
||||||
NONE(""),
|
NONE(""),
|
||||||
TYPE_PATTERN("Integer i"),
|
TYPE_PATTERN("Integer i"),
|
||||||
PARENTHESIZED_PATTERN("(Integer i)"),
|
|
||||||
CONSTANT("1"),
|
CONSTANT("1"),
|
||||||
NULL("null"),
|
NULL("null"),
|
||||||
DEFAULT("default");
|
DEFAULT("default");
|
||||||
|
@ -25,8 +25,8 @@
|
|||||||
* @test
|
* @test
|
||||||
* @bug 8291769 8301858 8304694 8304883
|
* @bug 8291769 8301858 8304694 8304883
|
||||||
* @summary Verify more complex switches work properly
|
* @summary Verify more complex switches work properly
|
||||||
* @compile --enable-preview -source ${jdk.version} DeconstructionDesugaring.java
|
* @compile DeconstructionDesugaring.java
|
||||||
* @run main/othervm --enable-preview DeconstructionDesugaring
|
* @run main DeconstructionDesugaring
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -79,7 +79,7 @@ public class DeconstructionDesugaring {
|
|||||||
|
|
||||||
private int runCheckStatement(Object o) {
|
private int runCheckStatement(Object o) {
|
||||||
switch (o) {
|
switch (o) {
|
||||||
case (((R1((((R2((((String s))))))))))) -> { return 1; }
|
case R1(R2(String s)) -> { return 1; }
|
||||||
case R1(R2(Integer i)) -> { return 2; }
|
case R1(R2(Integer i)) -> { return 2; }
|
||||||
case R1(R2(Double d)) -> { return 3; }
|
case R1(R2(Double d)) -> { return 3; }
|
||||||
case R1(R2(CharSequence cs)) -> { return 4; }
|
case R1(R2(CharSequence cs)) -> { return 4; }
|
||||||
@ -93,7 +93,7 @@ public class DeconstructionDesugaring {
|
|||||||
|
|
||||||
private int runCheckExpression(Object o) {
|
private int runCheckExpression(Object o) {
|
||||||
return switch (o) {
|
return switch (o) {
|
||||||
case (((R1((((R2((((String s))))))))))) -> 1;
|
case R1(R2(String s)) -> 1;
|
||||||
case R1(R2(Integer i)) -> 2;
|
case R1(R2(Integer i)) -> 2;
|
||||||
case R1(R2(Double d)) -> 3;
|
case R1(R2(Double d)) -> 3;
|
||||||
case R1(R2(CharSequence cs)) -> 4;
|
case R1(R2(CharSequence cs)) -> 4;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* @test /nodynamiccopyright/
|
* @test /nodynamiccopyright/
|
||||||
* @summary Verify error reports for erroneous deconstruction patterns are sensible
|
* @summary Verify error reports for erroneous deconstruction patterns are sensible
|
||||||
* @enablePreview
|
|
||||||
* @compile/fail/ref=DeconstructionPatternErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW -XDdev DeconstructionPatternErrors.java
|
* @compile/fail/ref=DeconstructionPatternErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW -XDdev DeconstructionPatternErrors.java
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -26,7 +26,4 @@ DeconstructionPatternErrors.java:31:29: compiler.err.prob.found.req: (compiler.m
|
|||||||
DeconstructionPatternErrors.java:32:29: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
|
DeconstructionPatternErrors.java:32:29: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
|
||||||
DeconstructionPatternErrors.java:34: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, byte)
|
||||||
DeconstructionPatternErrors.java:35:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
|
DeconstructionPatternErrors.java:35:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
|
||||||
DeconstructionPatternErrors.java:44:9: compiler.err.not.exhaustive.statement
|
28 errors
|
||||||
- compiler.note.preview.filename: DeconstructionPatternErrors.java, DEFAULT
|
|
||||||
- compiler.note.preview.recompile
|
|
||||||
29 errors
|
|
||||||
|
@ -28,37 +28,22 @@
|
|||||||
* jdk.compiler/com.sun.tools.javac.parser
|
* jdk.compiler/com.sun.tools.javac.parser
|
||||||
* jdk.compiler/com.sun.tools.javac.tree
|
* jdk.compiler/com.sun.tools.javac.tree
|
||||||
* jdk.compiler/com.sun.tools.javac.util
|
* jdk.compiler/com.sun.tools.javac.util
|
||||||
* @enablePreview
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import com.sun.source.tree.CaseLabelTree;
|
import com.sun.source.tree.CaseLabelTree;
|
||||||
import com.sun.source.tree.ClassTree;
|
import com.sun.source.tree.ClassTree;
|
||||||
import com.sun.source.tree.CompilationUnitTree;
|
import com.sun.source.tree.CompilationUnitTree;
|
||||||
import com.sun.source.tree.ConstantCaseLabelTree;
|
import com.sun.source.tree.ConstantCaseLabelTree;
|
||||||
import com.sun.source.tree.EnhancedForLoopTree;
|
|
||||||
import com.sun.source.tree.MethodTree;
|
import com.sun.source.tree.MethodTree;
|
||||||
import com.sun.source.tree.PatternCaseLabelTree;
|
import com.sun.source.tree.PatternCaseLabelTree;
|
||||||
import com.sun.source.tree.PatternTree;
|
|
||||||
import com.sun.source.tree.StatementTree;
|
|
||||||
import com.sun.source.tree.SwitchTree;
|
import com.sun.source.tree.SwitchTree;
|
||||||
import com.sun.source.tree.Tree.Kind;
|
|
||||||
import com.sun.tools.javac.file.JavacFileManager;
|
import com.sun.tools.javac.file.JavacFileManager;
|
||||||
import com.sun.tools.javac.parser.JavacParser;
|
import com.sun.tools.javac.parser.JavacParser;
|
||||||
import com.sun.tools.javac.parser.ParserFactory;
|
import com.sun.tools.javac.parser.ParserFactory;
|
||||||
import com.sun.tools.javac.util.Context;
|
import com.sun.tools.javac.util.Context;
|
||||||
import com.sun.tools.javac.main.Option;
|
import com.sun.tools.javac.main.Option;
|
||||||
import com.sun.tools.javac.util.Log;
|
|
||||||
import com.sun.tools.javac.util.Options;
|
import com.sun.tools.javac.util.Options;
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import javax.tools.Diagnostic;
|
|
||||||
import javax.tools.DiagnosticListener;
|
|
||||||
import javax.tools.JavaFileObject;
|
|
||||||
import javax.tools.SimpleJavaFileObject;
|
|
||||||
|
|
||||||
public class DisambiguatePatterns {
|
public class DisambiguatePatterns {
|
||||||
|
|
||||||
@ -68,11 +53,11 @@ public class DisambiguatePatterns {
|
|||||||
ExpressionType.PATTERN);
|
ExpressionType.PATTERN);
|
||||||
test.disambiguationTest("String s when s.isEmpty()",
|
test.disambiguationTest("String s when s.isEmpty()",
|
||||||
ExpressionType.PATTERN);
|
ExpressionType.PATTERN);
|
||||||
test.disambiguationTest("(String s)",
|
test.disambiguationTest("String s",
|
||||||
ExpressionType.PATTERN);
|
ExpressionType.PATTERN);
|
||||||
test.disambiguationTest("(@Ann String s)",
|
test.disambiguationTest("@Ann String s",
|
||||||
ExpressionType.PATTERN);
|
ExpressionType.PATTERN);
|
||||||
test.disambiguationTest("((String s))",
|
test.disambiguationTest("String s",
|
||||||
ExpressionType.PATTERN);
|
ExpressionType.PATTERN);
|
||||||
test.disambiguationTest("(String) s",
|
test.disambiguationTest("(String) s",
|
||||||
ExpressionType.EXPRESSION);
|
ExpressionType.EXPRESSION);
|
||||||
@ -92,7 +77,7 @@ public class DisambiguatePatterns {
|
|||||||
ExpressionType.EXPRESSION);
|
ExpressionType.EXPRESSION);
|
||||||
test.disambiguationTest("(a << b || a < b | a >>> b)",
|
test.disambiguationTest("(a << b || a < b | a >>> b)",
|
||||||
ExpressionType.EXPRESSION);
|
ExpressionType.EXPRESSION);
|
||||||
test.disambiguationTest("(a < c.d > b)",
|
test.disambiguationTest("a < c.d > b",
|
||||||
ExpressionType.PATTERN);
|
ExpressionType.PATTERN);
|
||||||
test.disambiguationTest("a<? extends c.d> b",
|
test.disambiguationTest("a<? extends c.d> b",
|
||||||
ExpressionType.PATTERN);
|
ExpressionType.PATTERN);
|
||||||
@ -124,44 +109,14 @@ public class DisambiguatePatterns {
|
|||||||
ExpressionType.EXPRESSION);
|
ExpressionType.EXPRESSION);
|
||||||
test.disambiguationTest("a & b",
|
test.disambiguationTest("a & b",
|
||||||
ExpressionType.EXPRESSION);
|
ExpressionType.EXPRESSION);
|
||||||
test.forDisambiguationTest("T[] a", ForType.ENHANCED_FOR);
|
|
||||||
test.forDisambiguationTest("T[].class.getName()", ForType.TRADITIONAL_FOR);
|
|
||||||
test.forDisambiguationTest("T[].class", ForType.TRADITIONAL_FOR, "compiler.err.not.stmt");
|
|
||||||
test.forDisambiguationTest("R(T[] a)", ForType.ENHANCED_FOR_WITH_PATTERNS);
|
|
||||||
|
|
||||||
test.forDisambiguationTest("Point(Integer a, Integer b)", ForType.ENHANCED_FOR_WITH_PATTERNS);
|
|
||||||
test.forDisambiguationTest("ForEachPatterns.Point(Integer a, Integer b)", ForType.ENHANCED_FOR_WITH_PATTERNS);
|
|
||||||
test.forDisambiguationTest("GPoint<Integer>(Integer a, Integer b)", ForType.ENHANCED_FOR_WITH_PATTERNS);
|
|
||||||
test.forDisambiguationTest("@Annot(field = \"test\") Point p", ForType.ENHANCED_FOR);
|
|
||||||
test.forDisambiguationTest("GPoint<Point>(Point(Integer a, Integer b), Point c)", ForType.ENHANCED_FOR_WITH_PATTERNS);
|
|
||||||
test.forDisambiguationTest("GPoint<Point>(Point(var a, Integer b), Point c)", ForType.ENHANCED_FOR_WITH_PATTERNS);
|
|
||||||
test.forDisambiguationTest("GPoint<VoidPoint>(VoidPoint(), VoidPoint())", ForType.ENHANCED_FOR_WITH_PATTERNS);
|
|
||||||
test.forDisambiguationTest("RecordOfLists(List<Integer> lr)", ForType.ENHANCED_FOR_WITH_PATTERNS);
|
|
||||||
test.forDisambiguationTest("RecordOfLists2(List<List<Integer>> lr)", ForType.ENHANCED_FOR_WITH_PATTERNS);
|
|
||||||
test.forDisambiguationTest("GPoint<@Annot(field = \"\") ? extends Point>(var x, var y)", ForType.ENHANCED_FOR_WITH_PATTERNS);
|
|
||||||
|
|
||||||
test.forDisambiguationTest("method()", ForType.TRADITIONAL_FOR);
|
|
||||||
test.forDisambiguationTest("method(), method()", ForType.TRADITIONAL_FOR);
|
|
||||||
test.forDisambiguationTest("method2((Integer a) -> 42)", ForType.TRADITIONAL_FOR);
|
|
||||||
test.forDisambiguationTest("m(cond ? b() : i)", ForType.TRADITIONAL_FOR);
|
|
||||||
test.forDisambiguationTest("m((GPoint<?>)null, cond ? b() : i)", ForType.TRADITIONAL_FOR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ParserFactory factory;
|
private final ParserFactory factory;
|
||||||
private final List<String> errors = new ArrayList<>();
|
|
||||||
|
|
||||||
public DisambiguatePatterns() throws URISyntaxException {
|
public DisambiguatePatterns() {
|
||||||
Context context = new Context();
|
Context context = new Context();
|
||||||
context.put(DiagnosticListener.class, d -> {
|
|
||||||
if (d.getKind() == Diagnostic.Kind.ERROR) {
|
|
||||||
errors.add(d.getCode());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
JavacFileManager jfm = new JavacFileManager(context, true, Charset.defaultCharset());
|
JavacFileManager jfm = new JavacFileManager(context, true, Charset.defaultCharset());
|
||||||
Options.instance(context).put(Option.PREVIEW, "");
|
Options.instance(context).put(Option.PREVIEW, "");
|
||||||
SimpleJavaFileObject source =
|
|
||||||
new SimpleJavaFileObject(new URI("mem://Test.java"), JavaFileObject.Kind.SOURCE) {};
|
|
||||||
Log.instance(context).useSource(source);
|
|
||||||
factory = ParserFactory.instance(context);
|
factory = ParserFactory.instance(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,66 +147,9 @@ public class DisambiguatePatterns {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void forDisambiguationTest(String snippet, ForType forType, String... expectedErrors) {
|
|
||||||
errors.clear();
|
|
||||||
|
|
||||||
String codeTemplate = switch (forType) {
|
|
||||||
case TRADITIONAL_FOR ->
|
|
||||||
"""
|
|
||||||
public class Test {
|
|
||||||
private void test() {
|
|
||||||
for (SNIPPET; ;) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
case ENHANCED_FOR, ENHANCED_FOR_WITH_PATTERNS ->
|
|
||||||
"""
|
|
||||||
public class Test {
|
|
||||||
private void test() {
|
|
||||||
for (SNIPPET : collection) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
};
|
|
||||||
|
|
||||||
String code = codeTemplate.replace("SNIPPET", snippet);
|
|
||||||
JavacParser parser = factory.newParser(code, false, false, false);
|
|
||||||
CompilationUnitTree result = parser.parseCompilationUnit();
|
|
||||||
if (!Arrays.asList(expectedErrors).equals(errors)) {
|
|
||||||
throw new AssertionError("Expected errors: " + Arrays.asList(expectedErrors) +
|
|
||||||
", actual: " + errors +
|
|
||||||
", for: " + code);
|
|
||||||
}
|
|
||||||
ClassTree clazz = (ClassTree) result.getTypeDecls().get(0);
|
|
||||||
MethodTree method = (MethodTree) clazz.getMembers().get(0);
|
|
||||||
StatementTree st = method.getBody().getStatements().get(0);
|
|
||||||
if (forType == ForType.TRADITIONAL_FOR) {
|
|
||||||
if (st.getKind() != Kind.FOR_LOOP) {
|
|
||||||
throw new AssertionError("Unpected statement: " + st);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
EnhancedForLoopTree ef = (EnhancedForLoopTree) st;
|
|
||||||
ForType actualType = switch (ef.getVariableOrRecordPattern()) {
|
|
||||||
case PatternTree pattern -> ForType.ENHANCED_FOR_WITH_PATTERNS;
|
|
||||||
default -> ForType.ENHANCED_FOR;
|
|
||||||
};
|
|
||||||
if (forType != actualType) {
|
|
||||||
throw new AssertionError("Expected: " + forType + ", actual: " + actualType +
|
|
||||||
", for: " + code + ", parsed: " + result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ExpressionType {
|
enum ExpressionType {
|
||||||
PATTERN,
|
PATTERN,
|
||||||
EXPRESSION;
|
EXPRESSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ForType {
|
|
||||||
TRADITIONAL_FOR,
|
|
||||||
ENHANCED_FOR,
|
|
||||||
ENHANCED_FOR_WITH_PATTERNS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,9 @@
|
|||||||
* @test
|
* @test
|
||||||
* @bug 8262891 8290709
|
* @bug 8262891 8290709
|
||||||
* @summary Check the pattern domination error are reported correctly.
|
* @summary Check the pattern domination error are reported correctly.
|
||||||
* @enablePreview
|
|
||||||
* @compile/fail/ref=Domination.out -XDrawDiagnostics Domination.java
|
* @compile/fail/ref=Domination.out -XDrawDiagnostics Domination.java
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class Domination {
|
public class Domination {
|
||||||
int testDominatesError1(Object o) {
|
int testDominatesError1(Object o) {
|
||||||
switch (o) {
|
switch (o) {
|
||||||
@ -180,8 +180,8 @@ public class Domination {
|
|||||||
record R(int a) {}
|
record R(int a) {}
|
||||||
Object o = null;
|
Object o = null;
|
||||||
switch (o) {
|
switch (o) {
|
||||||
case ((R r)): return 1;
|
case R r: return 1;
|
||||||
case ((R(int a))): return -1;
|
case R(int a): return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,8 +189,8 @@ public class Domination {
|
|||||||
record R(int a) {}
|
record R(int a) {}
|
||||||
Object o = null;
|
Object o = null;
|
||||||
switch (o) {
|
switch (o) {
|
||||||
case ((R(int a))): return 1;
|
case R(int a): return 1;
|
||||||
case ((R(int a))): return -1;
|
case R(int a): return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,4 @@ Domination.java:184:18: compiler.err.pattern.dominated
|
|||||||
Domination.java:193:18: compiler.err.pattern.dominated
|
Domination.java:193:18: compiler.err.pattern.dominated
|
||||||
Domination.java:202:18: compiler.err.pattern.dominated
|
Domination.java:202:18: compiler.err.pattern.dominated
|
||||||
Domination.java:211:18: compiler.err.pattern.dominated
|
Domination.java:211:18: compiler.err.pattern.dominated
|
||||||
- compiler.note.preview.filename: Domination.java, DEFAULT
|
|
||||||
- compiler.note.preview.recompile
|
|
||||||
18 errors
|
18 errors
|
@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @enablePreview
|
|
||||||
* @compile EmptyRecordClass.java
|
* @compile EmptyRecordClass.java
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
* @test
|
* @test
|
||||||
* @bug 8262891
|
* @bug 8262891
|
||||||
* @summary Verify pattern switches work properly when the set of enum constant changes.
|
* @summary Verify pattern switches work properly when the set of enum constant changes.
|
||||||
* @enablePreview
|
|
||||||
* @compile EnumTypeChanges.java
|
* @compile EnumTypeChanges.java
|
||||||
* @compile EnumTypeChanges2.java
|
* @compile EnumTypeChanges2.java
|
||||||
* @run main EnumTypeChanges
|
* @run main EnumTypeChanges
|
||||||
|
@ -25,10 +25,9 @@
|
|||||||
* @test
|
* @test
|
||||||
* @bug 8297118
|
* @bug 8297118
|
||||||
* @summary Verify pattern switches work properly when the set of enum constant changes.
|
* @summary Verify pattern switches work properly when the set of enum constant changes.
|
||||||
* @compile EnumTypeChangesNonPreview.java
|
* @compile --release 20 EnumTypeChangesNonPreview.java
|
||||||
* @compile EnumTypeChanges2.java
|
* @compile EnumTypeChanges2.java
|
||||||
* @run main EnumTypeChangesNonPreview
|
* @run main EnumTypeChangesNonPreview
|
||||||
* @run main/othervm --enable-preview EnumTypeChangesNonPreview
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,254 +0,0 @@
|
|||||||
/*
|
|
||||||
* @test /nodynamiccopyright/
|
|
||||||
* @summary
|
|
||||||
* @enablePreview
|
|
||||||
*/
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class ForEachPatterns {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
|
|
||||||
List<Point> in = List.of(new Point(1, 2), new Point(2, 3));
|
|
||||||
List<IPoint> in_iface = List.of(new Point(1, 2), new Point(2, 3));
|
|
||||||
List inRaw = List.of(new Point(1, 2), new Point(2, 3), new Frog(3, 4));
|
|
||||||
List<PointEx> inWithPointEx = List.of(new PointEx(1, 2));
|
|
||||||
byte[] inBytes = { (byte) 127, (byte) 127 };
|
|
||||||
List<Point> inWithNullComponent = List.of(new Point(1, null), new Point(2, 3));
|
|
||||||
Point[] inArray = in.toArray(Point[]::new);
|
|
||||||
List<WithPrimitives> inWithPrimitives = List.of(new WithPrimitives(1, 2), new WithPrimitives(2, 3));
|
|
||||||
IParent recs [] = { new Rec(1) };
|
|
||||||
List<Point> inWithNull = new ArrayList<>();
|
|
||||||
{
|
|
||||||
inWithNull.add(new Point(2, 3));
|
|
||||||
inWithNull.add(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(8, iteratorEnhancedFor(in));
|
|
||||||
assertEquals(8, arrayEnhancedFor(inArray));
|
|
||||||
assertEquals(8, simpleDecostructionPatternWithAccesses(in));
|
|
||||||
assertEx(ForEachPatterns::simpleDecostructionPatternWithAccesses, null, NullPointerException.class);
|
|
||||||
assertMatchExceptionWithNested(ForEachPatterns::simpleDecostructionPatternWithAccesses, inWithNull, NullPointerException.class);
|
|
||||||
assertEx(ForEachPatterns::simpleDecostructionPatternWithAccesses, inWithNullComponent, NullPointerException.class);
|
|
||||||
assertMatchExceptionWithNested(ForEachPatterns::simpleDecostructionPatternException, inWithPointEx, TestPatternFailed.class);
|
|
||||||
assertEx(ForEachPatterns::simpleDecostructionPatternWithAccesses, (List<Point>) inRaw, ClassCastException.class);
|
|
||||||
assertEquals(2, simpleDecostructionPatternNoComponentAccess(in));
|
|
||||||
assertMatchExceptionWithNested(ForEachPatterns::simpleDecostructionPatternNoComponentAccess, inWithNull, NullPointerException.class);
|
|
||||||
assertEquals(2, simpleDecostructionPatternNoComponentAccess(inWithNullComponent));
|
|
||||||
assertEquals(8, varAndConcrete(in));
|
|
||||||
assertEquals(3, returnFromEnhancedFor(in));
|
|
||||||
assertEquals(0, breakFromEnhancedFor(in));
|
|
||||||
assertEquals(254, primitiveWidening(inBytes));
|
|
||||||
assertEquals(8, sealedRecordPassBaseType(in_iface));
|
|
||||||
assertEquals(8, withPrimitives(inWithPrimitives));
|
|
||||||
assertEquals(List.of(Color.RED), JEPExample());
|
|
||||||
assertEquals(1, arrayWithSealed(recs));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int iteratorEnhancedFor(List<Point> points) {
|
|
||||||
int result = 0;
|
|
||||||
for (Point(Integer a, Integer b) : points) {
|
|
||||||
result += a + b;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int arrayEnhancedFor(Point[] points) {
|
|
||||||
int result = 0;
|
|
||||||
for (Point(Integer a, Integer b) : points) {
|
|
||||||
result += a + b;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int simpleDecostructionPatternWithAccesses(List<Point> points) {
|
|
||||||
int result = 0;
|
|
||||||
for (Point(var a, var b): points) {
|
|
||||||
result += a + b;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int simpleDecostructionPatternException(List<PointEx> points) {
|
|
||||||
int result = 0;
|
|
||||||
for (PointEx(var a, var b): points) {
|
|
||||||
result += a + b;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int simpleDecostructionPatternNoComponentAccess(List<Point> points) {
|
|
||||||
int result = 0;
|
|
||||||
for (Point(var a, var b): points) {
|
|
||||||
result += 1;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int varAndConcrete(List<Point> points) {
|
|
||||||
int result = 0;
|
|
||||||
for (Point(Integer a, var b): points) {
|
|
||||||
result += a + b;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int returnFromEnhancedFor(List<Point> points) {
|
|
||||||
for (Point(var a, var b): points) {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int breakFromEnhancedFor(List<Point> points) {
|
|
||||||
int i = 1;
|
|
||||||
int result = 0;
|
|
||||||
for (Point(var a, var b): points) {
|
|
||||||
if (i == 1) break;
|
|
||||||
else result += a + b;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sealedRecordPassBaseType(List<IPoint> points) {
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
for(Point(var x, var y) : points) {
|
|
||||||
result += (x + y);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int withPrimitives(List<WithPrimitives> points) {
|
|
||||||
int result = 0;
|
|
||||||
for (WithPrimitives(int a, double b): points) {
|
|
||||||
result += a + (int) b;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simpler pos tests with local variable declarations
|
|
||||||
// Should pass now and in the future if local variable
|
|
||||||
// declaration is subsumed by patterns (not just record patterns)
|
|
||||||
static int primitiveWidening(byte[] inBytes) {
|
|
||||||
int acc = 0;
|
|
||||||
for (int i: inBytes) {
|
|
||||||
acc += i;
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int applicability1(List<Point> points) {
|
|
||||||
for (IPoint p: points) {
|
|
||||||
System.out.println(p);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int applicability2(List<Object> points) {
|
|
||||||
for (Object p: points) {
|
|
||||||
System.out.println(p);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<Color> JEPExample() {
|
|
||||||
Rectangle rect = new Rectangle(
|
|
||||||
new ColoredPoint(new Point(1,2), Color.RED),
|
|
||||||
new ColoredPoint(new Point(3,4), Color.GREEN)
|
|
||||||
);
|
|
||||||
Rectangle[] rArr = {rect};
|
|
||||||
return printUpperLeftColors(rArr);
|
|
||||||
}
|
|
||||||
//where
|
|
||||||
static List<Color> printUpperLeftColors(Rectangle[] r) {
|
|
||||||
List<Color> ret = new ArrayList<>();
|
|
||||||
for (Rectangle(ColoredPoint(Point p, Color c), ColoredPoint lr): r) {
|
|
||||||
ret.add(c);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int arrayWithSealed(IParent[] recs){
|
|
||||||
for (Rec(int a) : recs) {
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Color { RED, GREEN, BLUE }
|
|
||||||
record ColoredPoint(Point p, Color c) {}
|
|
||||||
record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}
|
|
||||||
|
|
||||||
sealed interface IParent permits Rec {}
|
|
||||||
record Rec(int a) implements IParent {}
|
|
||||||
|
|
||||||
sealed interface IPoint permits Point {}
|
|
||||||
record Point(Integer x, Integer y) implements IPoint { }
|
|
||||||
|
|
||||||
record GPoint<T>(T x, T y) { }
|
|
||||||
record VoidPoint() { }
|
|
||||||
record RecordOfLists(List<Integer> o) {}
|
|
||||||
record RecordOfLists2(List<List<Integer>> o) {}
|
|
||||||
|
|
||||||
@Target({ElementType.TYPE_USE, ElementType.LOCAL_VARIABLE})
|
|
||||||
@interface Annot {
|
|
||||||
String field();
|
|
||||||
}
|
|
||||||
record Frog(Integer x, Integer y) { }
|
|
||||||
record PointEx(Integer x, Integer y) {
|
|
||||||
@Override
|
|
||||||
public Integer x() {
|
|
||||||
throw new TestPatternFailed(EXCEPTION_MESSAGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
record WithPrimitives(int x, double y) { }
|
|
||||||
static final String EXCEPTION_MESSAGE = "exception-message";
|
|
||||||
public static class TestPatternFailed extends AssertionError {
|
|
||||||
public TestPatternFailed(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// error handling
|
|
||||||
static void fail(String message) {
|
|
||||||
throw new AssertionError(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void assertEquals(Object expected, Object actual) {
|
|
||||||
if (!Objects.equals(expected, actual)) {
|
|
||||||
throw new AssertionError("Expected: " + expected + "," +
|
|
||||||
"got: " + actual);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T> void assertMatchExceptionWithNested(Function<List<T>, Integer> f, List<T> points, Class<?> nestedExceptionClass) {
|
|
||||||
try {
|
|
||||||
f.apply(points);
|
|
||||||
fail("Expected an exception, but none happened!");
|
|
||||||
}
|
|
||||||
catch(Exception ex) {
|
|
||||||
assertEquals(MatchException.class, ex.getClass());
|
|
||||||
|
|
||||||
MatchException me = (MatchException) ex;
|
|
||||||
|
|
||||||
assertEquals(nestedExceptionClass, me.getCause().getClass());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T> void assertEx(Function<List<T>, Integer> f, List<T> points, Class<?> exceptionClass) {
|
|
||||||
try {
|
|
||||||
f.apply(points);
|
|
||||||
fail("Expected an exception, but none happened!");
|
|
||||||
}
|
|
||||||
catch(Exception ex) {
|
|
||||||
assertEquals(exceptionClass, ex.getClass());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* @test /nodynamiccopyright/
|
|
||||||
* @summary
|
|
||||||
* @enablePreview
|
|
||||||
* @compile/fail/ref=ForEachPatternsErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW ForEachPatternsErrors.java
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ForEachPatternsErrors {
|
|
||||||
|
|
||||||
static void exhaustivity_error1(List<Object> points) {
|
|
||||||
for (Point(var x, var y): points) {
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void exhaustivity_error2(List points) {
|
|
||||||
for (Point(var x, var y): points) {
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void exhaustivity_error3(List<OPoint> opoints) {
|
|
||||||
for (OPoint(String s, String t) : opoints) {
|
|
||||||
System.out.println(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void exhaustivity_error4(List<?> f) {
|
|
||||||
for (Rec(var x): f){
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void applicability_error(List<Object> points) {
|
|
||||||
for (Interface p: points) {
|
|
||||||
System.out.println(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
record Rec(String x) { }
|
|
||||||
interface Interface {}
|
|
||||||
sealed interface IPoint permits Point {}
|
|
||||||
record Point(Integer x, Integer y) implements IPoint { }
|
|
||||||
record OPoint(Object x, Object y) { }
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
ForEachPatternsErrors.java:36:27: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Object, ForEachPatternsErrors.Interface)
|
|
||||||
ForEachPatternsErrors.java:13:9: compiler.err.foreach.not.exhaustive.on.type: ForEachPatternsErrors.Point, java.lang.Object
|
|
||||||
ForEachPatternsErrors.java:19:9: compiler.err.foreach.not.exhaustive.on.type: ForEachPatternsErrors.Point, java.lang.Object
|
|
||||||
ForEachPatternsErrors.java:25:9: compiler.err.foreach.not.exhaustive.on.type: ForEachPatternsErrors.OPoint, ForEachPatternsErrors.OPoint
|
|
||||||
ForEachPatternsErrors.java:31:9: compiler.err.foreach.not.exhaustive.on.type: ForEachPatternsErrors.Rec, compiler.misc.type.captureof: 1, ?
|
|
||||||
- compiler.note.preview.filename: ForEachPatternsErrors.java, DEFAULT
|
|
||||||
- compiler.note.preview.recompile
|
|
||||||
5 errors
|
|
@ -1,12 +0,0 @@
|
|||||||
/*
|
|
||||||
* @test /nodynamiccopyright/
|
|
||||||
* @summary
|
|
||||||
* @enablePreview
|
|
||||||
* @compile -XDfind=all ForEachTestAllAnalyzers.java
|
|
||||||
*/
|
|
||||||
public class ForEachTestAllAnalyzers {
|
|
||||||
private void test(Iterable<? extends R> l) {
|
|
||||||
for (R(Object a) : l) { }
|
|
||||||
}
|
|
||||||
record R(Object a) {}
|
|
||||||
}
|
|
@ -24,7 +24,6 @@
|
|||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @bug 8298184
|
* @bug 8298184
|
||||||
* @enablePreview
|
|
||||||
* @compile GenericRecordDeconstructionPattern.java
|
* @compile GenericRecordDeconstructionPattern.java
|
||||||
* @run main GenericRecordDeconstructionPattern
|
* @run main GenericRecordDeconstructionPattern
|
||||||
*/
|
*/
|
||||||
@ -47,8 +46,6 @@ public class GenericRecordDeconstructionPattern {
|
|||||||
runTest(this::runSwitchInference3);
|
runTest(this::runSwitchInference3);
|
||||||
runTest(this::runSwitchInference4);
|
runTest(this::runSwitchInference4);
|
||||||
testInference3();
|
testInference3();
|
||||||
assertEquals(0, forEachInference(List.of(new Box(""))));
|
|
||||||
assertEquals(1, forEachInference(List.of(new Box(null))));
|
|
||||||
assertEquals(1, runIfSuperBound(new Box<>(new StringBuilder())));
|
assertEquals(1, runIfSuperBound(new Box<>(new StringBuilder())));
|
||||||
assertEquals(1, runIfSuperBound(new Box<>(0)));
|
assertEquals(1, runIfSuperBound(new Box<>(0)));
|
||||||
}
|
}
|
||||||
@ -106,13 +103,6 @@ public class GenericRecordDeconstructionPattern {
|
|||||||
: -1;
|
: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int forEachInference(Iterable<I<String>> b) {
|
|
||||||
for (Box(var s) : b) {
|
|
||||||
return s == null ? 1 : s.length();
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void testInference3() {
|
void testInference3() {
|
||||||
I<I<String>> b = new Box<>(new Box<>(null));
|
I<I<String>> b = new Box<>(new Box<>(null));
|
||||||
assertEquals(1, runSwitchInferenceNested(b));
|
assertEquals(1, runSwitchInferenceNested(b));
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
* @test
|
* @test
|
||||||
* @bug 8262891 8268663 8289894
|
* @bug 8262891 8268663 8289894
|
||||||
* @summary Check guards implementation.
|
* @summary Check guards implementation.
|
||||||
* @enablePreview
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -134,24 +133,24 @@ public class Guards {
|
|||||||
|
|
||||||
String typeGuardAfterParenthesizedTrueSwitchStatement(Object o) {
|
String typeGuardAfterParenthesizedTrueSwitchStatement(Object o) {
|
||||||
switch (o) {
|
switch (o) {
|
||||||
case (Integer i) when i == 0: o = String.valueOf(i); return "true";
|
case Integer i when i == 0: o = String.valueOf(i); return "true";
|
||||||
case (Integer i) when i == 2: o = String.valueOf(i); return "second";
|
case Integer i when i == 2: o = String.valueOf(i); return "second";
|
||||||
case Object x: return "any";
|
case Object x: return "any";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String typeGuardAfterParenthesizedTrueSwitchExpression(Object o) {
|
String typeGuardAfterParenthesizedTrueSwitchExpression(Object o) {
|
||||||
return switch (o) {
|
return switch (o) {
|
||||||
case (Integer i) when i == 0: o = String.valueOf(i); yield "true";
|
case Integer i when i == 0: o = String.valueOf(i); yield "true";
|
||||||
case (Integer i) when i == 2: o = String.valueOf(i); yield "second";
|
case Integer i when i == 2: o = String.valueOf(i); yield "second";
|
||||||
case Object x: yield "any";
|
case Object x: yield "any";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
String typeGuardAfterParenthesizedTrueIfStatement(Object o) {
|
String typeGuardAfterParenthesizedTrueIfStatement(Object o) {
|
||||||
if (o != null && o instanceof (Integer i) && i == 0) {
|
if (o != null && o instanceof Integer i && i == 0) {
|
||||||
return "true";
|
return "true";
|
||||||
} else if (o != null && o instanceof (Integer i) && i == 2 && (o = i) != null) {
|
} else if (o != null && o instanceof Integer i && i == 2 && (o = i) != null) {
|
||||||
return "second";
|
return "second";
|
||||||
} else {
|
} else {
|
||||||
return "any";
|
return "any";
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
* @test
|
* @test
|
||||||
* @bug 8262891
|
* @bug 8262891
|
||||||
* @summary Check errors reported for guarded patterns.
|
* @summary Check errors reported for guarded patterns.
|
||||||
* @enablePreview
|
|
||||||
* @compile/fail/ref=GuardsErrors.out -XDrawDiagnostics GuardsErrors.java
|
* @compile/fail/ref=GuardsErrors.out -XDrawDiagnostics GuardsErrors.java
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -41,4 +40,35 @@ public class GuardsErrors {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void variablesInGuards(Object o) {
|
||||||
|
final int i1;
|
||||||
|
int i2 = 0;
|
||||||
|
switch (o) {
|
||||||
|
case Integer v when (i1 = 0) == 0 -> {}
|
||||||
|
case Integer v when i2++ == 0 -> {}
|
||||||
|
case Integer v when ++i2 == 0 -> {}
|
||||||
|
case Integer v when new Predicate() {
|
||||||
|
public boolean test() {
|
||||||
|
final int i;
|
||||||
|
i = 2;
|
||||||
|
return i == 2;
|
||||||
|
}
|
||||||
|
}.test() -> {}
|
||||||
|
case Number v1 when v1 instanceof Integer v2 && (v2 = 0) == 0 -> {}
|
||||||
|
default -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GuardsErrors(Object o) {
|
||||||
|
switch (o) {
|
||||||
|
case Integer v when (f = 0) == 0 -> {}
|
||||||
|
default -> throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final int f;
|
||||||
|
|
||||||
|
interface Predicate {
|
||||||
|
public boolean test();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user