diff --git a/src/java.base/share/classes/java/lang/MatchException.java b/src/java.base/share/classes/java/lang/MatchException.java
index e47150122c1..832d71bcb1d 100644
--- a/src/java.base/share/classes/java/lang/MatchException.java
+++ b/src/java.base/share/classes/java/lang/MatchException.java
@@ -25,48 +25,57 @@
package java.lang;
-import jdk.internal.javac.PreviewFeature;
-
/**
* Thrown to indicate an unexpected failure in pattern matching.
*
- *
{@code MatchException} may be thrown when an exhaustive pattern matching language construct
- * (such as a switch expression) encounters a value that does not match any of the provided
- * patterns at runtime. This can arise from a number of cases:
+ *
{@code MatchException} may be thrown when an exhaustive pattern matching
+ * language construct (such as a {@code switch} expression) encounters a value
+ * 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:
+ *
*
- *
Separate compilation anomalies, where a sealed interface has a different set of permitted
- * subtypes at runtime than it had at compilation time, an enum has a different set of
- * constants at runtime than it had at compilation time, or the type hierarchy has changed
- * in incompatible ways between compile time and run time.
- *
{@code null} values and nested patterns using sealed types. If an interface or abstract
- * class {@code C} is sealed to permit {@code A} and {@code B}, then the set of record
- * patterns {@code R(A a)} and {@code R(B b)} are exhaustive on a record {@code R} whose
- * sole component is of type {@code C}, but neither of these patterns will match
- * {@code new R(null)}.
- *
Null targets and nested record patterns. Given a record type {@code R} whose sole
- * component is {@code S}, which in turn is a record whose sole component is {@code String},
- * then the nested record pattern {@code R(S(String s))} will not match {@code new R(null)}.
+ *
Separate compilation anomalies, where parts of the type hierarchy that
+ * the patterns reference have been changed, but the pattern matching
+ * construct has not been recompiled. For example, if a sealed interface
+ * has a different set of permitted subtypes at run time than it had at
+ * compile time, or if an enum class has a different set of enum constants
+ * at runtime than it had at compile time, or if the type hierarchy has
+ * been changed in some incompatible way between compile time and run time.
+ *
+ *
{@code null} values and nested patterns involving sealed classes. If,
+ * for example, an interface {@code I} is {@code sealed} with two permitted
+ * subclasses {@code A} and {@code B}, and a record class {@code R} has a
+ * 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)}.
+ *
+ *
{@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).
*
*
- *
Match failures arising from unexpected inputs will generally throw {@code MatchException} only
- * after all patterns have been tried; even if {@code R(S(String s))} does not match
- * {@code new R(null)}, a later pattern (such as {@code R r}) may still match the target.
+ *
{@code MatchException} may also be thrown by the process of pattern matching
+ * a value against a pattern. For example, pattern matching involving a record
+ * 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.
*
- *
MatchException may also be thrown when operations performed as part of pattern matching throw
- * 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.11.3 Execution of a {@code switch} Statement
* @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 {
@java.io.Serial
private static final long serialVersionUID = 0L;
diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
index c0d40e5b205..4ac90d35503 100644
--- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
+++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
@@ -25,17 +25,18 @@
package java.lang.runtime;
+import java.lang.Enum.EnumDesc;
import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantBootstraps;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.util.Objects;
import java.util.stream.Stream;
-import jdk.internal.javac.PreviewFeature;
-
import static java.util.Objects.requireNonNull;
+import jdk.internal.vm.annotation.Stable;
/**
* 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
* 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 {
private SwitchBootstraps() {}
@@ -60,7 +60,8 @@ public class SwitchBootstraps {
DO_TYPE_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "doTypeSwitch",
MethodType.methodType(int.class, Object.class, int.class, Object[].class));
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) {
throw new ExceptionInInitializerError(e);
@@ -71,7 +72,7 @@ public class SwitchBootstraps {
* Bootstrap method for linking an {@code invokedynamic} call site that
* 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
- * {@code String} or {@code Integer} or {@code Class}.
+ * {@code String} or {@code Integer} or {@code Class} or {@code EnumDesc}.
*
* 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
@@ -89,10 +90,16 @@ public class SwitchBootstraps {
* from the target's class; or
*
the element is of type {@code String} or {@code Integer} and
* equals to the target.
+ *
the element is of type {@code EnumDesc}, that describes a constant that is
+ * equals to the target.
*
*
* 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 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
* 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,
* a reference type, an {@code int}, and {@code int} as a return type.
* @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
*
* @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,
* second parameter of type {@code int} and with {@code int} as its return type,
* or if {@code labels} contains an element that is not of type {@code String},
- * {@code Integer} or {@code Class}.
+ * {@code Integer}, {@code Class} or {@code EnumDesc}.
* @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
* @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
*/
@@ -138,12 +145,15 @@ public class SwitchBootstraps {
Class> labelClass = label.getClass();
if (labelClass != Class.class &&
labelClass != String.class &&
- labelClass != Integer.class) {
+ labelClass != Integer.class &&
+ labelClass != EnumDesc.class) {
throw new IllegalArgumentException("label with illegal type found: " + label.getClass());
}
}
private static int doTypeSwitch(Object target, int startIndex, Object[] labels) {
+ Objects.checkIndex(startIndex, labels.length + 1);
+
if (target == null)
return -1;
@@ -160,6 +170,11 @@ public class SwitchBootstraps {
} else if (target instanceof Character input && constant.intValue() == input.charValue()) {
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)) {
return i;
}
@@ -200,6 +215,10 @@ public class SwitchBootstraps {
*
* 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 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
* privileges of the caller. When used with {@code invokedynamic},
@@ -235,13 +254,28 @@ public class SwitchBootstraps {
labels = labels.clone();
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 =
- MethodHandles.insertArguments(DO_ENUM_SWITCH, 2, (Object) labels);
- target = target.asType(invocationType);
+ return new ConstantCallSite(temporary);
+ }
- return new ConstantCallSite(target);
+ private static > 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 > 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)
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
Class> targetClass = target.getClass();
for (int i = startIndex; i < labels.length; i++) {
@@ -288,4 +334,8 @@ public class SwitchBootstraps {
return labels.length;
}
+ private static final class ResolvedEnumLabels {
+ @Stable
+ public Object[] resolvedLabels;
+ }
}
diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java
index afcd73aaf5f..ed3a0d6121e 100644
--- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java
+++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java
@@ -480,12 +480,10 @@ public abstract class CallArranger {
StorageCalculator.StructStorage[] structStorages
= storageCalculator.structStorages((GroupLayout) layout, forHFA);
- for (StorageCalculator.StructStorage(
- long offset, Class> ca, int byteWidth, VMStorage storage
- ) : structStorages) {
+ for (StorageCalculator.StructStorage structStorage : structStorages) {
bindings.dup();
- bindings.vmLoad(storage, ca)
- .bufferStore(offset, ca, byteWidth);
+ bindings.vmLoad(structStorage.storage(), structStorage.carrier())
+ .bufferStore(structStorage.offset(), structStorage.carrier(), structStorage.byteWidth());
}
}
case STRUCT_REFERENCE -> {
diff --git a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java
index 59d07cdbf1e..9d3016a507d 100644
--- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java
+++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java
@@ -64,10 +64,6 @@ public @interface PreviewFeature {
* Values should be annotated with the feature's {@code JEP}.
*/
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
VIRTUAL_THREADS,
@JEP(number=442, title="Foreign Function & Memory API", status="Third Preview")
diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/CaseLabelTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/CaseLabelTree.java
index 422e7575c74..d24ce1d16ba 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/CaseLabelTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/CaseLabelTree.java
@@ -25,12 +25,9 @@
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.
*
- * @since 17
+ * @since 21
*/
-@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public interface CaseLabelTree extends Tree {}
diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/CaseTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/CaseTree.java
index 0a25df0a856..facdb8efdaf 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/CaseTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/CaseTree.java
@@ -27,8 +27,6 @@ package com.sun.source.tree;
import java.util.List;
-import jdk.internal.javac.PreviewFeature;
-
/**
* 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}.
*
* @return labels for this case
- * @since 17
+ * @since 21
*/
- @PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
List extends CaseLabelTree> getLabels();
+ /**
+ * The guard for the case.
+ *
+ * @return the guard
+ * @since 21
+ */
+ ExpressionTree getGuard();
+
/**
* For case with kind {@linkplain CaseKind#STATEMENT},
* returns the statements labeled by the case.
diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/ConstantCaseLabelTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/ConstantCaseLabelTree.java
index 90b6d101a18..6849fa79d0e 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/ConstantCaseLabelTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/ConstantCaseLabelTree.java
@@ -25,13 +25,10 @@
package com.sun.source.tree;
-import jdk.internal.javac.PreviewFeature;
-
/**
* 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 {
/**
diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/DeconstructionPatternTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/DeconstructionPatternTree.java
index 8eb067948e7..fa1f8de0fc7 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/DeconstructionPatternTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/DeconstructionPatternTree.java
@@ -26,14 +26,12 @@
package com.sun.source.tree;
import java.util.List;
-import jdk.internal.javac.PreviewFeature;
/**
* A deconstruction pattern tree.
*
- * @since 19
+ * @since 21
*/
-@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
public interface DeconstructionPatternTree extends PatternTree {
/**
diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/DefaultCaseLabelTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/DefaultCaseLabelTree.java
index 7cbe33b3fa0..365b9b543da 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/DefaultCaseLabelTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/DefaultCaseLabelTree.java
@@ -24,12 +24,9 @@
*/
package com.sun.source.tree;
-import jdk.internal.javac.PreviewFeature;
-
/**
* 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 {}
diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/EnhancedForLoopTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/EnhancedForLoopTree.java
index 2c80cf780dc..7c56985e4fc 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/EnhancedForLoopTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/EnhancedForLoopTree.java
@@ -25,8 +25,6 @@
package com.sun.source.tree;
-import jdk.internal.javac.PreviewFeature;
-
/**
* A tree node for an "enhanced" {@code for} loop statement.
*
@@ -43,37 +41,12 @@ import jdk.internal.javac.PreviewFeature;
* @since 1.6
*/
public interface EnhancedForLoopTree extends StatementTree {
- /**
- * "Enhanced" {@code for} declarations come in two forms:
- *
- *
local variable declarations and
- *
record patterns
- *
- *
- * @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.
- * @return the control variable, or {@code null} if this "enhanced" {@code for} uses a pattern
+ * @return the control variable
*/
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.
* @return the expression
@@ -85,12 +58,4 @@ public interface EnhancedForLoopTree extends StatementTree {
* @return the body of the loop
*/
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();
}
diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java
index a294a8f0f68..f5bede70539 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/InstanceOfTree.java
@@ -25,8 +25,6 @@
package com.sun.source.tree;
-import jdk.internal.javac.PreviewFeature;
-
/**
* A tree node for an {@code instanceof} expression.
*
@@ -43,22 +41,6 @@ import jdk.internal.javac.PreviewFeature;
*/
public interface InstanceOfTree extends ExpressionTree {
- /**
- * Two possible variants of instanceof expressions:
- *
- *
testing types, and
- *
performing pattern matching
- *
- * @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.
* @return the expression
@@ -93,12 +75,4 @@ public interface InstanceOfTree extends ExpressionTree {
*/
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();
}
diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/ParenthesizedPatternTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/ParenthesizedPatternTree.java
deleted file mode 100644
index ff4ebb7a16b..00000000000
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/ParenthesizedPatternTree.java
+++ /dev/null
@@ -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:
- *
- * ( pattern )
- *
- *
- * @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();
-}
diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/PatternCaseLabelTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/PatternCaseLabelTree.java
index 286b1d3426c..6997f2a2cc5 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/PatternCaseLabelTree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/PatternCaseLabelTree.java
@@ -25,13 +25,10 @@
package com.sun.source.tree;
-import jdk.internal.javac.PreviewFeature;
-
/**
* 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 {
/**
@@ -41,11 +38,4 @@ public interface PatternCaseLabelTree extends CaseLabelTree {
*/
public PatternTree getPattern();
- /**
- * The guard for the case.
- *
- * @return the guard
- */
- ExpressionTree getGuard();
-
}
diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java
index ceb7ace553a..20a3b2f36f2 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java
@@ -234,44 +234,32 @@ public interface Tree {
*/
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}.
*
- * @since 17
+ * @since 21
*/
- @PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
DEFAULT_CASE_LABEL(DefaultCaseLabelTree.class),
/**
* Used for instances of {@link ConstantCaseLabelTree}.
*
- * @since 19
+ * @since 21
*/
- @PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
CONSTANT_CASE_LABEL(ConstantCaseLabelTree.class),
/**
* Used for instances of {@link PatternCaseLabelTree}.
*
- * @since 19
+ * @since 21
*/
- @PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
PATTERN_CASE_LABEL(PatternCaseLabelTree.class),
/**
* Used for instances of {@link DeconstructionPatternTree}.
*
- * @since 19
+ * @since 21
*/
- @PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
DECONSTRUCTION_PATTERN(DeconstructionPatternTree.class),
/**
diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java
index 98ff3cdc749..29adab1a575 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java
@@ -282,9 +282,8 @@ public interface TreeVisitor {
* @param node the node being visited
* @param p a parameter value
* @return a result value
- * @since 17
+ * @since 21
*/
- @PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p);
/**
@@ -292,9 +291,8 @@ public interface TreeVisitor {
* @param node the node being visited
* @param p a parameter value
* @return a result value
- * @since 19
+ * @since 21
*/
- @PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
R visitConstantCaseLabel(ConstantCaseLabelTree node, P p);
/**
@@ -302,9 +300,8 @@ public interface TreeVisitor {
* @param node the node being visited
* @param p a parameter value
* @return a result value
- * @since 19
+ * @since 21
*/
- @PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
R visitPatternCaseLabel(PatternCaseLabelTree node, P p);
/**
@@ -312,9 +309,8 @@ public interface TreeVisitor {
* @param node the node being visited
* @param p a parameter value
* @return a result value
- * @since 19
+ * @since 21
*/
- @PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
R visitDeconstructionPattern(DeconstructionPatternTree node, P p);
/**
@@ -341,16 +337,6 @@ public interface TreeVisitor {
*/
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.
* @param node the node being visited
diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java
index 048c67af68d..5ab4674a08c 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java
@@ -664,10 +664,9 @@ public class SimpleTreeVisitor implements TreeVisitor {
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
- * @since 17
+ * @since 21
*/
@Override
- @PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) {
return defaultAction(node, p);
}
@@ -680,10 +679,9 @@ public class SimpleTreeVisitor implements TreeVisitor {
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
- * @since 19
+ * @since 21
*/
@Override
- @PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitConstantCaseLabel(ConstantCaseLabelTree node, P p) {
return defaultAction(node, p);
}
@@ -696,10 +694,9 @@ public class SimpleTreeVisitor implements TreeVisitor {
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
- * @since 19
+ * @since 21
*/
@Override
- @PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
public R visitDeconstructionPattern(DeconstructionPatternTree node, P p) {
return defaultAction(node, p);
}
@@ -712,10 +709,9 @@ public class SimpleTreeVisitor implements TreeVisitor {
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
- * @since 19
+ * @since 21
*/
@Override
- @PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitPatternCaseLabel(PatternCaseLabelTree node, P p) {
return defaultAction(node, p);
}
@@ -748,22 +744,6 @@ public class SimpleTreeVisitor implements TreeVisitor {
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}
*
diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java
index b339ad3dd9b..15a3598fccd 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java
@@ -333,7 +333,7 @@ public class TreeScanner implements TreeVisitor {
*/
@Override
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.getStatement(), p, r);
return r;
@@ -397,6 +397,7 @@ public class TreeScanner implements TreeVisitor {
@Override
public R visitCase(CaseTree node, P p) {
R r = scan(node.getLabels(), p);
+ r = scanAndReduce(node.getGuard(), p, r);
if (node.getCaseKind() == CaseTree.CaseKind.RULE)
r = scanAndReduce(node.getBody(), p, r);
else
@@ -799,10 +800,9 @@ public class TreeScanner implements TreeVisitor {
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of scanning
- * @since 17
+ * @since 21
*/
@Override
- @PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) {
return null;
}
@@ -815,10 +815,9 @@ public class TreeScanner implements TreeVisitor {
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of scanning
- * @since 19
+ * @since 21
*/
@Override
- @PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitConstantCaseLabel(ConstantCaseLabelTree node, P p) {
return scan(node.getConstantExpression(), p);
}
@@ -831,14 +830,11 @@ public class TreeScanner implements TreeVisitor {
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of scanning
- * @since 19
+ * @since 21
*/
@Override
- @PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitPatternCaseLabel(PatternCaseLabelTree node, P p) {
- R r = scan(node.getPattern(), p);
- r = scanAndReduce(node.getGuard(), p, r);
- return r;
+ return scan(node.getPattern(), p);
}
/**
@@ -849,10 +845,9 @@ public class TreeScanner implements TreeVisitor {
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of scanning
- * @since 19
+ * @since 21
*/
@Override
- @PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
public R visitDeconstructionPattern(DeconstructionPatternTree node, P p) {
R r = scan(node.getDeconstructor(), p);
r = scanAndReduce(node.getNestedPatterns(), p, r);
@@ -887,22 +882,6 @@ public class TreeScanner implements TreeVisitor {
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}
*
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java
index 9e0ababaaf7..0ed4b8f0a26 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java
@@ -209,11 +209,7 @@ public class Preview {
*/
public boolean isPreview(Feature feature) {
return switch (feature) {
- case CASE_NULL -> true;
- case PATTERN_SWITCH -> 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).
//When real preview features will be added, this method can be implemented to return 'true'
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java
index 59bd648dce9..a0839870e83 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java
@@ -232,12 +232,12 @@ public enum Source {
REIFIABLE_TYPES_INSTANCEOF(JDK16, Fragments.FeatureReifiableTypesInstanceof, DiagKind.PLURAL),
RECORDS(JDK16, Fragments.FeatureRecords, DiagKind.PLURAL),
SEALED_CLASSES(JDK17, Fragments.FeatureSealedClasses, DiagKind.PLURAL),
- CASE_NULL(JDK17, Fragments.FeatureCaseNull, DiagKind.NORMAL),
- PATTERN_SWITCH(JDK17, Fragments.FeaturePatternSwitch, DiagKind.PLURAL),
+ CASE_NULL(JDK21, Fragments.FeatureCaseNull, DiagKind.NORMAL),
+ PATTERN_SWITCH(JDK21, Fragments.FeaturePatternSwitch, DiagKind.PLURAL),
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),
- UNCONDITIONAL_PATTERN_IN_INSTANCEOF(JDK19, Fragments.FeatureUnconditionalPatternsInInstanceof, DiagKind.PLURAL),
- RECORD_PATTERNS(JDK19, Fragments.FeatureDeconstructionPatterns, DiagKind.PLURAL),
WARN_ON_ILLEGAL_UTF8(MIN, JDK21),
;
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java
index e63ea1234cb..7560e5a0904 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java
@@ -191,7 +191,6 @@ public class Symtab {
public final Type incompatibleClassChangeErrorType;
public final Type cloneNotSupportedExceptionType;
public final Type matchExceptionType;
- public final Type nullPointerExceptionType;
public final Type annotationType;
public final TypeSymbol enumSym;
public final Type listType;
@@ -224,8 +223,11 @@ public class Symtab {
public final Type typeDescriptorType;
public final Type recordType;
public final Type switchBootstrapsType;
+ public final Type constantBootstrapsType;
public final Type valueBasedType;
public final Type valueBasedInternalType;
+ public final Type classDescType;
+ public final Type enumDescType;
// For serialization lint checking
public final Type objectStreamFieldType;
@@ -565,7 +567,6 @@ public class Symtab {
incompatibleClassChangeErrorType = enterClass("java.lang.IncompatibleClassChangeError");
cloneNotSupportedExceptionType = enterClass("java.lang.CloneNotSupportedException");
matchExceptionType = enterClass("java.lang.MatchException");
- nullPointerExceptionType = enterClass("java.lang.NullPointerException");
annotationType = enterClass("java.lang.annotation.Annotation");
classLoaderType = enterClass("java.lang.ClassLoader");
enumSym = enterClass(java_base, names.java_lang_Enum);
@@ -609,8 +610,11 @@ public class Symtab {
typeDescriptorType = enterClass("java.lang.invoke.TypeDescriptor");
recordType = enterClass("java.lang.Record");
switchBootstrapsType = enterClass("java.lang.runtime.SwitchBootstraps");
+ constantBootstrapsType = enterClass("java.lang.invoke.ConstantBootstraps");
valueBasedType = enterClass("jdk.internal.ValueBased");
valueBasedInternalType = enterSyntheticAnnotation("jdk.internal.ValueBased+Annotation");
+ classDescType = enterClass("java.lang.constant.ClassDesc");
+ enumDescType = enterClass("java.lang.Enum$EnumDesc");
// For serialization lint checking
objectStreamFieldType = enterClass("java.io.ObjectStreamField");
objectInputStreamType = enterClass("java.io.ObjectInputStream");
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java
index 894906e825c..d117573ce9d 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java
@@ -33,7 +33,6 @@ import java.util.Map;
import java.util.Queue;
import java.util.stream.Collectors;
-import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.VariableTree;
@@ -426,24 +425,18 @@ public class Analyzer {
@Override
boolean match(JCEnhancedForLoop tree){
- return tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE &&
- !isImplicitlyTyped((JCVariableDecl) tree.varOrRecordPattern);
+ return !isImplicitlyTyped(tree.var);
}
@Override
List rewrite(JCEnhancedForLoop oldTree) {
- Assert.check(oldTree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE);
-
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());
return List.of(newTree);
}
@Override
void process(JCEnhancedForLoop oldTree, JCEnhancedForLoop newTree, boolean hasErrors){
- Assert.check(oldTree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE);
-
- processVar((JCVariableDecl) oldTree.varOrRecordPattern,
- (JCVariableDecl) newTree.varOrRecordPattern, hasErrors);
+ processVar(oldTree.var, newTree.var, hasErrors);
}
}
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
index 2c6fbfe4043..54a3c5799cc 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -34,7 +34,6 @@ import javax.lang.model.element.ElementKind;
import javax.tools.JavaFileObject;
import com.sun.source.tree.CaseTree;
-import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.source.tree.MemberSelectTree;
@@ -171,8 +170,8 @@ public class Attr extends JCTree.Visitor {
allowRecords = Feature.RECORDS.allowedInSource(source);
allowPatternSwitch = (preview.isEnabled() || !preview.isPreview(Feature.PATTERN_SWITCH)) &&
Feature.PATTERN_SWITCH.allowedInSource(source);
- allowUnconditionalPatternsInstanceOf = (preview.isEnabled() || !preview.isPreview(Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF)) &&
- Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF.allowedInSource(source);
+ allowUnconditionalPatternsInstanceOf =
+ Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF.allowedInSource(source);
sourceName = source.name;
useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning");
@@ -1514,25 +1513,24 @@ public class Attr extends JCTree.Visitor {
public void visitForeachLoop(JCEnhancedForLoop tree) {
Env loopEnv =
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
-
try {
//the Formal Parameter of a for-each loop is not in the scope when
//attributing the for-each expression; we mimic this by attributing
//the for-each expression first (against original scope).
Type exprType = types.cvarUpperBound(attribExpr(tree.expr, loopEnv));
chk.checkNonVoid(tree.pos(), exprType);
- tree.elementType = types.elemtype(exprType); // perhaps expr is an array?
- if (tree.elementType == null) {
+ Type elemtype = types.elemtype(exprType); // perhaps expr is an array?
+ if (elemtype == null) {
// or perhaps expr implements Iterable?
Type base = types.asSuper(exprType, syms.iterableType.tsym);
if (base == null) {
log.error(tree.expr.pos(),
Errors.ForeachNotApplicableToType(exprType,
Fragments.TypeReqArrayOrIterable));
- tree.elementType = types.createErrorType(exprType);
+ elemtype = types.createErrorType(exprType);
} else {
List iterableParams = base.allparams();
- tree.elementType = iterableParams.isEmpty()
+ elemtype = iterableParams.isEmpty()
? syms.objectType
: types.wildUpperBound(iterableParams.head);
@@ -1546,40 +1544,14 @@ public class Attr extends JCTree.Visitor {
}
}
}
- if (tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) {
- if (jcVariableDecl.isImplicitlyTyped()) {
- Type inferredType = chk.checkLocalVarType(jcVariableDecl, tree.elementType, jcVariableDecl.name);
- 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( 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 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();
- }
+ if (tree.var.isImplicitlyTyped()) {
+ Type inferredType = chk.checkLocalVarType(tree.var, elemtype, tree.var.name);
+ setSyntheticVariableType(tree.var, inferredType);
}
+ 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;
}
finally {
@@ -1717,7 +1689,8 @@ public class Attr extends JCTree.Visitor {
}
MatchBindings currentBindings = null;
boolean wasUnconditionalPattern = hasUnconditionalPattern;
- for (JCCaseLabel label : c.labels) {
+ for (List labels = c.labels; labels.nonEmpty(); labels = labels.tail) {
+ JCCaseLabel label = labels.head;
if (label instanceof JCConstantCaseLabel constLabel) {
JCExpression expr = constLabel.expr;
if (TreeInfo.isNull(expr)) {
@@ -1731,7 +1704,11 @@ public class Attr extends JCTree.Visitor {
} else if (enumSwitch) {
Symbol sym = enumConstant(expr, seltype);
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)) {
log.error(label.pos(), Errors.DuplicateCaseLabel);
}
@@ -1747,17 +1724,14 @@ public class Attr extends JCTree.Visitor {
rs.basicLogResolveHelper = prevResolveHelper;
}
} else {
- ResultInfo valTypInfo = new ResultInfo(KindSelector.VAL_TYP,
- !seltype.hasTag(ERROR) ? seltype
- : Type.noType);
- Type pattype = attribTree(expr, switchEnv, valTypInfo);
+ Type pattype = attribTree(expr, switchEnv, caseLabelResultInfo(seltype));
if (!pattype.hasTag(ERROR)) {
if (pattype.constValue() == null) {
Symbol s = TreeInfo.symbol(expr);
if (s != null && s.kind == TYP && allowPatternSwitch) {
log.error(expr.pos(),
Errors.PatternExpected);
- } else {
+ } else if ((s != null && !s.isEnum()) || !allowPatternSwitch) {
log.error(expr.pos(),
(stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq));
}
@@ -1786,8 +1760,8 @@ public class Attr extends JCTree.Visitor {
}
checkCastablePattern(pat.pos(), seltype, primaryType);
Type patternType = types.erasure(primaryType);
- JCExpression guard = patternlabel.guard;
- if (guard != null) {
+ JCExpression guard = c.guard;
+ if (labels.tail.isEmpty() && guard != null) {
MatchBindings afterPattern = matchBindings;
Env bodyEnv = bindingEnv(switchEnv, matchBindings.bindingsWhenTrue);
try {
@@ -1801,7 +1775,7 @@ public class Attr extends JCTree.Visitor {
log.error(guard.pos(), Errors.GuardHasConstantExpressionFalse);
}
}
- boolean unguarded = TreeInfo.unguardedCaseLabel(label) && !pat.hasTag(RECORDPATTERN);
+ boolean unguarded = TreeInfo.unguardedCase(c) && !pat.hasTag(RECORDPATTERN);
boolean unconditional =
unguarded &&
!patternType.isErroneous() &&
@@ -1854,6 +1828,11 @@ public class Attr extends JCTree.Visitor {
}
}
// 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. */
private static void addVars(List stats, WriteableScope switchScope) {
for (;stats.nonEmpty(); stats = stats.tail) {
@@ -4101,7 +4080,6 @@ public class Attr extends JCTree.Visitor {
Type clazztype;
JCTree typeTree;
if (tree.pattern.getTag() == BINDINGPATTERN ||
- tree.pattern.getTag() == PARENTHESIZEDPATTERN ||
tree.pattern.getTag() == RECORDPATTERN) {
attribExpr(tree.pattern, env, exprtype);
clazztype = tree.pattern.type;
@@ -4109,9 +4087,8 @@ public class Attr extends JCTree.Visitor {
!exprtype.isErroneous() && !clazztype.isErroneous() &&
tree.pattern.getTag() != RECORDPATTERN) {
if (!allowUnconditionalPatternsInstanceOf) {
- log.error(tree.pos(), Errors.InstanceofPatternNoSubtype(exprtype, clazztype));
- } else if (preview.isPreview(Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF)) {
- preview.warnPreview(tree.pattern.pos(), Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF);
+ log.error(DiagnosticFlag.SOURCE_LEVEL, tree.pos(),
+ Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF.error(this.sourceName));
}
}
typeTree = TreeInfo.primaryPatternTypeTree((JCPattern) tree.pattern);
@@ -4253,11 +4230,6 @@ public class Attr extends JCTree.Visitor {
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) {
Type owntype = types.createErrorType(tree.type);
Type atype = attribExpr(tree.indexed, env);
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
index bf7ae0c5d59..1227b183be0 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
@@ -4626,7 +4626,7 @@ public class Check {
return false;
}
void checkSwitchCaseLabelDominated(List cases) {
- List caseLabels = List.nil();
+ List> caseLabels = List.nil();
boolean seenDefault = false;
boolean seenDefaultLabel = false;
boolean warnDominatedByDefault = false;
@@ -4653,7 +4653,9 @@ public class Check {
}
}
Type currentType = labelType(label);
- for (JCCaseLabel testCaseLabel : caseLabels) {
+ for (Pair caseAndLabel : caseLabels) {
+ JCCase testCase = caseAndLabel.fst;
+ JCCaseLabel testCaseLabel = caseAndLabel.snd;
Type testType = labelType(testCaseLabel);
if (types.isSubtype(currentType, testType) &&
!currentType.hasTag(ERROR) && !testType.hasTag(ERROR)) {
@@ -4663,7 +4665,7 @@ public class Check {
dominated |= !(testCaseLabel instanceof JCConstantCaseLabel);
} else if (label instanceof JCPatternCaseLabel patternCL &&
testCaseLabel instanceof JCPatternCaseLabel testPatternCaseLabel &&
- TreeInfo.unguardedCaseLabel(testCaseLabel)) {
+ TreeInfo.unguardedCase(testCase)) {
dominated = patternDominated(testPatternCaseLabel.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;
}
}
- while (existingPattern instanceof JCParenthesizedPattern parenthesized) {
- existingPattern = parenthesized.pattern;
- }
- while (currentPattern instanceof JCParenthesizedPattern parenthesized) {
- currentPattern = parenthesized.pattern;
- }
if (currentPattern instanceof JCBindingPattern) {
return existingPattern instanceof JCBindingPattern;
} else if (currentPattern instanceof JCRecordPattern currentRecordPattern) {
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
index 47086190b78..656e6a5a41b 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
@@ -32,13 +32,11 @@ import java.util.Map.Entry;
import java.util.HashMap;
import java.util.HashSet;
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.tools.javac.code.*;
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.Warnings;
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.BLOCK;
+import 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 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 com.sun.tools.javac.code.Types.UniqueType;
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
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
* different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that
@@ -211,6 +216,7 @@ public class Flow {
private final JCDiagnostic.Factory diags;
private Env attrEnv;
private Lint lint;
+ private final Infer infer;
public static Flow instance(Context context) {
Flow instance = context.get(flowKey);
@@ -334,6 +340,7 @@ public class Flow {
types = Types.instance(context);
chk = Check.instance(context);
lint = Lint.instance(context);
+ infer = Infer.instance(context);
rs = Resolve.instance(context);
diags = JCDiagnostic.Factory.instance(context);
Source source = Source.instance(context);
@@ -647,21 +654,7 @@ public class Flow {
}
public void visitForeachLoop(JCEnhancedForLoop tree) {
- if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) {
- visitVarDef(jcVariableDecl);
- } else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
- visitRecordPattern(jcRecordPattern);
-
- Set 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));
- }
- }
+ visitVarDef(tree.var);
ListBuffer prevPendingExits = pendingExits;
scan(tree.expr);
pendingExits = new ListBuffer<>();
@@ -705,8 +698,7 @@ public class Flow {
tree.isExhaustive = tree.hasUnconditionalPattern ||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases);
if (exhaustiveSwitch) {
- Set coveredSymbols = coveredSymbolsForCases(tree.pos(), tree.cases);
- tree.isExhaustive |= isExhaustive(tree.selector.pos(), tree.selector.type, coveredSymbols);
+ tree.isExhaustive |= exhausts(tree.selector, tree.cases);
if (!tree.isExhaustive) {
log.error(tree, Errors.NotExhaustiveStatement);
}
@@ -740,10 +732,9 @@ public class Flow {
}
}
}
- Set coveredSymbols = coveredSymbolsForCases(tree.pos(), tree.cases);
tree.isExhaustive = tree.hasUnconditionalPattern ||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) ||
- isExhaustive(tree.selector.pos(), tree.selector.type, coveredSymbols);
+ exhausts(tree.selector, tree.cases);
if (!tree.isExhaustive) {
log.error(tree, Errors.NotExhaustive);
}
@@ -751,211 +742,444 @@ public class Flow {
alive = alive.or(resolveYields(tree, prevPendingExits));
}
- private Set coveredSymbolsForCases(DiagnosticPosition pos,
- List cases) {
- HashSet labelValues = cases.stream()
- .flatMap(c -> c.labels.stream())
- .filter(TreeInfo::unguardedCaseLabel)
- .filter(l -> !l.hasTag(DEFAULTCASELABEL))
- .map(l -> l.hasTag(CONSTANTCASELABEL) ? ((JCConstantCaseLabel) l).expr
- : ((JCPatternCaseLabel) l).pat)
- .collect(Collectors.toCollection(HashSet::new));
- return coveredSymbols(pos, labelValues);
+ sealed interface PatternDescription {
+ public static PatternDescription from(Types types, Type selectorType, JCPattern pattern) {
+ if (pattern instanceof JCBindingPattern binding) {
+ Type type = types.isSubtype(selectorType, binding.type)
+ ? selectorType : binding.type;
+ return new BindingPattern(type);
+ } else if (pattern instanceof JCRecordPattern record) {
+ Type[] componentTypes = ((ClassSymbol) record.type.tsym).getRecordComponents()
+ .map(r -> types.memberType(record.type, r))
+ .toArray(s -> new Type[s]);
+ PatternDescription[] nestedDescriptions =
+ new PatternDescription[record.nested.size()];
+ int i = 0;
+ for (List 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 coveredSymbols(DiagnosticPosition pos,
- Iterable extends JCTree> labels) {
- Set coveredSymbols = new HashSet<>();
- Map> deconstructionPatternsByType = new HashMap<>();
+ record BindingPattern(Type type) implements PatternDescription {
+ @Override
+ public int hashCode() {
+ 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) {
- switch (labelValue.getTag()) {
- case BINDINGPATTERN, PARENTHESIZEDPATTERN -> {
- Type primaryPatternType = TreeInfo.primaryPatternType((JCPattern) labelValue);
- if (!primaryPatternType.hasTag(NONE)) {
- coveredSymbols.add(primaryPatternType.tsym);
+ record RecordPattern(Type recordType, int _hashCode, Type[] fullComponentTypes, PatternDescription... nested) implements PatternDescription {
+
+ public RecordPattern(Type recordType, Type[] fullComponentTypes, PatternDescription[] nested) {
+ this(recordType, hashCode(-1, recordType, nested), fullComponentTypes, nested);
+ }
+
+ @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 cases) {
+ Set patternSet = new HashSet<>();
+ Map> 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));
}
- }
- case RECORDPATTERN -> {
- JCRecordPattern dpat = (JCRecordPattern) labelValue;
- UniqueType type = new UniqueType(dpat.type, types);
- List augmentedPatterns =
- deconstructionPatternsByType.getOrDefault(type, List.nil())
- .prepend(dpat);
-
- deconstructionPatternsByType.put(type, augmentedPatterns);
- }
-
- 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> 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 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 nestedComponentPatterns = deconstructionPatterns.map(d -> d.nested.get(component));
- Set 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> 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 covered = new HashSet<>();
-
- for (Entry> 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 covered) {
- List todo = List.from(covered);
- while (todo.nonEmpty()) {
- Symbol sym = todo.head;
- todo = todo.tail;
- switch (sym.kind) {
- case VAR -> {
- Iterable 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);
- }
- }
+ } else if (l instanceof JCConstantCaseLabel constantLabel) {
+ Symbol s = TreeInfo.symbol(constantLabel.expr);
+ if (s != null && s.isEnum()) {
+ enum2Constants.computeIfAbsent(s.owner, x -> {
+ Set result = new HashSet<>();
+ s.owner.members()
+ .getSymbols(sym -> sym.kind == Kind.VAR && sym.isEnum())
+ .forEach(result::add);
+ return result;
+ }).remove(s);
}
}
}
}
- }
-
- private boolean isTransitivelyCovered(DiagnosticPosition pos, Type seltype,
- Symbol sealed, Set covered) {
+ for (Entry> e : enum2Constants.entrySet()) {
+ if (e.getValue().isEmpty()) {
+ patternSet.add(new BindingPattern(e.getKey().type));
+ }
+ }
+ List patterns = List.from(patternSet);
try {
- if (covered.stream().anyMatch(c -> sealed.isSubClass(c, types)))
- return true;
- if (sealed.kind == TYP && sealed.isAbstract() && sealed.isSealed()) {
- return ((ClassSymbol) sealed).permitted
- .stream()
- .filter(s -> {
- return types.isCastable(seltype, s.type/*, types.noWarnings*/);
- })
- .allMatch(s -> isTransitivelyCovered(pos, seltype, s, covered));
+ boolean repeat = true;
+ while (repeat) {
+ List updatedPatterns;
+ updatedPatterns = reduceBindingPatterns(selector.type, patterns);
+ updatedPatterns = reduceNestedPatterns(updatedPatterns);
+ updatedPatterns = reduceRecordPatterns(updatedPatterns);
+ repeat = updatedPatterns != patterns;
+ patterns = updatedPatterns;
+ if (checkCovered(selector.type, patterns)) {
+ return true;
+ }
}
- return false;
+ return checkCovered(selector.type, patterns);
} catch (CompletionFailure cf) {
- chk.completionError(pos, cf);
- return true;
+ chk.completionError(selector.pos(), cf);
+ return true; //error recovery
}
}
- private boolean isExhaustive(DiagnosticPosition pos, Type seltype, Set covered) {
- transitiveCovers(pos, seltype, covered);
+ private boolean checkCovered(Type seltype, List patterns) {
+ 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 components(Type seltype) {
return switch (seltype.getTag()) {
case CLASS -> {
if (seltype.isCompound()) {
if (seltype.isIntersection()) {
yield ((Type.IntersectionClassType) seltype).getComponents()
.stream()
- .anyMatch(t -> isExhaustive(pos, t, covered));
+ .flatMap(t -> components(t).stream())
+ .collect(List.collector());
}
- yield false;
+ yield List.nil();
}
- yield covered.stream()
- .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);
+ yield List.of(types.erasure(seltype));
}
+ 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 reduceBindingPatterns(Type selectorType, List patterns) {
+ Set 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 toRemove = new HashSet<>();
+ Set 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 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 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 currentPermittedSubTypes =
+ allPermittedSubTypes((ClassSymbol) bpOther.type.tsym, s -> true);
+
+ PERMITTED: for (Iterator 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 allPermittedSubTypes(ClassSymbol root, Predicate accept) {
+ Set permitted = new HashSet<>();
+ List 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 reduceNestedPatterns(List 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 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 result = new ListBuffer<>();
+ Set 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 reduceRecordPatterns(List patterns) {
+ var newPatterns = new ListBuffer();
+ 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) {
ListBuffer prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>();
@@ -1374,11 +1598,7 @@ public class Flow {
}
public void visitForeachLoop(JCEnhancedForLoop tree) {
- if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) {
- visitVarDef(jcVariableDecl);
- } else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
- visitRecordPattern(jcRecordPattern);
- }
+ visitVarDef(tree.var);
ListBuffer prevPendingExits = pendingExits;
scan(tree.expr);
pendingExits = new ListBuffer<>();
@@ -2177,10 +2397,6 @@ public class Flow {
void scanPattern(JCTree tree) {
scan(tree);
- if (inits.isReset()) {
- inits.assign(initsWhenTrue);
- uninits.assign(uninitsWhenTrue);
- }
}
/** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse)
@@ -2559,6 +2775,8 @@ public class Flow {
}
public void visitForeachLoop(JCEnhancedForLoop tree) {
+ visitVarDef(tree.var);
+
ListBuffer prevPendingExits = pendingExits;
FlowKind prevFlowKind = flowKind;
flowKind = FlowKind.NORMAL;
@@ -2567,13 +2785,7 @@ public class Flow {
final Bits initsStart = new Bits(inits);
final Bits uninitsStart = new Bits(uninits);
- if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) {
- visitVarDef(jcVariableDecl);
- letInit(tree.pos(), jcVariableDecl.sym);
- } else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
- visitRecordPattern(jcRecordPattern);
- }
-
+ letInit(tree.pos(), tree.var.sym);
pendingExits = new ListBuffer<>();
int prevErrors = log.nerrors;
do {
@@ -2625,17 +2837,10 @@ public class Flow {
for (JCCaseLabel pat : c.labels) {
scanPattern(pat);
}
- if (l.head.stats.isEmpty() &&
- l.tail.nonEmpty() &&
- l.tail.head.labels.size() == 1 &&
- TreeInfo.isNullCaseLabel(l.tail.head.labels.head)) {
- //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.guard);
+ if (inits.isReset()) {
+ inits.assign(initsWhenTrue);
+ uninits.assign(uninitsWhenTrue);
}
scan(c.stats);
if (c.completesNormally && c.caseKind == JCCase.RULE) {
@@ -3028,12 +3233,6 @@ public class Flow {
initParam(tree.var);
}
- @Override
- public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
- scan(tree.pat);
- scan(tree.guard);
- }
-
void referenced(Symbol sym) {
unrefdResources.remove(sym);
}
@@ -3104,6 +3303,7 @@ public class Flow {
class CaptureAnalyzer extends BaseAnalyzer {
JCTree currentTree; //local class or lambda
+ WriteableScope declaredInsideGuard;
@Override
void markDead() {
@@ -3117,7 +3317,7 @@ public class Flow {
sym.pos < currentTree.getStartPosition()) {
switch (currentTree.getTag()) {
case CLASSDEF:
- case PATTERNCASELABEL:
+ case CASE:
case LAMBDA:
if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) {
reportEffectivelyFinalError(pos, sym);
@@ -3131,15 +3331,20 @@ public class Flow {
tree = TreeInfo.skipParens(tree);
if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) {
Symbol sym = TreeInfo.symbol(tree);
- if (currentTree != null &&
- sym.kind == VAR &&
- sym.owner.kind == MTH &&
- ((VarSymbol)sym).pos < currentTree.getStartPosition()) {
+ if (currentTree != null) {
switch (currentTree.getTag()) {
- case CLASSDEF:
- case CASE:
- case LAMBDA:
- reportEffectivelyFinalError(tree, sym);
+ case CLASSDEF, LAMBDA -> {
+ if (sym.kind == VAR &&
+ sym.owner.kind == MTH &&
+ ((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) {
Fragment subKey = switch (currentTree.getTag()) {
case LAMBDA -> Fragments.Lambda;
- case PATTERNCASELABEL -> Fragments.Guard;
+ case CASE -> Fragments.Guard;
case CLASSDEF -> Fragments.InnerCls;
default -> throw new AssertionError("Unexpected tree kind: " + currentTree.getTag());
};
@@ -3188,20 +3393,21 @@ public class Flow {
}
@Override
- public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
- scan(tree.pattern);
- }
-
- @Override
- public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
- scan(tree.pat);
- JCTree prevTree = currentTree;
- try {
- currentTree = tree;
- scan(tree.guard);
- } finally {
- currentTree = prevTree;
+ public void visitCase(JCCase tree) {
+ scan(tree.labels);
+ if (tree.guard != null) {
+ JCTree prevTree = currentTree;
+ WriteableScope prevDeclaredInsideGuard = declaredInsideGuard;
+ try {
+ currentTree = tree;
+ declaredInsideGuard = WriteableScope.create(attrEnv.enclClass.sym);
+ scan(tree.guard);
+ } finally {
+ currentTree = prevTree;
+ declaredInsideGuard = prevDeclaredInsideGuard;
+ }
}
+ scan(tree.stats);
}
@Override
@@ -3256,6 +3462,14 @@ public class Flow {
super.visitTry(tree);
}
+ @Override
+ public void visitVarDef(JCVariableDecl tree) {
+ if (declaredInsideGuard != null) {
+ declaredInsideGuard.enter(tree.sym);
+ }
+ super.visitVarDef(tree);
+ }
+
@Override
public void visitYield(JCYield tree) {
scan(tree.value);
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
index cf857953912..5f2a5820e03 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
@@ -703,7 +703,7 @@ public class LambdaToMethod extends TreeTranslator {
JCBreak br = make.Break(null);
breaks.add(br);
List 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());
for (JCBreak br : breaks) {
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
index de826507f7f..eb99897ee59 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
@@ -28,7 +28,6 @@ package com.sun.tools.javac.comp;
import java.util.*;
import java.util.stream.Collectors;
-import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Kinds.KindSelector;
import com.sun.tools.javac.code.Scope.WriteableScope;
@@ -3558,18 +3557,13 @@ public class Lower extends TreeTranslator {
Type elemtype = types.elemtype(tree.expr.type);
JCExpression loopvarinit = make.Indexed(make.Ident(arraycache),
make.Ident(index)).setType(elemtype);
-
- Assert.check(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE);
- JCVariableDecl jcVariableDecl = (JCVariableDecl) tree.varOrRecordPattern;
-
- JCVariableDecl loopvardef = (JCVariableDecl)make.VarDef(jcVariableDecl.mods,
- jcVariableDecl.name,
- jcVariableDecl.vartype,
- loopvarinit).setType(jcVariableDecl.type);
- loopvardef.sym = jcVariableDecl.sym;
+ JCVariableDecl loopvardef = (JCVariableDecl)make.VarDef(tree.var.mods,
+ tree.var.name,
+ tree.var.vartype,
+ loopvarinit).setType(tree.var.type);
+ loopvardef.sym = tree.var.sym;
JCBlock body = make.
- Block(0, List.of(loopvardef, tree.body));
-
+ Block(0, List.of(loopvardef, tree.body));
result = translate(make.
ForLoop(loopinit,
@@ -3648,26 +3642,22 @@ public class Lower extends TreeTranslator {
itvar.type,
List.nil());
JCExpression vardefinit = make.App(make.Select(make.Ident(itvar), next));
-
- Assert.check(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE);
-
- JCVariableDecl var = (JCVariableDecl) tree.varOrRecordPattern;
- if (var.type.isPrimitive())
+ if (tree.var.type.isPrimitive())
vardefinit = make.TypeCast(types.cvarUpperBound(iteratorTarget), vardefinit);
else
- vardefinit = make.TypeCast(var.type, vardefinit);
- JCVariableDecl indexDef = (JCVariableDecl) make.VarDef(var.mods,
- var.name,
- var.vartype,
- vardefinit).setType(var.type);
- indexDef.sym = var.sym;
+ vardefinit = make.TypeCast(tree.var.type, vardefinit);
+ JCVariableDecl indexDef = (JCVariableDecl)make.VarDef(tree.var.mods,
+ tree.var.name,
+ tree.var.vartype,
+ vardefinit).setType(tree.var.type);
+ indexDef.sym = tree.var.sym;
JCBlock body = make.Block(0, List.of(indexDef, tree.body));
body.endpos = TreeInfo.endPos(tree.body);
result = translate(make.
- ForLoop(List.of(init),
- cond,
- List.nil(),
- body));
+ ForLoop(List.of(init),
+ cond,
+ List.nil(),
+ body));
patchTargets(body, tree, result);
}
@@ -3753,7 +3743,7 @@ public class Lower extends TreeTranslator {
List params = matchException ? List.of(makeNull(), makeNull())
: List.nil();
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);
}
@@ -3780,6 +3770,7 @@ public class Lower extends TreeTranslator {
while (patterns.tail.nonEmpty()) {
convertedCases.append(make_at(c.pos()).Case(JCCase.STATEMENT,
List.of(patterns.head),
+ null,
List.nil(),
null));
patterns = patterns.tail;
@@ -3874,7 +3865,7 @@ public class Lower extends TreeTranslator {
VarSymbol label = (VarSymbol)TreeInfo.symbol(((JCConstantCaseLabel) c.labels.head).expr);
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 {
newCases.append(c);
}
@@ -4046,6 +4037,7 @@ public class Lower extends TreeTranslator {
caseBuffer.append(make.Case(JCCase.STATEMENT,
List.of(make.ConstantCaseLabel(make.Literal(hashCode))),
+ null,
lb.toList(),
null));
}
@@ -4081,6 +4073,7 @@ public class Lower extends TreeTranslator {
lb.append(make.Case(JCCase.STATEMENT, caseExpr == null ? List.of(make.DefaultCaseLabel())
: List.of(make.ConstantCaseLabel(caseExpr)),
+ null,
oneCase.stats, null));
}
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MatchBindingsComputer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MatchBindingsComputer.java
index 65f4f680c3c..e5b5b6308d6 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MatchBindingsComputer.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MatchBindingsComputer.java
@@ -142,7 +142,7 @@ public class MatchBindingsComputer extends TreeScanner {
public MatchBindings finishBindings(JCTree tree, MatchBindings matchBindings) {
switch (tree.getTag()) {
case NOT: case AND: case OR: case BINDINGPATTERN:
- case PARENTHESIZEDPATTERN: case TYPETEST:
+ case TYPETEST:
case PARENS: case RECORDPATTERN:
case CONDEXPR: //error recovery:
return matchBindings;
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java
index 67912090092..7f7f09b3391 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java
@@ -957,10 +957,6 @@ class ThisEscapeAnalyzer extends TreeScanner {
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
}
- @Override
- public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
- }
-
@Override
public void visitRecordPattern(JCRecordPattern that) {
}
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java
index c90660d370a..5a7250c7459 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java
@@ -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.ClassSymbol;
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.Symtab;
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.JCBindingPattern;
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.TreeMaker;
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.JCMethodInvocation;
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.JCPatternCaseLabel;
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.TreeScanner;
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;
/**
@@ -196,8 +198,7 @@ public class TransPatterns extends TreeTranslator {
@Override
public void visitTypeTest(JCInstanceOf tree) {
if (tree.pattern instanceof JCPattern pattern) {
- //first, resolve any parenthesized and record patterns:
- pattern = TreeInfo.skipParens(pattern);
+ //first, resolve any record patterns:
JCExpression extraConditions = null;
if (pattern instanceof JCRecordPattern 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
public void visitRecordPattern(JCRecordPattern tree) {
//record patterns should be resolved by the constructs that use them.
@@ -314,7 +310,7 @@ public class TransPatterns extends TreeTranslator {
while (components.nonEmpty()) {
RecordComponent component = components.head;
Type componentType = types.erasure(nestedFullComponentTypes.head);
- JCPattern nestedPattern = TreeInfo.skipParens(nestedPatterns.head);
+ JCPattern nestedPattern = nestedPatterns.head;
JCBindingPattern nestedBinding;
boolean allowNull;
if (nestedPattern instanceof JCRecordPattern nestedRecordPattern) {
@@ -390,8 +386,6 @@ public class TransPatterns extends TreeTranslator {
Type seltype = selector.type.hasTag(BOT)
? syms.objectType
: selector.type;
- Assert.check(preview.isEnabled());
- Assert.check(preview.usesPreview(env.toplevel.sourcefile));
//rewrite pattern matching switches, performed in several steps:
//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
ListBuffer newCases = new ListBuffer<>();
for (List 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) {
- JCPattern pattern = TreeInfo.skipParens(patternLabel.pat);
+ JCPattern pattern = patternLabel.pat;
if (pattern instanceof JCRecordPattern recordPattern) {
UnrolledRecordPattern deconstructed = unrollRecordPattern(recordPattern);
JCExpression guard = deconstructed.newGuard();
- if (patternLabel.guard != null) {
- guard = mergeConditions(guard, patternLabel.guard);
+ if (cse.guard != null) {
+ cse.guard = mergeConditions(guard, cse.guard);
+ } else {
+ cse.guard = guard;
}
- return make.PatternCaseLabel(deconstructed.primaryPattern(), guard);
+ return make.PatternCaseLabel(deconstructed.primaryPattern());
}
}
return l;
@@ -534,8 +531,8 @@ public class TransPatterns extends TreeTranslator {
try {
currentValue = temp;
JCExpression test = (JCExpression) this.translate(label.pat);
- if (label.guard != null) {
- JCExpression guard = translate(label.guard);
+ if (c.guard != null) {
+ JCExpression guard = translate(c.guard);
test = makeBinary(Tag.AND, test, guard);
}
c.stats = translate(c.stats);
@@ -716,12 +713,12 @@ public class TransPatterns extends TreeTranslator {
replaceNested.scan(accummulated);
JCExpression newGuard;
JCInstanceOf instanceofCheck;
- if (accummulatedFirstLabel.guard instanceof JCBinary binOp) {
+ if (accummulated.guard instanceof JCBinary binOp) {
newGuard = binOp.rhs;
instanceofCheck = (JCInstanceOf) binOp.lhs;
} else {
newGuard = null;
- instanceofCheck = (JCInstanceOf) accummulatedFirstLabel.guard;
+ instanceofCheck = (JCInstanceOf) accummulated.guard;
}
JCBindingPattern binding = (JCBindingPattern) instanceofCheck.pattern;
hasUnconditional =
@@ -730,11 +727,12 @@ public class TransPatterns extends TreeTranslator {
List newLabel;
if (hasUnconditional) {
newLabel = List.of(make.ConstantCaseLabel(makeNull()),
- make.PatternCaseLabel(binding, newGuard));
+ make.PatternCaseLabel(binding));
} 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;
}
if (lastGuard != null || !hasUnconditional) {
@@ -745,6 +743,7 @@ public class TransPatterns extends TreeTranslator {
? List.of(make.DefaultCaseLabel())
: List.of(make.ConstantCaseLabel(makeNull()),
make.DefaultCaseLabel()),
+ null,
List.of(continueSwitch),
null));
}
@@ -752,9 +751,9 @@ public class TransPatterns extends TreeTranslator {
newSwitch.patternSwitch = true;
JCPatternCaseLabel leadingTest =
(JCPatternCaseLabel) accummulator.first().labels.head;
- leadingTest.guard = null;
result.add(make.Case(CaseKind.STATEMENT,
List.of(leadingTest),
+ null,
List.of(newSwitch),
null));
} else {
@@ -776,14 +775,14 @@ public class TransPatterns extends TreeTranslator {
if (c.head.labels.size() == 1 &&
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 &&
instanceofCheck.pattern instanceof JCBindingPattern binding) {
currentBinding = ((JCBindingPattern) patternLabel.pat).var.sym;
currentNullable = instanceofCheck.allowNull;
currentNestedExpression = instanceofCheck.expr;
currentNestedBinding = binding.var.sym;
- } else if (patternLabel.guard instanceof JCInstanceOf instanceofCheck &&
+ } else if (c.head.guard instanceof JCInstanceOf instanceofCheck &&
instanceofCheck.pattern instanceof JCBindingPattern binding) {
currentBinding = ((JCBindingPattern) patternLabel.pat).var.sym;
currentNullable = instanceofCheck.allowNull;
@@ -835,10 +834,15 @@ public class TransPatterns extends TreeTranslator {
} else {
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;
- if ((expr.type.tsym.flags_field & Flags.ENUM) != 0) {
- return LoadableConstant.String(((JCIdent) expr).name.toString());
+ Symbol sym = TreeInfo.symbol(expr);
+ 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 {
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 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
public void visitBinary(JCBinary tree) {
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
- *
- *