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: + * *

* - *

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 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: - *

    - * - * @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: - * - * @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 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 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 - * - *
    -                 *     for ( : coll ) stmt ;
    -                 * 
    - * - * (where coll implements {@code Iterable}) gets translated to - * - *
    {@code
    -                 *     for ( N$temp : coll) {
    -                 *     switch (N$temp) {
    -                 *         case : stmt;
    -                 *         case null: throw new MatchException();
    -                 *     }
    -                 * }
    - * - */ - Type selectorType = types.classBound(tree.elementType); - - currentValue = new VarSymbol(Flags.FINAL | Flags.SYNTHETIC, - names.fromString("patt" + tree.pos + target.syntheticNameChar() + "temp"), - selectorType, - currentMethodSym); - - JCStatement newForVariableDeclaration = - make.at(tree.pos).VarDef(currentValue, null).setType(selectorType); - - List nestedNPEParams = List.of(makeNull()); - JCNewClass nestedNPE = makeNewClass(syms.nullPointerExceptionType, nestedNPEParams); - - List matchExParams = List.of(makeNull(), nestedNPE); - JCThrow thr = make.Throw(makeNewClass(syms.matchExceptionType, matchExParams)); - - JCCase caseNull = make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(makeNull())), List.of(thr), null); - - JCCase casePattern = make.Case(CaseTree.CaseKind.STATEMENT, - List.of(make.PatternCaseLabel(jcRecordPattern, null)), - List.of(translate(tree.body)), - null); - - JCSwitch switchBody = - make.Switch(make.Ident(currentValue).setType(selectorType), - List.of(caseNull, casePattern)); - - switchBody.patternSwitch = true; - - // re-using the same node to eliminate the need to re-patch targets (break/continue) - tree.varOrRecordPattern = newForVariableDeclaration.setType(selectorType); - tree.expr = translate(tree.expr); - tree.body = translate(switchBody); - - JCTree.JCEnhancedForLoop newForEach = tree; - - result = bindingContext.decorateStatement(newForEach); - } else { - super.visitForeachLoop(tree); - result = bindingContext.decorateStatement(tree); - } - } finally { - currentValue = prevCurrentValue; - bindingContext.pop(); - } - } - @Override public void visitWhileLoop(JCWhileLoop tree) { bindingContext = new BasicBindingContext(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java index 246a3f69b10..5fb82aca8a0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java @@ -512,7 +512,7 @@ public class TransTypes extends TreeTranslator { } public void visitForeachLoop(JCEnhancedForLoop tree) { - tree.varOrRecordPattern = translate(tree.varOrRecordPattern, null); + tree.var = translate(tree.var, null); Type iterableType = tree.expr.type; tree.expr = translate(tree.expr, erasure(tree.expr.type)); if (types.elemtype(tree.expr.type) == null) @@ -551,6 +551,7 @@ public class TransTypes extends TreeTranslator { public void visitCase(JCCase tree) { tree.labels = translate(tree.labels, null); + tree.guard = translate(tree.guard, syms.booleanType); tree.stats = translate(tree.stats); result = tree; } @@ -569,7 +570,6 @@ public class TransTypes extends TreeTranslator { @Override public void visitPatternCaseLabel(JCPatternCaseLabel tree) { tree.pat = translate(tree.pat, null); - tree.guard = translate(tree.guard, syms.booleanType); result = tree; } @@ -584,12 +584,6 @@ public class TransTypes extends TreeTranslator { result = retype(tree, tree.type, pt); } - @Override - public void visitParenthesizedPattern(JCParenthesizedPattern tree) { - tree.pattern = translate(tree.pattern, null); - result = tree; - } - public void visitRecordPattern(JCRecordPattern tree) { tree.fullComponentTypes = tree.record.getRecordComponents() .map(rc -> types.memberType(tree.type, rc)); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java index 36222b88766..4bcc7937aa9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TreeDiffer.java @@ -297,7 +297,9 @@ public class TreeDiffer extends TreeScanner { @Override public void visitCase(JCCase tree) { JCCase that = (JCCase) parameter; - result = scan(tree.labels, that.labels) && scan(tree.stats, that.stats); + result = scan(tree.labels, that.labels) && + scan(tree.guard, that.guard) && + scan(tree.stats, that.stats); } @Override @@ -309,7 +311,7 @@ public class TreeDiffer extends TreeScanner { @Override public void visitPatternCaseLabel(JCPatternCaseLabel tree) { JCPatternCaseLabel that = (JCPatternCaseLabel) parameter; - result = scan(tree.pat, that.pat) && scan(tree.guard, that.guard); + result = scan(tree.pat, that.pat); } @Override @@ -388,7 +390,7 @@ public class TreeDiffer extends TreeScanner { public void visitForeachLoop(JCEnhancedForLoop tree) { JCEnhancedForLoop that = (JCEnhancedForLoop) parameter; result = - scan(tree.varOrRecordPattern, that.varOrRecordPattern) + scan(tree.var, that.var) && scan(tree.expr, that.expr) && scan(tree.body, that.body); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java index 8e16d7e3c48..2cd4142c748 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java @@ -293,7 +293,7 @@ implements CRTFlags { public void visitForeachLoop(JCEnhancedForLoop tree) { SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); - sr.mergeWith(csp(tree.varOrRecordPattern)); + sr.mergeWith(csp(tree.var)); sr.mergeWith(csp(tree.expr)); sr.mergeWith(csp(tree.body)); result = sr; @@ -323,6 +323,7 @@ implements CRTFlags { public void visitCase(JCCase tree) { SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); sr.mergeWith(csp(tree.labels)); + sr.mergeWith(csp(tree.guard)); sr.mergeWith(csp(tree.stats)); result = sr; } @@ -343,7 +344,6 @@ implements CRTFlags { public void visitPatternCaseLabel(JCPatternCaseLabel tree) { SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); sr.mergeWith(csp(tree.pat)); - sr.mergeWith(csp(tree.guard)); result = sr; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index c0d8d9b00cc..3695c3cbeeb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -941,6 +941,15 @@ public class ClassWriter extends ClassFile { */ void writeBootstrapMethods() { int alenIdx = writeAttr(names.BootstrapMethods); + int lastBootstrapMethods; + do { + lastBootstrapMethods = poolWriter.bootstrapMethods.size(); + for (BsmKey bsmKey : java.util.List.copyOf(poolWriter.bootstrapMethods.keySet())) { + for (LoadableConstant arg : bsmKey.staticArgs) { + poolWriter.putConstant(arg); + } + } + } while (lastBootstrapMethods < poolWriter.bootstrapMethods.size()); databuf.appendChar(poolWriter.bootstrapMethods.size()); for (BsmKey bsmKey : poolWriter.bootstrapMethods.keySet()) { //write BSM handle diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 6988e7b7189..a957b66b296 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -1535,11 +1535,6 @@ public class JavaCompiler { super.visitRecordPattern(that); } @Override - public void visitParenthesizedPattern(JCTree.JCParenthesizedPattern tree) { - hasPatterns = true; - super.visitParenthesizedPattern(tree); - } - @Override public void visitSwitch(JCSwitch tree) { hasPatterns |= tree.patternSwitch; super.visitSwitch(tree); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index b9e52cf6293..5413888a3e9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -830,48 +830,52 @@ public class JavacParser implements Parser { /** parses patterns. */ + public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType, boolean allowVar, boolean checkGuard) { JCPattern pattern; - if (token.kind == LPAREN && parsedType == null) { - //parenthesized pattern: - int startPos = token.pos; - accept(LPAREN); - JCPattern p = parsePattern(token.pos, null, null, true, false); - accept(RPAREN); - pattern = toP(F.at(startPos).ParenthesizedPattern(p)); + mods = mods != null ? mods : optFinal(0); + JCExpression e; + if (parsedType == null) { + boolean var = token.kind == IDENTIFIER && token.name() == names.var; + e = unannotatedType(allowVar, TYPE | NOLAMBDA); + if (var) { + e = null; + } } else { - mods = mods != null ? mods : optFinal(0); - JCExpression e; - if (parsedType == null) { - boolean var = token.kind == IDENTIFIER && token.name() == names.var; - e = unannotatedType(allowVar, TYPE | NOLAMBDA); - if (var) { - e = null; - } - } else { - e = parsedType; - } - if (token.kind == LPAREN) { - //deconstruction pattern: - checkSourceLevel(Feature.RECORD_PATTERNS); - ListBuffer nested = new ListBuffer<>(); - if (!peekToken(RPAREN)) { - do { - nextToken(); - JCPattern nestedPattern = parsePattern(token.pos, null, null, true, false); - nested.append(nestedPattern); - } while (token.kind == COMMA); - } else { + e = parsedType; + } + if (token.kind == LPAREN) { + //deconstruction pattern: + checkSourceLevel(Feature.RECORD_PATTERNS); + ListBuffer nested = new ListBuffer<>(); + if (!peekToken(RPAREN)) { + do { nextToken(); - } - accept(RPAREN); - pattern = toP(F.at(pos).RecordPattern(e, nested.toList())); + JCPattern nestedPattern = parsePattern(token.pos, null, null, true, false); + nested.append(nestedPattern); + } while (token.kind == COMMA); } else { - //type test pattern: - JCVariableDecl var = toP(F.at(token.pos).VarDef(mods, ident(), e, null)); - pattern = toP(F.at(pos).BindingPattern(var)); + nextToken(); } + accept(RPAREN); + pattern = toP(F.at(pos).RecordPattern(e, nested.toList())); + if (mods.annotations.nonEmpty()) { + log.error(mods.annotations.head.pos(), Errors.RecordPatternsAnnotationsNotAllowed); + } + new TreeScanner() { + @Override + public void visitAnnotatedType(JCAnnotatedType tree) { + log.error(tree.pos(), Errors.RecordPatternsAnnotationsNotAllowed); + } + }.scan(e); + } else { + //type test pattern: + JCVariableDecl var = toP(F.at(token.pos).VarDef(mods, ident(), e, null)); + if (e == null) { + var.startPos = pos; + } + pattern = toP(F.at(pos).BindingPattern(var)); } return pattern; } @@ -1620,6 +1624,7 @@ public class JavacParser implements Parser { allowDefault = TreeInfo.isNullCaseLabel(label); }; } + JCExpression guard = parseGuard(pats.last()); List stats = null; JCTree body = null; CaseTree.CaseKind kind; @@ -1645,7 +1650,7 @@ public class JavacParser implements Parser { kind = JCCase.STATEMENT; break; } - caseExprs.append(toP(F.at(casePos).Case(kind, pats.toList(), stats, body))); + caseExprs.append(toP(F.at(casePos).Case(kind, pats.toList(), guard, stats, body))); return caseExprs.toList(); } @@ -2910,47 +2915,25 @@ public class JavacParser implements Parser { case FOR: { nextToken(); accept(LPAREN); - JCTree pattern; - - ForInitResult initResult = analyzeForInit(); - - if (initResult == ForInitResult.RecordPattern) { - int patternPos = token.pos; - JCModifiers mods = optFinal(0); - int typePos = token.pos; - JCExpression type = unannotatedType(false); - - pattern = parsePattern(patternPos, mods, type, false, false); - - if (pattern != null) { - checkSourceLevel(token.pos, Feature.PATTERN_SWITCH); - } + List inits = token.kind == SEMI ? List.nil() : forInit(); + if (inits.length() == 1 && + inits.head.hasTag(VARDEF) && + ((JCVariableDecl) inits.head).init == null && + token.kind == COLON) { + JCVariableDecl var = (JCVariableDecl)inits.head; accept(COLON); JCExpression expr = parseExpression(); accept(RPAREN); JCStatement body = parseStatementAsBlock(); - return F.at(pos).ForeachLoop(pattern, expr, body); + return F.at(pos).ForeachLoop(var, expr, body); } else { - List inits = token.kind == SEMI ? List.nil() : forInit(); - if (inits.length() == 1 && - inits.head.hasTag(VARDEF) && - ((JCVariableDecl) inits.head).init == null && - token.kind == COLON) { - JCVariableDecl var = (JCVariableDecl) inits.head; - accept(COLON); - JCExpression expr = parseExpression(); - accept(RPAREN); - JCStatement body = parseStatementAsBlock(); - return F.at(pos).ForeachLoop(var, expr, body); - } else { - accept(SEMI); - JCExpression cond = token.kind == SEMI ? null : parseExpression(); - accept(SEMI); - List steps = token.kind == RPAREN ? List.nil() : forUpdate(); - accept(RPAREN); - JCStatement body = parseStatementAsBlock(); - return F.at(pos).ForLoop(inits, cond, steps, body); - } + accept(SEMI); + JCExpression cond = token.kind == SEMI ? null : parseExpression(); + accept(SEMI); + List steps = token.kind == RPAREN ? List.nil() : forUpdate(); + accept(RPAREN); + JCStatement body = parseStatementAsBlock(); + return F.at(pos).ForLoop(inits, cond, steps, body); } } case WHILE: { @@ -3067,91 +3050,6 @@ public class JavacParser implements Parser { } } - private enum ForInitResult { - LocalVarDecl, - RecordPattern - } - - @SuppressWarnings("fallthrough") - ForInitResult analyzeForInit() { - boolean inType = false; - boolean inSelectionAndParenthesis = false; - int typeParameterPossibleStart = -1; - outer: for (int lookahead = 0; ; lookahead++) { - TokenKind tk = S.token(lookahead).kind; - switch (tk) { - case DOT: - if (inType) break; // in qualified type - case COMMA: - typeParameterPossibleStart = lookahead; - break; - case QUES: - // "?" only allowed in a type parameter position - otherwise it's an expression - if (typeParameterPossibleStart == lookahead - 1) break; - else return ForInitResult.LocalVarDecl; - case EXTENDS: case SUPER: case AMP: - case GTGTGT: case GTGT: case GT: - case FINAL: case ELLIPSIS: - break; - case BYTE: case SHORT: case INT: case LONG: case FLOAT: - case DOUBLE: case BOOLEAN: case CHAR: case VOID: - if (peekToken(lookahead, IDENTIFIER)) { - return inSelectionAndParenthesis ? ForInitResult.RecordPattern - : ForInitResult.LocalVarDecl; - } - break; - case LPAREN: - if (lookahead != 0 && inType) { - inSelectionAndParenthesis = true; - inType = false; - } - break; - case RPAREN: - // a method call in the init part or a record pattern? - if (inSelectionAndParenthesis) { - if (peekToken(lookahead, DOT) || - peekToken(lookahead, SEMI) || - peekToken(lookahead, ARROW)) { - return ForInitResult.LocalVarDecl; - } - else if(peekToken(lookahead, COLON)) { - return ForInitResult.RecordPattern; - } - break; - } - case UNDERSCORE: - case ASSERT: - case ENUM: - case IDENTIFIER: - if (lookahead == 0) { - inType = true; - } - break; - case MONKEYS_AT: { - int prevLookahead = lookahead; - lookahead = skipAnnotation(lookahead); - if (typeParameterPossibleStart == prevLookahead - 1) { - // move possible start of type param after the anno - typeParameterPossibleStart = lookahead; - } - break; - } - case LBRACKET: - if (peekToken(lookahead, RBRACKET)) { - return inSelectionAndParenthesis ? ForInitResult.RecordPattern - : ForInitResult.LocalVarDecl; - } - return ForInitResult.LocalVarDecl; - case LT: - typeParameterPossibleStart = lookahead; - break; - default: - //this includes EOF - return ForInitResult.LocalVarDecl; - } - } - } - @Override public JCStatement parseStatement() { return parseStatementAsBlock(); @@ -3234,6 +3132,7 @@ public class JavacParser implements Parser { checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS); allowDefault = TreeInfo.isNullCaseLabel(label); }; + JCExpression guard = parseGuard(pats.last()); CaseTree.CaseKind caseKind; JCTree body = null; if (token.kind == ARROW) { @@ -3251,7 +3150,7 @@ public class JavacParser implements Parser { caseKind = JCCase.STATEMENT; stats = blockStatements(); } - c = F.at(pos).Case(caseKind, pats.toList(), stats, body); + c = F.at(pos).Case(caseKind, pats.toList(), guard, stats, body); if (stats.isEmpty()) storeEnd(c, S.prevToken().endPos); return cases.append(c).toList(); @@ -3259,6 +3158,7 @@ public class JavacParser implements Parser { case DEFAULT: { nextToken(); JCCaseLabel defaultPattern = toP(F.at(pos).DefaultCaseLabel()); + JCExpression guard = parseGuard(defaultPattern); CaseTree.CaseKind caseKind; JCTree body = null; if (token.kind == ARROW) { @@ -3276,7 +3176,7 @@ public class JavacParser implements Parser { caseKind = JCCase.STATEMENT; stats = blockStatements(); } - c = F.at(pos).Case(caseKind, List.of(defaultPattern), stats, body); + c = F.at(pos).Case(caseKind, List.of(defaultPattern), guard, stats, body); if (stats.isEmpty()) storeEnd(c, S.prevToken().endPos); return cases.append(c).toList(); @@ -3304,12 +3204,7 @@ public class JavacParser implements Parser { if (pattern) { checkSourceLevel(token.pos, Feature.PATTERN_SWITCH); JCPattern p = parsePattern(patternPos, mods, null, false, true); - JCExpression guard = null; - if (token.kind == IDENTIFIER && token.name() == names.when) { - nextToken(); - guard = term(EXPR | NOLAMBDA); - } - return toP(F.at(patternPos).PatternCaseLabel(p, guard)); + return toP(F.at(patternPos).PatternCaseLabel(p)); } else { JCExpression expr = term(EXPR | NOLAMBDA); return toP(F.at(patternPos).ConstantCaseLabel(expr)); @@ -3319,6 +3214,22 @@ public class JavacParser implements Parser { return label; } + private JCExpression parseGuard(JCCaseLabel label) { + JCExpression guard = null; + + if (token.kind == IDENTIFIER && token.name() == names.when) { + int pos = token.pos; + + nextToken(); + guard = term(EXPR | NOLAMBDA); + + if (!(label instanceof JCPatternCaseLabel)) { + guard = syntaxError(pos, List.of(guard), Errors.GuardNotAllowed); + } + } + + return guard; + } @SuppressWarnings("fallthrough") PatternResult analyzePattern(int lookahead) { int typeDepth = 0; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 4473b7603f0..01a007247d5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -528,9 +528,16 @@ compiler.err.duplicate.unconditional.pattern=\ compiler.err.unconditional.pattern.and.default=\ switch has both an unconditional pattern and a default label +compiler.err.guard.not.allowed=\ + guards are only allowed for case with a pattern + compiler.err.guard.has.constant.expression.false=\ this case label has a guard that is a constant expression with value ''false'' +# 0: symbol +compiler.err.cannot.assign.not.declared.guard=\ + cannot assign to {0}, as it was not declared inside the guard + # 0: type, 1: type compiler.err.constant.label.not.compatible=\ constant label of type {0} is not compatible with switch selector type {1} @@ -619,10 +626,6 @@ compiler.err.foreach.not.applicable.to.type=\ required: {1}\n\ found: {0} -# 0: type, 1: type -compiler.err.foreach.not.exhaustive.on.type=\ - Pattern {0} is not exhaustive on {1} - compiler.err.fp.number.too.large=\ floating-point number too large @@ -1500,10 +1503,6 @@ compiler.misc.varargs.trustme.on.reifiable.varargs=\ compiler.err.instanceof.reifiable.not.safe=\ {0} cannot be safely cast to {1} -# 0: type, 1: type -compiler.err.instanceof.pattern.no.subtype=\ - expression type {0} is a subtype of pattern type {1} - # 0: symbol compiler.misc.varargs.trustme.on.non.varargs.meth=\ Method {0} is not a varargs method. @@ -3854,6 +3853,9 @@ compiler.err.instance.initializer.not.allowed.in.records=\ compiler.err.static.declaration.not.allowed.in.inner.classes=\ static declarations not allowed in inner classes +compiler.err.record.patterns.annotations.not.allowed=\ + annotations not allowed on record patterns + ############################################ # messages previously at javac.properties diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index 9d884631b4f..e1c6cc65783 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -240,7 +240,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { /** Patterns. */ BINDINGPATTERN, - PARENTHESIZEDPATTERN, RECORDPATTERN, /* Case labels. @@ -1222,13 +1221,11 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { * The enhanced for loop. */ public static class JCEnhancedForLoop extends JCStatement implements EnhancedForLoopTree { - public JCTree varOrRecordPattern; + public JCVariableDecl var; public JCExpression expr; public JCStatement body; - public Type elementType; - - protected JCEnhancedForLoop(JCTree varOrRecordPattern, JCExpression expr, JCStatement body) { - this.varOrRecordPattern = varOrRecordPattern; + protected JCEnhancedForLoop(JCVariableDecl var, JCExpression expr, JCStatement body) { + this.var = var; this.expr = expr; this.body = body; } @@ -1238,11 +1235,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { @DefinedBy(Api.COMPILER_TREE) public Kind getKind() { return Kind.ENHANCED_FOR_LOOP; } @DefinedBy(Api.COMPILER_TREE) - public JCVariableDecl getVariable() { - return varOrRecordPattern instanceof JCVariableDecl var ? var : null; - } - @DefinedBy(Api.COMPILER_TREE) - public JCTree getVariableOrRecordPattern() { return varOrRecordPattern; } + public JCVariableDecl getVariable() { return var; } @DefinedBy(Api.COMPILER_TREE) public JCExpression getExpression() { return expr; } @DefinedBy(Api.COMPILER_TREE) @@ -1255,10 +1248,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { public Tag getTag() { return FOREACHLOOP; } - @Override @DefinedBy(Api.COMPILER_TREE) - public EnhancedForLoopTree.DeclarationKind getDeclarationKind() { - return varOrRecordPattern.hasTag(VARDEF) ? DeclarationKind.VARIABLE : DeclarationKind.PATTERN; - } } /** @@ -1334,15 +1323,18 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { public static final CaseKind RULE = CaseKind.RULE; public final CaseKind caseKind; public List labels; + public JCExpression guard; public List stats; public JCTree body; public boolean completesNormally; protected JCCase(CaseKind caseKind, List labels, + JCExpression guard, List stats, JCTree body) { Assert.checkNonNull(labels); Assert.check(labels.isEmpty() || labels.head != null); this.caseKind = caseKind; this.labels = labels; + this.guard = guard; this.stats = stats; this.body = body; } @@ -1365,6 +1357,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { @Override @DefinedBy(Api.COMPILER_TREE) public List getLabels() { return labels; } @Override @DefinedBy(Api.COMPILER_TREE) + public JCExpression getGuard() { return guard; } + @Override @DefinedBy(Api.COMPILER_TREE) public List getStatements() { return caseKind == CaseKind.STATEMENT ? stats : null; } @@ -2252,10 +2246,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { @DefinedBy(Api.COMPILER_TREE) public JCExpression getExpression() { return expr; } - @DefinedBy(Api.COMPILER_TREE) - public TestKind getTestKind() { - return pattern instanceof JCPatternCaseLabel ? TestKind.PATTERN : TestKind.TYPE; - } @Override @DefinedBy(Api.COMPILER_TREE) public R accept(TreeVisitor v, D d) { return v.visitInstanceOf(this, d); @@ -2378,11 +2368,9 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { implements PatternCaseLabelTree { public JCPattern pat; - public JCExpression guard; - protected JCPatternCaseLabel(JCPattern pat, JCExpression guard) { + protected JCPatternCaseLabel(JCPattern pat) { this.pat = pat; - this.guard = guard; } @Override @DefinedBy(Api.COMPILER_TREE) @@ -2390,11 +2378,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { return pat; } - @Override @DefinedBy(Api.COMPILER_TREE) - public JCExpression getGuard() { - return guard; - } - @Override public void accept(Visitor v) { v.visitPatternCaseLabel(this); @@ -2418,41 +2401,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { } - public static class JCParenthesizedPattern extends JCPattern - implements ParenthesizedPatternTree { - public JCPattern pattern; - - public JCParenthesizedPattern(JCPattern pattern) { - this.pattern = pattern; - } - - @Override @DefinedBy(Api.COMPILER_TREE) - public PatternTree getPattern() { - return pattern; - } - - @Override - public void accept(Visitor v) { - v.visitParenthesizedPattern(this); - } - - @DefinedBy(Api.COMPILER_TREE) - public Kind getKind() { - return Kind.PARENTHESIZED_PATTERN; - } - - @Override - @DefinedBy(Api.COMPILER_TREE) - public R accept(TreeVisitor v, D d) { - return v.visitParenthesizedPattern(this, d); - } - - @Override - public Tag getTag() { - return PARENTHESIZEDPATTERN; - } - } - public static class JCRecordPattern extends JCPattern implements DeconstructionPatternTree { public JCExpression deconstructor; @@ -3487,11 +3435,11 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { JCExpression cond, List step, JCStatement body); - JCEnhancedForLoop ForeachLoop(JCTree var, JCExpression expr, JCStatement body); + JCEnhancedForLoop ForeachLoop(JCVariableDecl var, JCExpression expr, JCStatement body); JCLabeledStatement Labelled(Name label, JCStatement body); JCSwitch Switch(JCExpression selector, List cases); JCSwitchExpression SwitchExpression(JCExpression selector, List cases); - JCCase Case(CaseTree.CaseKind caseKind, List labels, + JCCase Case(CaseTree.CaseKind caseKind, List labels, JCExpression guard, List stats, JCTree body); JCSynchronized Synchronized(JCExpression lock, JCBlock body); JCTry Try(JCBlock body, List catchers, JCBlock finalizer); @@ -3601,7 +3549,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { public void visitDefaultCaseLabel(JCDefaultCaseLabel that) { visitTree(that); } public void visitConstantCaseLabel(JCConstantCaseLabel that) { visitTree(that); } public void visitPatternCaseLabel(JCPatternCaseLabel that) { visitTree(that); } - public void visitParenthesizedPattern(JCParenthesizedPattern that) { visitTree(that); } public void visitRecordPattern(JCRecordPattern that) { visitTree(that); } public void visitIndexed(JCArrayAccess that) { visitTree(that); } public void visitSelect(JCFieldAccess that) { visitTree(that); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java index a18e789661a..8fd27531f89 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java @@ -814,7 +814,7 @@ public class Pretty extends JCTree.Visitor { public void visitForeachLoop(JCEnhancedForLoop tree) { try { print("for ("); - printExpr(tree.varOrRecordPattern); + printExpr(tree.var); print(" : "); printExpr(tree.expr); print(") "); @@ -862,6 +862,10 @@ public class Pretty extends JCTree.Visitor { print("case "); printExprs(tree.labels); } + if (tree.guard != null) { + print(" when "); + print(tree.guard); + } if (tree.caseKind == JCCase.STATEMENT) { print(':'); println(); @@ -904,10 +908,6 @@ public class Pretty extends JCTree.Visitor { public void visitPatternCaseLabel(JCPatternCaseLabel tree) { try { print(tree.pat); - if (tree.guard != null) { - print(" when "); - print(tree.guard); - } } catch (IOException e) { throw new UncheckedIOException(e); } @@ -941,17 +941,6 @@ public class Pretty extends JCTree.Visitor { } } - @Override - public void visitParenthesizedPattern(JCParenthesizedPattern patt) { - try { - print('('); - printExpr(patt.pattern); - print(')'); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - @Override public void visitRecordPattern(JCRecordPattern tree) { try { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java index 5775a797208..d3abdc70142 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java @@ -153,6 +153,7 @@ public class TreeCopier

    implements TreeVisitor { public JCTree visitCase(CaseTree node, P p) { JCCase t = (JCCase) node; List labels = copy(t.labels, p); + JCExpression guard = copy(t.guard, p); List stats = copy(t.stats, p); JCTree body; if (node.getCaseKind() == CaseTree.CaseKind.RULE) { @@ -161,7 +162,7 @@ public class TreeCopier

    implements TreeVisitor { } else { body = null; } - return M.at(t.pos).Case(t.caseKind, labels, stats, body); + return M.at(t.pos).Case(t.caseKind, labels, guard, stats, body); } @DefinedBy(Api.COMPILER_TREE) @@ -223,10 +224,10 @@ public class TreeCopier

    implements TreeVisitor { @DefinedBy(Api.COMPILER_TREE) public JCTree visitEnhancedForLoop(EnhancedForLoopTree node, P p) { JCEnhancedForLoop t = (JCEnhancedForLoop) node; - JCTree varOrRecordPattern = copy(t.varOrRecordPattern, p); + JCVariableDecl var = copy(t.var, p); JCExpression expr = copy(t.expr, p); JCStatement body = copy(t.body, p); - return M.at(t.pos).ForeachLoop(varOrRecordPattern, expr, body); + return M.at(t.pos).ForeachLoop(var, expr, body); } @DefinedBy(Api.COMPILER_TREE) @@ -505,13 +506,6 @@ public class TreeCopier

    implements TreeVisitor { return M.at(t.pos).BindingPattern(var); } - @DefinedBy(Api.COMPILER_TREE) - public JCTree visitParenthesizedPattern(ParenthesizedPatternTree node, P p) { - JCParenthesizedPattern t = (JCParenthesizedPattern) node; - JCPattern pattern = copy(t.pattern, p); - return M.at(t.pos).ParenthesizedPattern(pattern); - } - @DefinedBy(Api.COMPILER_TREE) public JCTree visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) { JCDefaultCaseLabel t = (JCDefaultCaseLabel) node; @@ -529,8 +523,7 @@ public class TreeCopier

    implements TreeVisitor { public JCTree visitPatternCaseLabel(PatternCaseLabelTree node, P p) { JCPatternCaseLabel t = (JCPatternCaseLabel) node; JCPattern pat = copy(t.pat, p); - JCExpression guard = copy(t.guard, p); - return M.at(t.pos).PatternCaseLabel(pat, guard); + return M.at(t.pos).PatternCaseLabel(pat); } @DefinedBy(Api.COMPILER_TREE) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index ae0b6dfa627..550eeb9c7fe 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -648,10 +648,6 @@ public class TreeInfo { return getEndPos(((JCWhileLoop) tree).body, endPosTable); case ANNOTATED_TYPE: return getEndPos(((JCAnnotatedType) tree).underlyingType, endPosTable); - case PARENTHESIZEDPATTERN: { - JCParenthesizedPattern node = (JCParenthesizedPattern) tree; - return getEndPos(node.pattern, endPosTable); - } case ERRONEOUS: { JCErroneous node = (JCErroneous)tree; if (node.errs != null && node.errs.nonEmpty()) @@ -855,15 +851,6 @@ public class TreeInfo { return tree; } - /** Skip parens and return the enclosed expression - */ - public static JCPattern skipParens(JCPattern tree) { - while (tree.hasTag(PARENTHESIZEDPATTERN)) { - tree = ((JCParenthesizedPattern) tree).pattern; - } - return tree; - } - /** Return the types of a list of trees. */ public static List types(List trees) { @@ -1358,7 +1345,6 @@ public class TreeInfo { public static Type primaryPatternType(JCTree pat) { return switch (pat.getTag()) { case BINDINGPATTERN -> pat.type; - case PARENTHESIZEDPATTERN -> primaryPatternType(((JCParenthesizedPattern) pat).pattern); case RECORDPATTERN -> ((JCRecordPattern) pat).type; default -> throw new AssertionError(); }; @@ -1367,7 +1353,6 @@ public class TreeInfo { public static JCTree primaryPatternTypeTree(JCTree pat) { return switch (pat.getTag()) { case BINDINGPATTERN -> ((JCBindingPattern) pat).var.vartype; - case PARENTHESIZEDPATTERN -> primaryPatternTypeTree(((JCParenthesizedPattern) pat).pattern); case RECORDPATTERN -> ((JCRecordPattern) pat).deconstructor; default -> throw new AssertionError(); }; @@ -1380,11 +1365,8 @@ public class TreeInfo { .anyMatch(l -> TreeInfo.isNullCaseLabel(l)); } - public static boolean unguardedCaseLabel(JCCaseLabel cse) { - if (!cse.hasTag(PATTERNCASELABEL)) { - return true; - } - JCExpression guard = ((JCPatternCaseLabel) cse).guard; + public static boolean unguardedCase(JCCase cse) { + JCExpression guard = cse.guard; if (guard == null) { return true; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java index eb28a54121a..26713e22d1d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java @@ -274,8 +274,8 @@ public class TreeMaker implements JCTree.Factory { return tree; } - public JCEnhancedForLoop ForeachLoop(JCTree varOrRecordPattern, JCExpression expr, JCStatement body) { - JCEnhancedForLoop tree = new JCEnhancedForLoop(varOrRecordPattern, expr, body); + public JCEnhancedForLoop ForeachLoop(JCVariableDecl var, JCExpression expr, JCStatement body) { + JCEnhancedForLoop tree = new JCEnhancedForLoop(var, expr, body); tree.pos = pos; return tree; } @@ -293,8 +293,8 @@ public class TreeMaker implements JCTree.Factory { } public JCCase Case(CaseTree.CaseKind caseKind, List labels, - List stats, JCTree body) { - JCCase tree = new JCCase(caseKind, labels, stats, body); + JCExpression guard, List stats, JCTree body) { + JCCase tree = new JCCase(caseKind, labels, guard, stats, body); tree.pos = pos; return tree; } @@ -501,14 +501,8 @@ public class TreeMaker implements JCTree.Factory { return tree; } - public JCPatternCaseLabel PatternCaseLabel(JCPattern pat, JCExpression guard) { - JCPatternCaseLabel tree = new JCPatternCaseLabel(pat, guard); - tree.pos = pos; - return tree; - } - - public JCParenthesizedPattern ParenthesizedPattern(JCPattern pattern) { - JCParenthesizedPattern tree = new JCParenthesizedPattern(pattern); + public JCPatternCaseLabel PatternCaseLabel(JCPattern pat) { + JCPatternCaseLabel tree = new JCPatternCaseLabel(pat); tree.pos = pos; return tree; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java index 83ccd2e4ec8..3cb13ef313e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java @@ -162,7 +162,7 @@ public class TreeScanner extends Visitor { } public void visitForeachLoop(JCEnhancedForLoop tree) { - scan(tree.varOrRecordPattern); + scan(tree.var); scan(tree.expr); scan(tree.body); } @@ -178,6 +178,7 @@ public class TreeScanner extends Visitor { public void visitCase(JCCase tree) { scan(tree.labels); + scan(tree.guard); scan(tree.stats); } @@ -319,12 +320,6 @@ public class TreeScanner extends Visitor { @Override public void visitPatternCaseLabel(JCPatternCaseLabel tree) { scan(tree.pat); - scan(tree.guard); - } - - @Override - public void visitParenthesizedPattern(JCParenthesizedPattern tree) { - scan(tree.pattern); } @Override diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java index 387b4d20908..a29e512daea 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeTranslator.java @@ -189,7 +189,7 @@ public class TreeTranslator extends JCTree.Visitor { } public void visitForeachLoop(JCEnhancedForLoop tree) { - tree.varOrRecordPattern = translate(tree.varOrRecordPattern); + tree.var = translate(tree.var); tree.expr = translate(tree.expr); tree.body = translate(tree.body); result = tree; @@ -208,6 +208,7 @@ public class TreeTranslator extends JCTree.Visitor { public void visitCase(JCCase tree) { tree.labels = translate(tree.labels); + tree.guard = translate(tree.guard); tree.stats = translate(tree.stats); result = tree; } @@ -377,13 +378,6 @@ public class TreeTranslator extends JCTree.Visitor { @Override public void visitPatternCaseLabel(JCPatternCaseLabel tree) { tree.pat = translate(tree.pat); - tree.guard = translate(tree.guard); - result = tree; - } - - @Override - public void visitParenthesizedPattern(JCParenthesizedPattern tree) { - tree.pattern = translate(tree.pattern); result = tree; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java index 217a7e6ae1d..39c2ccd616c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java @@ -92,9 +92,11 @@ public class Names { public final Name hasNext; public final Name hashCode; public final Name init; + public final Name invoke; public final Name iterator; public final Name length; public final Name next; + public final Name of; public final Name ordinal; public final Name provider; public final Name serialVersionUID; @@ -221,6 +223,7 @@ public class Names { // pattern switches public final Name typeSwitch; public final Name enumSwitch; + public final Name enumConstant; // templated string public final Name process; @@ -282,9 +285,11 @@ public class Names { hasNext = fromString("hasNext"); hashCode = fromString("hashCode"); init = fromString(""); + invoke = fromString("invoke"); iterator = fromString("iterator"); length = fromString("length"); next = fromString("next"); + of = fromString("of"); ordinal = fromString("ordinal"); provider = fromString("provider"); serialVersionUID = fromString("serialVersionUID"); @@ -415,6 +420,7 @@ public class Names { // pattern switches typeSwitch = fromString("typeSwitch"); enumSwitch = fromString("enumSwitch"); + enumConstant = fromString("enumConstant"); } protected Name.Table createTable(Options options) { diff --git a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java index e83c0209994..f69a9b78cef 100644 --- a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java +++ b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java @@ -24,15 +24,20 @@ */ import java.io.Serializable; +import java.lang.Enum.EnumDesc; +import java.lang.constant.ClassDesc; import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.runtime.SwitchBootstraps; +import java.util.concurrent.atomic.AtomicBoolean; import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; /** @@ -158,6 +163,28 @@ public class SwitchBootstrapsTest { } } + public void testSwitchLabelTypes() throws Throwable { + enum E {A} + try { + testType(E.A, 0, -1, E.A); + fail("Didn't get the expected exception."); + } catch (IllegalArgumentException ex) { + //OK, expected + } + } + + public void testSwitchQualifiedEnum() throws Throwable { + enum E {A, B, C} + Object[] labels = new Object[] { + EnumDesc.of(ClassDesc.of(E.class.getName()), "A"), + EnumDesc.of(ClassDesc.of(E.class.getName()), "B"), + EnumDesc.of(ClassDesc.of(E.class.getName()), "C") + }; + testType(E.A, 0, 0, labels); + testType(E.B, 0, 1, labels); + testType(E.C, 0, 2, labels); + } + public void testNullLabels() throws Throwable { MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); try { @@ -188,4 +215,100 @@ public class SwitchBootstrapsTest { //OK } } + + private static AtomicBoolean enumInitialized = new AtomicBoolean(); + public void testEnumInitialization1() throws Throwable { + enumInitialized.set(false); + + enum E { + A; + + static { + enumInitialized.set(true); + } + } + + MethodType enumSwitchType = MethodType.methodType(int.class, E.class, int.class); + + CallSite invocation = (CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, new Object[] {"A"}); + assertFalse(enumInitialized.get()); + assertEquals(invocation.dynamicInvoker().invoke(null, 0), -1); + assertFalse(enumInitialized.get()); + E e = E.A; + assertTrue(enumInitialized.get()); + assertEquals(invocation.dynamicInvoker().invoke(e, 0), 0); + } + + public void testEnumInitialization2() throws Throwable { + enumInitialized.set(false); + + enum E { + A; + + static { + enumInitialized.set(true); + } + } + + MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); + Object[] labels = new Object[] { + EnumDesc.of(ClassDesc.of(E.class.getName()), "A"), + "test" + }; + CallSite invocation = (CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels); + assertFalse(enumInitialized.get()); + assertEquals(invocation.dynamicInvoker().invoke(null, 0), -1); + assertFalse(enumInitialized.get()); + assertEquals(invocation.dynamicInvoker().invoke("test", 0), 1); + assertFalse(enumInitialized.get()); + E e = E.A; + assertTrue(enumInitialized.get()); + assertEquals(invocation.dynamicInvoker().invoke(e, 0), 0); + } + + public void testIncorrectEnumLabels() throws Throwable { + try { + testEnum(E1.B, 0, -1, "B", 1); + fail("Didn't get the expected exception."); + } catch (IllegalArgumentException ex) { + //OK + } + try { + testEnum(E1.B, 0, -1, "B", null); + fail("Didn't get the expected exception."); + } catch (IllegalArgumentException ex) { + //OK + } + } + + public void testIncorrectEnumStartIndex() throws Throwable { + try { + testEnum(E1.B, -1, -1, "B"); + fail("Didn't get the expected exception."); + } catch (IndexOutOfBoundsException ex) { + //OK + } + try { + testEnum(E1.B, 2, -1, "B"); + fail("Didn't get the expected exception."); + } catch (IndexOutOfBoundsException ex) { + //OK + } + } + + public void testIncorrectTypeStartIndex() throws Throwable { + try { + testType("", -1, -1, ""); + fail("Didn't get the expected exception."); + } catch (IndexOutOfBoundsException ex) { + //OK + } + try { + testType("", 2, -1, ""); + fail("Didn't get the expected exception."); + } catch (IndexOutOfBoundsException ex) { + //OK + } + } + } diff --git a/test/langtools/tools/javac/ConditionalExpressionResolvePending.java b/test/langtools/tools/javac/ConditionalExpressionResolvePending.java index 11e62a25308..e0fca330b39 100644 --- a/test/langtools/tools/javac/ConditionalExpressionResolvePending.java +++ b/test/langtools/tools/javac/ConditionalExpressionResolvePending.java @@ -99,10 +99,7 @@ public class ConditionalExpressionResolvePending extends ComboInstance False; case "SNIPPET" -> snippet; default -> throw new UnsupportedOperationException(pname); - }) - .withOption("--enable-preview") - .withOption("-source") - .withOption(String.valueOf(Runtime.version().feature())); + }); task.generate(result -> { try { diff --git a/test/langtools/tools/javac/T8286797.java b/test/langtools/tools/javac/T8286797.java index f0064516be9..c59d25eaece 100644 --- a/test/langtools/tools/javac/T8286797.java +++ b/test/langtools/tools/javac/T8286797.java @@ -25,7 +25,7 @@ * @test * @bug 8286797 * @summary Guards of constant value false are not permitted - * @compile/fail/ref=T8286797.out --enable-preview -source ${jdk.version} -XDrawDiagnostics -XDshould-stop.at=FLOW T8286797.java + * @compile/fail/ref=T8286797.out -XDrawDiagnostics -XDshould-stop.at=FLOW T8286797.java */ public class T8286797 { diff --git a/test/langtools/tools/javac/T8286797.out b/test/langtools/tools/javac/T8286797.out index 2587857c4e5..b067851b697 100644 --- a/test/langtools/tools/javac/T8286797.out +++ b/test/langtools/tools/javac/T8286797.out @@ -1,5 +1,3 @@ T8286797.java:35:32: compiler.err.guard.has.constant.expression.false T8286797.java:43:34: compiler.err.guard.has.constant.expression.false -- compiler.note.preview.filename: T8286797.java, DEFAULT -- compiler.note.preview.recompile 2 errors \ No newline at end of file diff --git a/test/langtools/tools/javac/T8295447.java b/test/langtools/tools/javac/T8295447.java index 76fcaf10f8d..990ce5deaad 100644 --- a/test/langtools/tools/javac/T8295447.java +++ b/test/langtools/tools/javac/T8295447.java @@ -25,7 +25,7 @@ * @bug 8295447 * @summary NullPointerException with invalid pattern matching construct in constructor call * @modules jdk.compiler - * @compile/fail/ref=T8295447.out -XDrawDiagnostics --enable-preview -source ${jdk.version} T8295447.java + * @compile/fail/ref=T8295447.out -XDrawDiagnostics T8295447.java */ public class T8295447 { class Foo { diff --git a/test/langtools/tools/javac/T8295447.out b/test/langtools/tools/javac/T8295447.out index 7f6746f802f..24b1be5a03e 100644 --- a/test/langtools/tools/javac/T8295447.out +++ b/test/langtools/tools/javac/T8295447.out @@ -1,6 +1,4 @@ T8295447.java:33:29: compiler.err.deconstruction.pattern.only.records: T8295447.Foo T8295447.java:37:29: compiler.err.deconstruction.pattern.only.records: T8295447.Foo T8295447.java:44:44: compiler.err.deconstruction.pattern.only.records: java.awt.Point -- compiler.note.preview.filename: T8295447.java, DEFAULT -- compiler.note.preview.recompile 3 errors \ No newline at end of file diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/Patterns.java b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/Patterns.java index b9b1e88d746..1589dbc7858 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/Patterns.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/Patterns.java @@ -28,7 +28,6 @@ * @modules java.compiler * jdk.jdeps/com.sun.tools.javap * @build toolbox.JavapTask - * @enablePreview * @run main Patterns */ diff --git a/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/RuleSwitchBreaks.java b/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/RuleSwitchBreaks.java index 700085226cf..35278522ddf 100644 --- a/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/RuleSwitchBreaks.java +++ b/test/langtools/tools/javac/classfiles/attributes/LineNumberTable/RuleSwitchBreaks.java @@ -81,7 +81,7 @@ public class RuleSwitchBreaks extends LineNumberTestBase { """, List.of(1, 3, 4, 5, 6, 7, 9, 11), true, - List.of("--enable-preview", "-source", System.getProperty("java.specification.version")), + List.of(), "TestGuards") }; diff --git a/test/langtools/tools/javac/diags/examples.not-yet.txt b/test/langtools/tools/javac/diags/examples.not-yet.txt index 6e78ca644bf..5ea7fdc1d60 100644 --- a/test/langtools/tools/javac/diags/examples.not-yet.txt +++ b/test/langtools/tools/javac/diags/examples.not-yet.txt @@ -141,6 +141,7 @@ compiler.warn.invalid.path # this warning is genera compiler.err.invalid.path # this error is generated only in Windows systems compiler.note.multiple.elements # needs user code compiler.err.preview.feature.disabled.classfile # preview feature support: needs compilation against classfile +compiler.warn.preview.feature.use # preview feature support: not generated currently compiler.warn.preview.feature.use.classfile # preview feature support: needs compilation against classfile compiler.note.preview.plural.additional # preview feature support: diag test causes intermittent failures (see JDK-8201498) compiler.misc.bad.intersection.target.for.functional.expr # currently not generated, should be removed? diff --git a/test/langtools/tools/javac/T8297602.java b/test/langtools/tools/javac/diags/examples/CannotAssignNotDeclaredGuard.java similarity index 59% rename from test/langtools/tools/javac/T8297602.java rename to test/langtools/tools/javac/diags/examples/CannotAssignNotDeclaredGuard.java index 5d7519d0318..05231c9faee 100644 --- a/test/langtools/tools/javac/T8297602.java +++ b/test/langtools/tools/javac/diags/examples/CannotAssignNotDeclaredGuard.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,32 +21,14 @@ * questions. */ -/* - * @test - * @bug 8297602 - * @summary Compiler crash with type annotation and generic record during pattern matching - * @enablePreview - * @compile --enable-preview -source ${jdk.version} -XDrawDiagnostics T8297602.java - */ -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; - -public class T8297602 -{ - void meth(Foo p) { - switch(p) { - case Foo<@Annot(field = "") Integer>(): {} - }; - - if (p instanceof Foo<@Annot(field = "") Integer>()) { +// key: compiler.err.cannot.assign.not.declared.guard +class CannotAssignNotDeclaredGuard { + void test(Object i) { + final boolean b; + switch (i) { + case Object o when b = true -> {} + default -> {} } } - - @Target({ElementType.TYPE_USE}) - @interface Annot { - String field(); - } - - record Foo() { } } diff --git a/test/langtools/tools/javac/diags/examples/CantRefNonEffectivelyFinalVar.java b/test/langtools/tools/javac/diags/examples/CantRefNonEffectivelyFinalVar.java index bf2affc418e..b63c0ac1194 100644 --- a/test/langtools/tools/javac/diags/examples/CantRefNonEffectivelyFinalVar.java +++ b/test/langtools/tools/javac/diags/examples/CantRefNonEffectivelyFinalVar.java @@ -25,9 +25,6 @@ // key: compiler.misc.inner.cls // key: compiler.misc.lambda // key: compiler.misc.guard -// key: compiler.misc.feature.pattern.switch -// key: compiler.warn.preview.feature.use.plural -// options: --enable-preview -source ${jdk.version} -Xlint:preview class CantRefNonEffectivelyFinalVar { void test() { diff --git a/test/langtools/tools/javac/diags/examples/CaseNull.java b/test/langtools/tools/javac/diags/examples/CaseNull.java index b49b299e53f..816c4c3e1fc 100644 --- a/test/langtools/tools/javac/diags/examples/CaseNull.java +++ b/test/langtools/tools/javac/diags/examples/CaseNull.java @@ -21,9 +21,9 @@ * questions. */ +// key: compiler.err.feature.not.supported.in.source // key: compiler.misc.feature.case.null -// key: compiler.warn.preview.feature.use -// options: --enable-preview -source ${jdk.version} -Xlint:preview +// options: -source 20 -Xlint:-options class CaseNull { private void doSwitch(String s) { diff --git a/test/langtools/tools/javac/diags/examples/ConstantLabelNotCompatible.java b/test/langtools/tools/javac/diags/examples/ConstantLabelNotCompatible.java index 1d3fbfea22c..928e2548647 100644 --- a/test/langtools/tools/javac/diags/examples/ConstantLabelNotCompatible.java +++ b/test/langtools/tools/javac/diags/examples/ConstantLabelNotCompatible.java @@ -22,9 +22,6 @@ */ // key: compiler.err.constant.label.not.compatible -// key: compiler.misc.feature.pattern.switch -// key: compiler.warn.preview.feature.use.plural -// options: --enable-preview -source ${jdk.version} -Xlint:preview class ConstantLabelNotCompatible { private void doSwitch(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/DeconstructionPatternOnlyRecords.java b/test/langtools/tools/javac/diags/examples/DeconstructionPatternOnlyRecords.java index 577c3b7c693..d0e6256b9ca 100644 --- a/test/langtools/tools/javac/diags/examples/DeconstructionPatternOnlyRecords.java +++ b/test/langtools/tools/javac/diags/examples/DeconstructionPatternOnlyRecords.java @@ -22,9 +22,6 @@ */ // key: compiler.err.deconstruction.pattern.only.records -// key: compiler.warn.preview.feature.use.plural -// key: compiler.misc.feature.deconstruction.patterns -// options: --enable-preview -source ${jdk.version} -Xlint:preview class DeconstructionpatternOnlyRecords { public boolean deconstruction(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/DefaultLabelNotAllowed.java b/test/langtools/tools/javac/diags/examples/DefaultLabelNotAllowed.java index 65d339fb33b..d17825bc735 100644 --- a/test/langtools/tools/javac/diags/examples/DefaultLabelNotAllowed.java +++ b/test/langtools/tools/javac/diags/examples/DefaultLabelNotAllowed.java @@ -22,9 +22,6 @@ */ // key: compiler.err.default.label.not.allowed -// key: compiler.misc.feature.pattern.switch -// key: compiler.warn.preview.feature.use.plural -// options: --enable-preview -source ${jdk.version} -Xlint:preview class DefaultLabelNotAllowed { private void doSwitch(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/DuplicateUnconditionalPattern.java b/test/langtools/tools/javac/diags/examples/DuplicateUnconditionalPattern.java index e822fdf0652..fa40ae92c75 100644 --- a/test/langtools/tools/javac/diags/examples/DuplicateUnconditionalPattern.java +++ b/test/langtools/tools/javac/diags/examples/DuplicateUnconditionalPattern.java @@ -22,9 +22,6 @@ */ // key: compiler.err.duplicate.unconditional.pattern -// key: compiler.misc.feature.pattern.switch -// key: compiler.warn.preview.feature.use.plural -// options: --enable-preview -source ${jdk.version} -Xlint:preview class DuplicateUnconditionalPattern { private void doSwitch(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/EnumLabelUnqualified.java b/test/langtools/tools/javac/diags/examples/EnumLabelUnqualified.java index 8d4aadaa05d..42b375f3b0f 100644 --- a/test/langtools/tools/javac/diags/examples/EnumLabelUnqualified.java +++ b/test/langtools/tools/javac/diags/examples/EnumLabelUnqualified.java @@ -22,6 +22,7 @@ */ // key: compiler.err.enum.label.must.be.unqualified.enum +// options: --release 20 class EnumLabelUnqualified { diff --git a/test/langtools/tools/javac/diags/examples/ForeachNotExhaustive.java b/test/langtools/tools/javac/diags/examples/FeatureDeconstructionPatterns.java similarity index 67% rename from test/langtools/tools/javac/diags/examples/ForeachNotExhaustive.java rename to test/langtools/tools/javac/diags/examples/FeatureDeconstructionPatterns.java index 97ce07467f8..0388ecf123b 100644 --- a/test/langtools/tools/javac/diags/examples/ForeachNotExhaustive.java +++ b/test/langtools/tools/javac/diags/examples/FeatureDeconstructionPatterns.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,20 +21,13 @@ * questions. */ +// key: compiler.err.feature.not.supported.in.source.plural // key: compiler.misc.feature.deconstruction.patterns -// key: compiler.misc.feature.pattern.switch -// key: compiler.warn.preview.feature.use.plural -// key: compiler.err.foreach.not.exhaustive.on.type -// options: --enable-preview -source ${jdk.version} -Xlint:preview +// options: -source 20 -Xlint:-options -import java.util.List; +class FeatureDeconstructionPatterns { + Object o; + boolean b = o instanceof R(String s); -class ForeachNotExhaustive { - void m(List points) { - for (Point(var x, var y): points) { - System.out.println(); - } - } - - record Point(Integer x, Integer y) { } + record R(Object o) {} } diff --git a/test/langtools/tools/javac/diags/examples/FeatureUnconditionalPatternsInInstanceof.java b/test/langtools/tools/javac/diags/examples/FeatureUnconditionalPatternsInInstanceof.java index b3cf3401105..02b4eb17c02 100644 --- a/test/langtools/tools/javac/diags/examples/FeatureUnconditionalPatternsInInstanceof.java +++ b/test/langtools/tools/javac/diags/examples/FeatureUnconditionalPatternsInInstanceof.java @@ -21,11 +21,9 @@ * questions. */ +// key: compiler.err.feature.not.supported.in.source.plural // key: compiler.misc.feature.unconditional.patterns.in.instanceof -// key: compiler.warn.preview.feature.use.plural -// options: --enable-preview -source ${jdk.version} -Xlint:-options,preview - -import java.util.*; +// options: -source 20 -Xlint:-options class FeatureUnconditionalTypesInstanceof { String s; diff --git a/test/langtools/tools/javac/diags/examples/FlowsThroughFromPattern.java b/test/langtools/tools/javac/diags/examples/FlowsThroughFromPattern.java index 22c35404dcd..29b43b05ee8 100644 --- a/test/langtools/tools/javac/diags/examples/FlowsThroughFromPattern.java +++ b/test/langtools/tools/javac/diags/examples/FlowsThroughFromPattern.java @@ -22,9 +22,6 @@ */ // key: compiler.err.flows.through.from.pattern -// key: compiler.misc.feature.pattern.switch -// key: compiler.warn.preview.feature.use.plural -// options: --enable-preview -source ${jdk.version} -Xlint:preview class FlowsThroughToPattern { private void doSwitch(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/FlowsThroughToPattern.java b/test/langtools/tools/javac/diags/examples/FlowsThroughToPattern.java index 6c212b23281..a49083708af 100644 --- a/test/langtools/tools/javac/diags/examples/FlowsThroughToPattern.java +++ b/test/langtools/tools/javac/diags/examples/FlowsThroughToPattern.java @@ -22,11 +22,6 @@ */ // key: compiler.err.flows.through.to.pattern -// key: compiler.misc.feature.pattern.switch -// key: compiler.warn.preview.feature.use.plural -// key: compiler.misc.feature.case.null -// key: compiler.warn.preview.feature.use -// options: --enable-preview -source ${jdk.version} -Xlint:preview class FlowsThroughToPattern { private void doSwitch(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/GuardHasConstantFalse.java b/test/langtools/tools/javac/diags/examples/GuardHasConstantFalse.java index 24b093ece10..69b3c7cb05b 100644 --- a/test/langtools/tools/javac/diags/examples/GuardHasConstantFalse.java +++ b/test/langtools/tools/javac/diags/examples/GuardHasConstantFalse.java @@ -22,9 +22,6 @@ */ // key: compiler.err.guard.has.constant.expression.false -// key: compiler.misc.feature.pattern.switch -// key: compiler.warn.preview.feature.use.plural -// options: --enable-preview -source ${jdk.version} -Xlint:preview class GuardHasConstantFalse { private void doSwitch(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/InstanceofPatternNoSubtype.java b/test/langtools/tools/javac/diags/examples/GuardNotAllowed.java similarity index 79% rename from test/langtools/tools/javac/diags/examples/InstanceofPatternNoSubtype.java rename to test/langtools/tools/javac/diags/examples/GuardNotAllowed.java index afd4cc92634..f70e38adede 100644 --- a/test/langtools/tools/javac/diags/examples/InstanceofPatternNoSubtype.java +++ b/test/langtools/tools/javac/diags/examples/GuardNotAllowed.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,10 +21,13 @@ * questions. */ -// key: compiler.err.instanceof.pattern.no.subtype +// key: compiler.err.guard.not.allowed -class InstanceofPatternNoSubtype { - boolean test(Object o) { - return o instanceof Object obj; +class GuardNotAllowed { + private void doSwitch(int i, boolean b) { + switch (i) { + case 0 when b -> {} + default -> {} + } } } diff --git a/test/langtools/tools/javac/diags/examples/IncorrectNumberOfNestedPatterns.java b/test/langtools/tools/javac/diags/examples/IncorrectNumberOfNestedPatterns.java index e448fd93703..987eb7e5faa 100644 --- a/test/langtools/tools/javac/diags/examples/IncorrectNumberOfNestedPatterns.java +++ b/test/langtools/tools/javac/diags/examples/IncorrectNumberOfNestedPatterns.java @@ -22,9 +22,6 @@ */ // key: compiler.err.incorrect.number.of.nested.patterns -// key: compiler.misc.feature.deconstruction.patterns -// key: compiler.warn.preview.feature.use.plural -// options: --enable-preview -source ${jdk.version} -Xlint:preview class IncorrectNumberOfNestedPatterns { private boolean t(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/InvalidCaseLabelCombination.java b/test/langtools/tools/javac/diags/examples/InvalidCaseLabelCombination.java index 505b73df595..094355f30c9 100644 --- a/test/langtools/tools/javac/diags/examples/InvalidCaseLabelCombination.java +++ b/test/langtools/tools/javac/diags/examples/InvalidCaseLabelCombination.java @@ -22,9 +22,6 @@ */ // key: compiler.err.invalid.case.label.combination -// key: compiler.misc.feature.case.null -// key: compiler.warn.preview.feature.use -// options: --enable-preview -source ${jdk.version} -Xlint:preview class InvalidCaseLabelCombination { private void doSwitch(Integer i) { diff --git a/test/langtools/tools/javac/diags/examples/NotApplicableTypes.java b/test/langtools/tools/javac/diags/examples/NotApplicableTypes.java index 14ec357df5d..6146d2b6d9e 100644 --- a/test/langtools/tools/javac/diags/examples/NotApplicableTypes.java +++ b/test/langtools/tools/javac/diags/examples/NotApplicableTypes.java @@ -23,9 +23,6 @@ // key: compiler.misc.not.applicable.types // key: compiler.err.prob.found.req -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview --source ${jdk.version} class NotApplicableTypes { void t(int i) { diff --git a/test/langtools/tools/javac/diags/examples/NotExhaustiveStatement.java b/test/langtools/tools/javac/diags/examples/NotExhaustiveStatement.java index 0d151dbe6a8..65a11abb0e6 100644 --- a/test/langtools/tools/javac/diags/examples/NotExhaustiveStatement.java +++ b/test/langtools/tools/javac/diags/examples/NotExhaustiveStatement.java @@ -22,9 +22,6 @@ */ // key: compiler.err.not.exhaustive.statement -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview --source ${jdk.version} class NotExhaustive { void t(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/PatternDominated.java b/test/langtools/tools/javac/diags/examples/PatternDominated.java index f1e813da1df..2cddee047ac 100644 --- a/test/langtools/tools/javac/diags/examples/PatternDominated.java +++ b/test/langtools/tools/javac/diags/examples/PatternDominated.java @@ -22,9 +22,6 @@ */ // key: compiler.err.pattern.dominated -// key: compiler.misc.feature.pattern.switch -// key: compiler.warn.preview.feature.use.plural -// options: --enable-preview -source ${jdk.version} -Xlint:preview class PatternDominated { private void doSwitch(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/PatternExpected.java b/test/langtools/tools/javac/diags/examples/PatternExpected.java index b13d593fb35..7d3c62073a5 100644 --- a/test/langtools/tools/javac/diags/examples/PatternExpected.java +++ b/test/langtools/tools/javac/diags/examples/PatternExpected.java @@ -22,9 +22,6 @@ */ // key: compiler.err.pattern.expected -// key: compiler.note.preview.filename -// key: compiler.note.preview.recompile -// options: --enable-preview -source ${jdk.version} class PatternSwitch { private void doSwitch(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/PatternSwitch.java b/test/langtools/tools/javac/diags/examples/PatternSwitch.java index a563ad3d749..5b54f6c3c36 100644 --- a/test/langtools/tools/javac/diags/examples/PatternSwitch.java +++ b/test/langtools/tools/javac/diags/examples/PatternSwitch.java @@ -21,9 +21,9 @@ * questions. */ +// key: compiler.err.feature.not.supported.in.source.plural // key: compiler.misc.feature.pattern.switch -// key: compiler.warn.preview.feature.use.plural -// options: --enable-preview -source ${jdk.version} -Xlint:preview +// options: -source 20 -Xlint:-options class PatternSwitch { private void doSwitch(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/PatternTypeCannotInfer.java b/test/langtools/tools/javac/diags/examples/PatternTypeCannotInfer.java index 3d500a45431..e2f841882f6 100644 --- a/test/langtools/tools/javac/diags/examples/PatternTypeCannotInfer.java +++ b/test/langtools/tools/javac/diags/examples/PatternTypeCannotInfer.java @@ -22,9 +22,6 @@ */ // key: compiler.err.pattern.type.cannot.infer -// key: compiler.warn.preview.feature.use.plural -// key: compiler.misc.feature.deconstruction.patterns -// options: --enable-preview -source ${jdk.version} -Xlint:preview class PatternTypeCannotInfer { interface A {} diff --git a/test/langtools/tools/javac/diags/examples/RecordPatternsAnnotationsNotAllowed.java b/test/langtools/tools/javac/diags/examples/RecordPatternsAnnotationsNotAllowed.java new file mode 100644 index 00000000000..5f920a60ad4 --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/RecordPatternsAnnotationsNotAllowed.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.err.record.patterns.annotations.not.allowed + +class RecordPatternsAnnotationsNotAllowed { + + public boolean test(Object o) { + return o instanceof @Deprecated R(String s); + } + + record R(String s) {} +} diff --git a/test/langtools/tools/javac/diags/examples/UnconditionalPatternAndDefault.java b/test/langtools/tools/javac/diags/examples/UnconditionalPatternAndDefault.java index 1d5528d73be..20de3c1ed7d 100644 --- a/test/langtools/tools/javac/diags/examples/UnconditionalPatternAndDefault.java +++ b/test/langtools/tools/javac/diags/examples/UnconditionalPatternAndDefault.java @@ -22,9 +22,6 @@ */ // key: compiler.err.unconditional.pattern.and.default -// key: compiler.misc.feature.pattern.switch -// key: compiler.warn.preview.feature.use.plural -// options: --enable-preview -source ${jdk.version} -Xlint:preview class UnconditionalPatternAndDefault { private void doSwitch(Object o) { diff --git a/test/langtools/tools/javac/enum/EnumSwitch2.java b/test/langtools/tools/javac/enum/EnumSwitch2.java index aae2c55cb02..85a78852726 100644 --- a/test/langtools/tools/javac/enum/EnumSwitch2.java +++ b/test/langtools/tools/javac/enum/EnumSwitch2.java @@ -3,7 +3,7 @@ * @bug 4936393 8050021 * @summary enum switch case labels required to be unqualified. * @author gafter - * @compile/fail/ref=EnumSwitch2.out -XDrawDiagnostics EnumSwitch2.java + * @compile/fail/ref=EnumSwitch2.out -XDrawDiagnostics --release 20 EnumSwitch2.java */ enum E1 { a, b, c, d, e } diff --git a/test/langtools/tools/javac/lib/DPrinter.java b/test/langtools/tools/javac/lib/DPrinter.java index 6a64195b39e..19a70dff87a 100644 --- a/test/langtools/tools/javac/lib/DPrinter.java +++ b/test/langtools/tools/javac/lib/DPrinter.java @@ -722,7 +722,7 @@ public class DPrinter { @Override public void visitForeachLoop(JCEnhancedForLoop tree) { - printTree("var", tree.varOrRecordPattern); + printTree("var", tree.var); printTree("expr", tree.expr); printTree("body", tree.body); } diff --git a/test/langtools/tools/javac/parser/JavacParserTest.java b/test/langtools/tools/javac/parser/JavacParserTest.java index 6b4a95b4c2a..0b5f67e089f 100644 --- a/test/langtools/tools/javac/parser/JavacParserTest.java +++ b/test/langtools/tools/javac/parser/JavacParserTest.java @@ -2342,6 +2342,59 @@ public class JavacParserTest extends TestCase { } } + @Test + void testGuardRecovery() throws IOException { + String code = """ + package t; + class Test { + private int t(Integer i, boolean b) { + switch (i) { + case 0 when b -> {} + case null when b -> {} + default when b -> {} + } + return switch (i) { + case 0 when b -> 0; + case null when b -> 0; + default when b -> 0; + }; + } + }"""; + DiagnosticCollector coll = + new DiagnosticCollector<>(); + JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll, null, + null, Arrays.asList(new MyFileObject(code))); + CompilationUnitTree cut = ct.parse().iterator().next(); + new TreeScanner() { + @Override + public Void visitCase(CaseTree node, Void p) { + assertNotNull(node.getGuard()); + assertEquals("guard kind", Kind.ERRONEOUS, node.getGuard().getKind()); + assertEquals("guard content", + List.of("b"), + ((ErroneousTree) node.getGuard()).getErrorTrees() + .stream() + .map(t -> t.toString()).toList()); + return super.visitCase(node, p); + } + }.scan(cut, null); + + List codes = new LinkedList<>(); + + for (Diagnostic d : coll.getDiagnostics()) { + codes.add(d.getLineNumber() + ":" + d.getColumnNumber() + ":" + d.getCode()); + } + + assertEquals("testUsupportedTextBlock: " + codes, + List.of("5:20:compiler.err.guard.not.allowed", + "6:23:compiler.err.guard.not.allowed", + "7:21:compiler.err.guard.not.allowed", + "10:20:compiler.err.guard.not.allowed", + "11:23:compiler.err.guard.not.allowed", + "12:21:compiler.err.guard.not.allowed"), + codes); + } + void run(String[] args) throws Exception { int passed = 0, failed = 0; final Pattern p = (args != null && args.length > 0) diff --git a/test/langtools/tools/javac/patterns/AnnotationErrors.java b/test/langtools/tools/javac/patterns/AnnotationErrors.java new file mode 100644 index 00000000000..8e5130e3708 --- /dev/null +++ b/test/langtools/tools/javac/patterns/AnnotationErrors.java @@ -0,0 +1,42 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8300543 + * @summary Verify error related to annotations and patterns + * @compile/fail/ref=AnnotationErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW AnnotationErrors.java + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +public class AnnotationErrors { + + private void test(Object o, G g) { + boolean b1 = o instanceof @DA R(var s); + boolean b2 = o instanceof @DTA R(var s); + boolean b3 = o instanceof @TA R(var s); + boolean b5 = g instanceof G<@DTA String>(var s); + boolean b6 = g instanceof G<@TA String>(var s); + switch (o) { + case @DA R(var s) when b1 -> {} + case @DTA R(var s) when b1 -> {} + case @TA R(var s) when b1 -> {} + default -> {} + } + switch (g) { + case G<@DTA String>(var s) when b1 -> {} + case G<@TA String>(var s) when b1 -> {} + default -> {} + } + } + + record R(String s) {} + record G(T t) {} + + @Target(ElementType.LOCAL_VARIABLE) + @interface DA {} + @Target({ElementType.TYPE_USE, ElementType.LOCAL_VARIABLE}) + @interface DTA {} + @Target(ElementType.TYPE_USE) + @interface TA {} +} + diff --git a/test/langtools/tools/javac/patterns/AnnotationErrors.out b/test/langtools/tools/javac/patterns/AnnotationErrors.out new file mode 100644 index 00000000000..d7c1959adfa --- /dev/null +++ b/test/langtools/tools/javac/patterns/AnnotationErrors.out @@ -0,0 +1,11 @@ +AnnotationErrors.java:14:35: compiler.err.record.patterns.annotations.not.allowed +AnnotationErrors.java:15:35: compiler.err.record.patterns.annotations.not.allowed +AnnotationErrors.java:16:35: compiler.err.record.patterns.annotations.not.allowed +AnnotationErrors.java:17:37: compiler.err.record.patterns.annotations.not.allowed +AnnotationErrors.java:18:37: compiler.err.record.patterns.annotations.not.allowed +AnnotationErrors.java:20:18: compiler.err.record.patterns.annotations.not.allowed +AnnotationErrors.java:21:18: compiler.err.record.patterns.annotations.not.allowed +AnnotationErrors.java:22:18: compiler.err.record.patterns.annotations.not.allowed +AnnotationErrors.java:26:20: compiler.err.record.patterns.annotations.not.allowed +AnnotationErrors.java:27:20: compiler.err.record.patterns.annotations.not.allowed +10 errors diff --git a/test/langtools/tools/javac/patterns/CaseStructureTest.java b/test/langtools/tools/javac/patterns/CaseStructureTest.java index 3d0e1e1d217..3ffed763fd5 100644 --- a/test/langtools/tools/javac/patterns/CaseStructureTest.java +++ b/test/langtools/tools/javac/patterns/CaseStructureTest.java @@ -85,16 +85,14 @@ public class CaseStructureTest extends ComboInstance { protected void doWork() throws Throwable { String labelSeparator = asCaseLabelElements ? ", " : ": case "; String labels = Arrays.stream(caseLabels).filter(l -> l != CaseLabel.NONE).map(l -> l.code).collect(Collectors.joining(labelSeparator, "case ", ": break;")); - boolean hasDefault = Arrays.stream(caseLabels).anyMatch(l -> l == CaseLabel.DEFAULT || l == CaseLabel.TYPE_PATTERN || l == CaseLabel.PARENTHESIZED_PATTERN); + boolean hasDefault = Arrays.stream(caseLabels).anyMatch(l -> l == CaseLabel.DEFAULT || l == CaseLabel.TYPE_PATTERN); ComboTask task = newCompilationTask() - .withSourceFromTemplate(MAIN_TEMPLATE.replace("#{CASES}", labels).replace("#{DEFAULT}", hasDefault ? "" : "default: break;")) - .withOption("--enable-preview") - .withOption("-source").withOption(JAVA_VERSION); + .withSourceFromTemplate(MAIN_TEMPLATE.replace("#{CASES}", labels).replace("#{DEFAULT}", hasDefault ? "" : "default: break;")); task.generate(result -> { boolean shouldPass = true; - long patternCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.TYPE_PATTERN || l == CaseLabel.PARENTHESIZED_PATTERN).count(); + long patternCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.TYPE_PATTERN).count(); long constantCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.CONSTANT).count(); long nullCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.NULL).count(); long defaultCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.DEFAULT).count(); @@ -136,7 +134,6 @@ public class CaseStructureTest extends ComboInstance { public enum CaseLabel implements ComboParameter { NONE(""), TYPE_PATTERN("Integer i"), - PARENTHESIZED_PATTERN("(Integer i)"), CONSTANT("1"), NULL("null"), DEFAULT("default"); diff --git a/test/langtools/tools/javac/patterns/DeconstructionDesugaring.java b/test/langtools/tools/javac/patterns/DeconstructionDesugaring.java index d76da043125..e3f9e7d207e 100644 --- a/test/langtools/tools/javac/patterns/DeconstructionDesugaring.java +++ b/test/langtools/tools/javac/patterns/DeconstructionDesugaring.java @@ -25,8 +25,8 @@ * @test * @bug 8291769 8301858 8304694 8304883 * @summary Verify more complex switches work properly - * @compile --enable-preview -source ${jdk.version} DeconstructionDesugaring.java - * @run main/othervm --enable-preview DeconstructionDesugaring + * @compile DeconstructionDesugaring.java + * @run main DeconstructionDesugaring */ import java.util.Objects; @@ -79,7 +79,7 @@ public class DeconstructionDesugaring { private int runCheckStatement(Object o) { switch (o) { - case (((R1((((R2((((String s))))))))))) -> { return 1; } + case R1(R2(String s)) -> { return 1; } case R1(R2(Integer i)) -> { return 2; } case R1(R2(Double d)) -> { return 3; } case R1(R2(CharSequence cs)) -> { return 4; } @@ -93,7 +93,7 @@ public class DeconstructionDesugaring { private int runCheckExpression(Object o) { return switch (o) { - case (((R1((((R2((((String s))))))))))) -> 1; + case R1(R2(String s)) -> 1; case R1(R2(Integer i)) -> 2; case R1(R2(Double d)) -> 3; case R1(R2(CharSequence cs)) -> 4; diff --git a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java index 2a486c8042d..3485e725791 100644 --- a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java +++ b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java @@ -1,9 +1,9 @@ /** * @test /nodynamiccopyright/ * @summary Verify error reports for erroneous deconstruction patterns are sensible - * @enablePreview * @compile/fail/ref=DeconstructionPatternErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW -XDdev DeconstructionPatternErrors.java */ + import java.util.ArrayList; import java.util.List; diff --git a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out index 8fba5a32214..67ce9c1ed5b 100644 --- a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out +++ b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out @@ -26,7 +26,4 @@ DeconstructionPatternErrors.java:31:29: compiler.err.prob.found.req: (compiler.m DeconstructionPatternErrors.java:32:29: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long) DeconstructionPatternErrors.java:34:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, byte) DeconstructionPatternErrors.java:35:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long) -DeconstructionPatternErrors.java:44:9: compiler.err.not.exhaustive.statement -- compiler.note.preview.filename: DeconstructionPatternErrors.java, DEFAULT -- compiler.note.preview.recompile -29 errors +28 errors diff --git a/test/langtools/tools/javac/patterns/DisambiguatePatterns.java b/test/langtools/tools/javac/patterns/DisambiguatePatterns.java index 7518f3cc2e9..ef4d065f912 100644 --- a/test/langtools/tools/javac/patterns/DisambiguatePatterns.java +++ b/test/langtools/tools/javac/patterns/DisambiguatePatterns.java @@ -28,37 +28,22 @@ * jdk.compiler/com.sun.tools.javac.parser * jdk.compiler/com.sun.tools.javac.tree * jdk.compiler/com.sun.tools.javac.util - * @enablePreview */ import com.sun.source.tree.CaseLabelTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.ConstantCaseLabelTree; -import com.sun.source.tree.EnhancedForLoopTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.PatternCaseLabelTree; -import com.sun.source.tree.PatternTree; -import com.sun.source.tree.StatementTree; import com.sun.source.tree.SwitchTree; -import com.sun.source.tree.Tree.Kind; import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.parser.JavacParser; import com.sun.tools.javac.parser.ParserFactory; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.main.Option; -import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Options; -import java.net.URI; -import java.net.URISyntaxException; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import javax.tools.Diagnostic; -import javax.tools.DiagnosticListener; -import javax.tools.JavaFileObject; -import javax.tools.SimpleJavaFileObject; public class DisambiguatePatterns { @@ -68,11 +53,11 @@ public class DisambiguatePatterns { ExpressionType.PATTERN); test.disambiguationTest("String s when s.isEmpty()", ExpressionType.PATTERN); - test.disambiguationTest("(String s)", + test.disambiguationTest("String s", ExpressionType.PATTERN); - test.disambiguationTest("(@Ann String s)", + test.disambiguationTest("@Ann String s", ExpressionType.PATTERN); - test.disambiguationTest("((String s))", + test.disambiguationTest("String s", ExpressionType.PATTERN); test.disambiguationTest("(String) s", ExpressionType.EXPRESSION); @@ -92,7 +77,7 @@ public class DisambiguatePatterns { ExpressionType.EXPRESSION); test.disambiguationTest("(a << b || a < b | a >>> b)", ExpressionType.EXPRESSION); - test.disambiguationTest("(a < c.d > b)", + test.disambiguationTest("a < c.d > b", ExpressionType.PATTERN); test.disambiguationTest("a b", ExpressionType.PATTERN); @@ -124,44 +109,14 @@ public class DisambiguatePatterns { ExpressionType.EXPRESSION); test.disambiguationTest("a & b", ExpressionType.EXPRESSION); - test.forDisambiguationTest("T[] a", ForType.ENHANCED_FOR); - test.forDisambiguationTest("T[].class.getName()", ForType.TRADITIONAL_FOR); - test.forDisambiguationTest("T[].class", ForType.TRADITIONAL_FOR, "compiler.err.not.stmt"); - test.forDisambiguationTest("R(T[] a)", ForType.ENHANCED_FOR_WITH_PATTERNS); - - test.forDisambiguationTest("Point(Integer a, Integer b)", ForType.ENHANCED_FOR_WITH_PATTERNS); - test.forDisambiguationTest("ForEachPatterns.Point(Integer a, Integer b)", ForType.ENHANCED_FOR_WITH_PATTERNS); - test.forDisambiguationTest("GPoint(Integer a, Integer b)", ForType.ENHANCED_FOR_WITH_PATTERNS); - test.forDisambiguationTest("@Annot(field = \"test\") Point p", ForType.ENHANCED_FOR); - test.forDisambiguationTest("GPoint(Point(Integer a, Integer b), Point c)", ForType.ENHANCED_FOR_WITH_PATTERNS); - test.forDisambiguationTest("GPoint(Point(var a, Integer b), Point c)", ForType.ENHANCED_FOR_WITH_PATTERNS); - test.forDisambiguationTest("GPoint(VoidPoint(), VoidPoint())", ForType.ENHANCED_FOR_WITH_PATTERNS); - test.forDisambiguationTest("RecordOfLists(List lr)", ForType.ENHANCED_FOR_WITH_PATTERNS); - test.forDisambiguationTest("RecordOfLists2(List> lr)", ForType.ENHANCED_FOR_WITH_PATTERNS); - test.forDisambiguationTest("GPoint<@Annot(field = \"\") ? extends Point>(var x, var y)", ForType.ENHANCED_FOR_WITH_PATTERNS); - - test.forDisambiguationTest("method()", ForType.TRADITIONAL_FOR); - test.forDisambiguationTest("method(), method()", ForType.TRADITIONAL_FOR); - test.forDisambiguationTest("method2((Integer a) -> 42)", ForType.TRADITIONAL_FOR); - test.forDisambiguationTest("m(cond ? b() : i)", ForType.TRADITIONAL_FOR); - test.forDisambiguationTest("m((GPoint)null, cond ? b() : i)", ForType.TRADITIONAL_FOR); } private final ParserFactory factory; - private final List errors = new ArrayList<>(); - public DisambiguatePatterns() throws URISyntaxException { + public DisambiguatePatterns() { Context context = new Context(); - context.put(DiagnosticListener.class, d -> { - if (d.getKind() == Diagnostic.Kind.ERROR) { - errors.add(d.getCode()); - } - }); JavacFileManager jfm = new JavacFileManager(context, true, Charset.defaultCharset()); Options.instance(context).put(Option.PREVIEW, ""); - SimpleJavaFileObject source = - new SimpleJavaFileObject(new URI("mem://Test.java"), JavaFileObject.Kind.SOURCE) {}; - Log.instance(context).useSource(source); factory = ParserFactory.instance(context); } @@ -192,66 +147,9 @@ public class DisambiguatePatterns { } } - void forDisambiguationTest(String snippet, ForType forType, String... expectedErrors) { - errors.clear(); - - String codeTemplate = switch (forType) { - case TRADITIONAL_FOR -> - """ - public class Test { - private void test() { - for (SNIPPET; ;) { - } - } - } - """; - case ENHANCED_FOR, ENHANCED_FOR_WITH_PATTERNS -> - """ - public class Test { - private void test() { - for (SNIPPET : collection) { - } - } - } - """; - }; - - String code = codeTemplate.replace("SNIPPET", snippet); - JavacParser parser = factory.newParser(code, false, false, false); - CompilationUnitTree result = parser.parseCompilationUnit(); - if (!Arrays.asList(expectedErrors).equals(errors)) { - throw new AssertionError("Expected errors: " + Arrays.asList(expectedErrors) + - ", actual: " + errors + - ", for: " + code); - } - ClassTree clazz = (ClassTree) result.getTypeDecls().get(0); - MethodTree method = (MethodTree) clazz.getMembers().get(0); - StatementTree st = method.getBody().getStatements().get(0); - if (forType == ForType.TRADITIONAL_FOR) { - if (st.getKind() != Kind.FOR_LOOP) { - throw new AssertionError("Unpected statement: " + st); - } - } else { - EnhancedForLoopTree ef = (EnhancedForLoopTree) st; - ForType actualType = switch (ef.getVariableOrRecordPattern()) { - case PatternTree pattern -> ForType.ENHANCED_FOR_WITH_PATTERNS; - default -> ForType.ENHANCED_FOR; - }; - if (forType != actualType) { - throw new AssertionError("Expected: " + forType + ", actual: " + actualType + - ", for: " + code + ", parsed: " + result); - } - } - } - enum ExpressionType { PATTERN, EXPRESSION; } - enum ForType { - TRADITIONAL_FOR, - ENHANCED_FOR, - ENHANCED_FOR_WITH_PATTERNS; - } } diff --git a/test/langtools/tools/javac/patterns/Domination.java b/test/langtools/tools/javac/patterns/Domination.java index 4fbb1117d95..a5cdb99b9c8 100644 --- a/test/langtools/tools/javac/patterns/Domination.java +++ b/test/langtools/tools/javac/patterns/Domination.java @@ -25,9 +25,9 @@ * @test * @bug 8262891 8290709 * @summary Check the pattern domination error are reported correctly. - * @enablePreview * @compile/fail/ref=Domination.out -XDrawDiagnostics Domination.java */ + public class Domination { int testDominatesError1(Object o) { switch (o) { @@ -180,8 +180,8 @@ public class Domination { record R(int a) {} Object o = null; switch (o) { - case ((R r)): return 1; - case ((R(int a))): return -1; + case R r: return 1; + case R(int a): return -1; } } @@ -189,8 +189,8 @@ public class Domination { record R(int a) {} Object o = null; switch (o) { - case ((R(int a))): return 1; - case ((R(int a))): return -1; + case R(int a): return 1; + case R(int a): return -1; } } diff --git a/test/langtools/tools/javac/patterns/Domination.out b/test/langtools/tools/javac/patterns/Domination.out index 69a4dcad00f..f4d01085db2 100644 --- a/test/langtools/tools/javac/patterns/Domination.out +++ b/test/langtools/tools/javac/patterns/Domination.out @@ -16,6 +16,4 @@ Domination.java:184:18: compiler.err.pattern.dominated Domination.java:193:18: compiler.err.pattern.dominated Domination.java:202:18: compiler.err.pattern.dominated Domination.java:211:18: compiler.err.pattern.dominated -- compiler.note.preview.filename: Domination.java, DEFAULT -- compiler.note.preview.recompile 18 errors \ No newline at end of file diff --git a/test/langtools/tools/javac/patterns/EmptyRecordClass.java b/test/langtools/tools/javac/patterns/EmptyRecordClass.java index 6e79ad67efb..63dbc769f6e 100644 --- a/test/langtools/tools/javac/patterns/EmptyRecordClass.java +++ b/test/langtools/tools/javac/patterns/EmptyRecordClass.java @@ -23,7 +23,6 @@ /** * @test - * @enablePreview * @compile EmptyRecordClass.java */ diff --git a/test/langtools/tools/javac/patterns/EnumTypeChanges.java b/test/langtools/tools/javac/patterns/EnumTypeChanges.java index fba4fd942f3..f25b8774db8 100644 --- a/test/langtools/tools/javac/patterns/EnumTypeChanges.java +++ b/test/langtools/tools/javac/patterns/EnumTypeChanges.java @@ -25,7 +25,6 @@ * @test * @bug 8262891 * @summary Verify pattern switches work properly when the set of enum constant changes. - * @enablePreview * @compile EnumTypeChanges.java * @compile EnumTypeChanges2.java * @run main EnumTypeChanges diff --git a/test/langtools/tools/javac/patterns/EnumTypeChangesNonPreview.java b/test/langtools/tools/javac/patterns/EnumTypeChangesNonPreview.java index 163dda1f0cc..d15643b05b0 100644 --- a/test/langtools/tools/javac/patterns/EnumTypeChangesNonPreview.java +++ b/test/langtools/tools/javac/patterns/EnumTypeChangesNonPreview.java @@ -25,10 +25,9 @@ * @test * @bug 8297118 * @summary Verify pattern switches work properly when the set of enum constant changes. - * @compile EnumTypeChangesNonPreview.java + * @compile --release 20 EnumTypeChangesNonPreview.java * @compile EnumTypeChanges2.java * @run main EnumTypeChangesNonPreview - * @run main/othervm --enable-preview EnumTypeChangesNonPreview */ import java.util.function.Function; diff --git a/test/langtools/tools/javac/patterns/Exhaustiveness.java b/test/langtools/tools/javac/patterns/Exhaustiveness.java index 95c24b0a6e3..dca5864e1a9 100644 --- a/test/langtools/tools/javac/patterns/Exhaustiveness.java +++ b/test/langtools/tools/javac/patterns/Exhaustiveness.java @@ -37,17 +37,20 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; -import toolbox.TestRunner; import toolbox.JavacTask; import toolbox.Task; +import toolbox.TestRunner; import toolbox.ToolBox; public class Exhaustiveness extends TestRunner { - private static final String JAVA_VERSION = System.getProperty("java.specification.version"); - ToolBox tb; public static void main(String... args) throws Exception { @@ -119,8 +122,6 @@ public class Exhaustiveness extends TestRunner { } """, "Test.java:5:16: compiler.err.not.exhaustive", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"); } @@ -181,8 +182,6 @@ public class Exhaustiveness extends TestRunner { } """, "Test.java:5:16: compiler.err.not.exhaustive", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"); } @@ -214,8 +213,6 @@ public class Exhaustiveness extends TestRunner { } """, "Test.java:5:16: compiler.err.not.exhaustive", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"); } @@ -278,8 +275,6 @@ public class Exhaustiveness extends TestRunner { """, "Test.java:6:27: compiler.err.guard.has.constant.expression.false", "Test.java:5:16: compiler.err.not.exhaustive", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "2 errors"); } @@ -407,42 +402,11 @@ public class Exhaustiveness extends TestRunner { } """, "Test.java:4:9: compiler.err.not.exhaustive.statement", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"); } @Test - public void testExhaustiveStatement3(Path base) throws Exception { - doTest(base, - new String[]{""" - package lib; - public sealed interface S permits A, B {} - """, - """ - package lib; - public final class A implements S {} - """, - """ - package lib; - public final class B implements S {} - """}, - """ - package test; - import lib.*; - public class Test { - private int test(S obj) { - return switch (obj) { - case A a -> 0; - case S s -> 1; - }; - } - } - """); - } - - @Test - public void testExhaustiveStatement4(Path base) throws Exception { + public void testExhaustiveExpression1(Path base) throws Exception { doTest(base, new String[]{""" package lib; @@ -468,13 +432,11 @@ public class Exhaustiveness extends TestRunner { } """, "Test.java:5:16: compiler.err.not.exhaustive", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"); } @Test - public void testExhaustiveStatement5(Path base) throws Exception { + public void testExhaustiveExpression2(Path base) throws Exception { doTest(base, new String[]{""" package lib; @@ -578,110 +540,76 @@ public class Exhaustiveness extends TestRunner { } """, "Test.java:5:16: compiler.err.not.exhaustive", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"); } @Test - public void testExhaustiveIntersection(Path base) throws Exception { - doTest(base, - new String[]{""" - package lib; - public sealed interface S permits A, B {} - """, - """ - package lib; - public abstract class Base {} - """, - """ - package lib; - public interface Marker {} - """, - """ - package lib; - public final class A extends Base implements S, Marker {} - """, - """ - package lib; - public abstract sealed class B extends Base implements S permits C, D {} - """, - """ - package lib; - public final class C extends B implements Marker {} - """, - """ - package lib; - public final class D extends B implements Marker {} - """}, - """ - package test; - import lib.*; - public class Test { - private int test(T obj, boolean b) { - return switch (obj) { - case A a -> 0; - case C c when b -> 0; - case C c -> 0; - case D d -> 0; - }; + public void testIntersection(Path base) throws Exception { + record TestCase(String snippet, String... expected){} + TestCase[] testCases = new TestCase[] { + new TestCase(""" + return switch (obj) { + case A a -> 0; + case C c when b -> 0; + case C c -> 0; + case D d -> 0; + }; + """), + new TestCase(""" + return switch (obj) { + case A a -> 0; + case C c -> 0; + case D d when b -> 0; + }; + """, + "Test.java:5:16: compiler.err.not.exhaustive", + "1 error") + }; + for (TestCase tc : testCases) { + doTest(base, + new String[]{""" + package lib; + public sealed interface S permits A, B {} + """, + """ + package lib; + public abstract class Base {} + """, + """ + package lib; + public interface Marker {} + """, + """ + package lib; + public final class A extends Base implements S, Marker {} + """, + """ + package lib; + public abstract sealed class B extends Base implements S permits C, D {} + """, + """ + package lib; + public final class C extends B implements Marker {} + """, + """ + package lib; + public final class D extends B implements Marker {} + """}, + """ + package test; + import lib.*; + public class Test { + private int test(T obj, boolean b) { + ${tc.snippet()} + } } - } - """); + """.replace("${tc.snippet()}", tc.snippet()), + tc.expected()); + } } @Test - public void testNotExhaustiveIntersection(Path base) throws Exception { - doTest(base, - new String[]{""" - package lib; - public sealed interface S permits A, B {} - """, - """ - package lib; - public abstract class Base {} - """, - """ - package lib; - public interface Marker {} - """, - """ - package lib; - public final class A extends Base implements S, Marker {} - """, - """ - package lib; - public abstract sealed class B extends Base implements S permits C, D {} - """, - """ - package lib; - public final class C extends B implements Marker {} - """, - """ - package lib; - public final class D extends B implements Marker {} - """}, - """ - package test; - import lib.*; - public class Test { - private int test(T obj, boolean b) { - return switch (obj) { - case A a -> 0; - case C c -> 0; - case D d when b -> 0; - }; - } - } - """, - "Test.java:5:16: compiler.err.not.exhaustive", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", - "1 error"); - } - - @Test - public void testX(Path base) throws Exception { + public void testRecordPatterns(Path base) throws Exception { doTest(base, new String[]{""" package lib; @@ -745,8 +673,6 @@ public class Exhaustiveness extends TestRunner { } """, "Test.java:5:16: compiler.err.not.exhaustive", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"); doTest(base, new String[]{""" @@ -779,7 +705,188 @@ public class Exhaustiveness extends TestRunner { } } """); + doTest(base, + new String[]{""" + package lib; + public sealed interface S permits A, B {} + """, + """ + package lib; + public final class A implements S {} + """, + """ + package lib; + public record B(Object o) implements S {} + """, + """ + package lib; + public record R(S a, S b) {} + """}, + """ + package test; + import lib.*; + public class Test { + private int test(R r) { + return switch (r) { + case R(A a, A b) -> 0; + case R(A a, B b) -> 0; + case R(B a, A b) -> 0; + case R(B(String s), B b) -> 0; + }; + } + } + """, + "Test.java:5:16: compiler.err.not.exhaustive", + "1 error"); + doTest(base, + new String[]{""" + package lib; + public sealed interface S permits A, B {} + """, + """ + package lib; + public final class A implements S {} + """, + """ + package lib; + public record B(Object o) implements S {} + """, + """ + package lib; + public record R(S a, S b) {} + """}, + """ + package test; + import lib.*; + public class Test { + private int test(R r) { + return switch (r) { + case R(A a, A b) -> 0; + case R(A a, B b) -> 0; + case R(B a, A b) -> 0; + case R(B(Object o), B b) -> 0; + }; + } + } + """); + doTest(base, + new String[]{""" + package lib; + public sealed interface S permits A, B {} + """, + """ + package lib; + public final class A implements S {} + """, + """ + package lib; + public record B(Object o) implements S {} + """, + """ + package lib; + public record R(S a, S b) {} + """}, + """ + package test; + import lib.*; + public class Test { + private int test(R r) { + return switch (r) { + case R(A a, A b) -> 0; + case R(A a, B b) -> 0; + case R(B(String s), B b) -> 0; + case R(B a, A b) -> 0; + }; + } + } + """, + "Test.java:5:16: compiler.err.not.exhaustive", + "1 error"); + doTest(base, + new String[]{""" + package lib; + public record R(Object o1, Object o2) {} + """}, + """ + package test; + import lib.*; + public class Test { + private int test(R r) { + return switch (r) { + case R(String s, Object o) -> 0; + }; + } + } + """, + "Test.java:5:16: compiler.err.not.exhaustive", + "1 error"); + doTest(base, + new String[]{""" + package lib; + public sealed interface S permits A, B {} + """, + """ + package lib; + public final class A implements S {} + """, + """ + package lib; + public record B(S o) implements S {} + """, + """ + package lib; + public record R(S a, String s, S b) {} + """}, + """ + package test; + import lib.*; + public class Test { + private int test(R r) { + return switch (r) { + case R(A a, String s, A b) -> 0; + case R(A a, String s, B b) -> 0; + case R(B a, String s, A b) -> 0; + case R(B(A o), String s, B b) -> 0; + }; + } + } + """, + "Test.java:5:16: compiler.err.not.exhaustive", + "1 error"); + doTest(base, + new String[]{""" + package lib; + public sealed interface S permits A, B {} + """, + """ + package lib; + public final class A implements S {} + """, + """ + package lib; + public record B(S o) implements S {} + """, + """ + package lib; + public record R(S a, String s, S b) {} + """}, + """ + package test; + import lib.*; + public class Test { + private int test(R r) { + return switch (r) { + case R(A a, String s, A b) -> 0; + case R(A a, String s, B b) -> 0; + case R(B a, String s, A b) -> 0; + case R(B(A o), String s, B b) -> 0; + case R(B(B o), String s, B b) -> 0; + }; + } + } + """); } + public void testTransitiveSealed(Path base) throws Exception { doTest(base, new String[0], @@ -817,24 +924,18 @@ public class Exhaustiveness extends TestRunner { case C6 c -> {} """, "Test.java:11:9: compiler.err.not.exhaustive.statement", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"), new TestCase(""" case C3 c -> {} case C6 c -> {} """, "Test.java:11:9: compiler.err.not.exhaustive.statement", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"), new TestCase(""" case C3 c -> {} case C5 c -> {} """, "Test.java:11:9: compiler.err.not.exhaustive.statement", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"), new TestCase(""" case C1 c -> {} @@ -843,8 +944,6 @@ public class Exhaustiveness extends TestRunner { case C6 c -> {} """, "Test.java:12:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I, test.Test.C1)", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"), new TestCase(""" case C2 c -> {} @@ -853,8 +952,6 @@ public class Exhaustiveness extends TestRunner { case C6 c -> {} """, "Test.java:12:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I, test.Test.C2)", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"), new TestCase(""" case C4 c -> {} @@ -863,8 +960,6 @@ public class Exhaustiveness extends TestRunner { case C6 c -> {} """, "Test.java:12:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I, test.Test.C4)", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"), }; for (TestCase tc : subCases) { @@ -962,8 +1057,6 @@ public class Exhaustiveness extends TestRunner { "Test.java:42:42: compiler.err.cant.resolve.location: kindname.class, E, , , (compiler.misc.location: kindname.class, test.Test, null)", "Test.java:22:9: compiler.err.not.exhaustive.statement", "Test.java:29:17: compiler.err.not.exhaustive", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "4 errors"); } @@ -1030,8 +1123,6 @@ public class Exhaustiveness extends TestRunner { } """, "Test.java:8:17: compiler.err.not.exhaustive", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"); doTest(base, new String[0], @@ -1052,8 +1143,6 @@ public class Exhaustiveness extends TestRunner { """, "Test.java:9:29: compiler.err.cant.resolve.location.args: kindname.method, undefined, , , (compiler.misc.location: kindname.class, test.Test, null)", "Test.java:8:17: compiler.err.not.exhaustive", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "2 errors"); } @@ -1106,8 +1195,6 @@ public class Exhaustiveness extends TestRunner { } """, "Test.java:10:5: compiler.err.missing.ret.stmt", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"); doTest(base, new String[0], @@ -1126,8 +1213,6 @@ public class Exhaustiveness extends TestRunner { } """, "Test.java:11:5: compiler.err.missing.ret.stmt", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"); doTest(base, new String[0], @@ -1145,8 +1230,6 @@ public class Exhaustiveness extends TestRunner { } """, "Test.java:10:5: compiler.err.missing.ret.stmt", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"); doTest(base, new String[0], @@ -1165,8 +1248,6 @@ public class Exhaustiveness extends TestRunner { } """, "Test.java:11:5: compiler.err.missing.ret.stmt", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error"); } @@ -1193,7 +1274,685 @@ public class Exhaustiveness extends TestRunner { """); } + @Test + public void testEnumExhaustiveness(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface Opt {} + enum A implements Opt { A, B, C; } + enum B implements Opt { B, C, D; } + + void test(Opt optValue) { + switch (optValue) { + case A.A, A.B -> {} + case A.C, B.C -> {} + case B.B, B.D -> {} + } + } + } + """); + } + + public void testNestedApplicable(Path base) throws Exception { + record TestCase(String cases, String... errors) {} + TestCase[] subCases = new TestCase[] { + new TestCase(""" + case R(C3 c) -> {} + case R(C5 c) -> {} + case R(C6 c) -> {} + """), //OK + new TestCase(""" + case R(C5 c) -> {} + case R(C6 c) -> {} + """, + "Test.java:12:9: compiler.err.not.exhaustive.statement", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error"), + new TestCase(""" + case R(C3 c) -> {} + case R(C6 c) -> {} + """, + "Test.java:12:9: compiler.err.not.exhaustive.statement", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error"), + new TestCase(""" + case R(C3 c) -> {} + case R(C5 c) -> {} + """, + "Test.java:12:9: compiler.err.not.exhaustive.statement", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error"), + new TestCase(""" + case R(C1 c) -> {} + case R(C3 c) -> {} + case R(C5 c) -> {} + case R(C6 c) -> {} + """, + "Test.java:13:20: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I, test.Test.C1)", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error"), + new TestCase(""" + case R(C2 c) -> {} + case R(C3 c) -> {} + case R(C5 c) -> {} + case R(C6 c) -> {} + """, + "Test.java:13:20: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I, test.Test.C2)", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error"), + new TestCase(""" + case R(C4 c) -> {} + case R(C3 c) -> {} + case R(C5 c) -> {} + case R(C6 c) -> {} + """, + "Test.java:13:20: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I, test.Test.C4)", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error"), + }; + for (TestCase tc : subCases) { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + final class C1 implements I {} + final class C2 implements I {} + final class C3 implements I {} + final class C4 implements I {} + final class C5 implements I {} + final class C6 implements I {} + record R(I i) {} + void t(R i) { + switch (i) { + ${cases} + } + } + } + """.replace("${cases}", tc.cases), + tc.errors); + } + } + + @Test + public void testComplexSubTypes1(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I permits I1, I2, I3 {} + sealed interface I1 extends I permits C1 {} + sealed interface I2 extends I {} + sealed interface I3 extends I {} + final class C1 implements I1, I2 {} + final class C2 implements I3 {} + + void test(I i) { + switch (i) { + case I2 i2 -> + System.out.println("I2"); + case I3 i3 -> + System.out.println("I3"); + } + } + } + """); + } + + @Test + public void testComplexSubTypes2(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I permits I1, I2, I3 {} + sealed interface I1 extends I permits C1 {} + sealed interface I2 extends I {} + sealed interface I3 extends I {} + sealed abstract class C1 implements I1 {} + final class C2 extends C1 implements I2 {} + final class C3 extends C1 implements I3 {} + + void test(I i) { + switch (i) { + case I2 i2 -> + System.out.println("I2"); + case I3 i3 -> + System.out.println("I3"); + } + } + } + """); + } + + @Test + public void testComplexSubTypes3(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + sealed interface I1 extends I {} + final class I2 implements I1 {} + + void test(I i) { + switch (i) { + case I1 i1 -> + System.out.println("I1"); + case I2 i2 -> + System.out.println("I2"); + } + } + } + """, + "Test.java:11:18: compiler.err.pattern.dominated", + "1 error"); + } + + @Test + public void testComplexSubTypes4(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I permits I1, I2, I3 {} + sealed interface I1 extends I permits C1 {} + sealed class I2 implements I {} + sealed interface I3 extends I {} + final class C1 extends I2 implements I1 {} + final class C2 implements I3 {} + + void test(I i) { + switch (i) { + case I2 i2 -> + System.out.println("I2"); + case I3 i3 -> + System.out.println("I3"); + } + } + } + """, + "Test.java:11:9: compiler.err.not.exhaustive.statement", + "1 error"); + } + + @Test + public void testComplexSubTypes5(Path base) throws Exception { + doTest(base, + new String[0], + """ + class Test { + sealed interface I permits A, B, C { } + interface I3 { } + sealed interface I2 extends I3 permits B, C { } + final class A implements I {} + final class B implements I, I2 {} + final class C implements I, I2 {} + + int m(I i) { + return switch (i) { + case A a -> 1; + case I3 e -> 2; + }; + } + } + """); + } + + @Test + public void testComplexSubTypes6(Path base) throws Exception { + doTest(base, + new String[0], + """ + class Test { + sealed interface I0 permits I1, I2 { } + sealed interface I00 permits I1, I2 { } + + sealed interface I1 extends I0, I00 permits B, C { } + sealed interface I2 extends I0, I00 permits B, C { } + + static final class B implements I1, I2 { } + static final class C implements I1, I2 { } + + int test(Object o) { + return switch (o) { + case B c -> 2; + case C d -> 3; + }; + } + } + """, + "Test.java:12:16: compiler.err.not.exhaustive", + "1 error"); + } + + private static final int NESTING_CONSTANT = 5; + + Set createDeeplyNestedVariants() { + Set variants = new HashSet<>(); + variants.add("C _"); + variants.add("R(I _, I _, I _)"); + for (int n = 0; n < NESTING_CONSTANT; n++) { + Set newVariants = new HashSet<>(); + for (String variant : variants) { + if (variant.contains(", I _")) { + newVariants.add(variant.replaceFirst(", I _", ", C _")); + newVariants.add(variant.replaceFirst(", I _", ", R(I _, I _, I _)")); + } else { + newVariants.add(variant); + } + } + variants = newVariants; + } + for (int n = 0; n < NESTING_CONSTANT; n++) { + Set newVariants = new HashSet<>(); + for (String variant : variants) { + if (variant.contains("I _")) { + newVariants.add(variant.replaceFirst("I _", "C _")); + newVariants.add(variant.replaceFirst("I _", "R(I _, I _, I _)")); + } else { + newVariants.add(variant); + } + } + variants = newVariants; + } + OUTER: for (int i = 0; i < NESTING_CONSTANT; i++) { + Iterator it = variants.iterator(); + while (it.hasNext()) { + String current = it.next(); + if (current.contains("(I _, I _, I _)")) { + it.remove(); + variants.add(current.replaceFirst("\\(I _, I _, I _\\)", "(C _, I _, C _)")); + variants.add(current.replaceFirst("\\(I _, I _, I _\\)", "(C _, I _, R _)")); + variants.add(current.replaceFirst("\\(I _, I _, I _\\)", "(R _, I _, C _)")); + variants.add(current.replaceFirst("\\(I _, I _, I _\\)", "(R _, I _, R _)")); + continue OUTER; + } + } + } + + return variants; + } + + String testCodeForVariants(Iterable variants) { + StringBuilder cases = new StringBuilder(); + + for (String variant : variants) { + cases.append("case "); + String[] parts = variant.split("_"); + for (int i = 0; i < parts.length; i++) { + cases.append(parts[i]); + if (i + 1 < parts.length || i == 0) { + cases.append("v" + i); + } + } + cases.append(" -> System.err.println();\n"); + } + String code = """ + package test; + public class Test { + sealed interface I {} + final class C implements I {} + record R(I c1, I c2, I c3) implements I {} + + void test(I i) { + switch (i) { + ${cases} + } + } + } + """.replace("${cases}", cases); + + return code; + } + + @Test + public void testDeeplyNestedExhaustive(Path base) throws Exception { + Set variants = createDeeplyNestedVariants(); + String code = testCodeForVariants(variants); + + System.err.println("analyzing:"); + System.err.println(code); + doTest(base, + new String[0], + code, + true); + } + + @Test + public void testDeeplyNestedNotExhaustive(Path base) throws Exception { + List variants = createDeeplyNestedVariants().stream().collect(Collectors.toCollection(ArrayList::new)); + variants.remove((int) (Math.random() * variants.size())); + String code = testCodeForVariants(variants); + + System.err.println("analyzing:"); + System.err.println(code); + doTest(base, + new String[0], + code, + new String[] {null}); + } + + @Test + public void testMultipleSealed(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I1 {} + sealed interface I2 {} + final class A_I1 implements I1 {} + final class B_I2 implements I2 {} + final class C_I1_I2 implements I1, I2 {} + + void test(Z z) { + switch (z) { + case C_I1_I2 c -> + System.out.println("C_I1_I2"); + } + } + } + """); + } + + @Test + public void testOverfitting(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + final class A implements I {} + final class B implements I {} + record R(String s, I i) {} + + void test(R r) { + switch (r) { + case R(CharSequence s, A a) -> + System.out.println("A"); + case R(Object o, B b) -> + System.out.println("B"); + } + } + } + """); + } + + @Test + public void testDiamondInheritance1(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + sealed abstract class A {} + final class C extends A implements I {} + + void test(I i) { + switch (i) { + case C c -> + System.out.println("C"); + } + } + } + """); + } + + @Test + public void testDiamondInheritance2(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + sealed abstract class A {} + final class C extends A implements I {} + record R(I i) {} + + void test(R r) { + switch (r) { + case R(C c) -> + System.out.println("C"); + } + } + } + """); + } + + @Test + public void testDiamondInheritance3(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + sealed interface S1 extends I {} + sealed interface S2 extends I {} + final class C1 implements S1 {} + final class C2 implements S2 {} + final class C12 implements S1, S2 {} + + void test(I i) { + switch (i) { + case C1 c -> + System.out.println("C1"); + case C2 c -> + System.out.println("C2"); + case C12 c -> + System.out.println("C12"); + } + } + } + """); + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + sealed interface S1 extends I {} + sealed interface S2 extends I {} + final class C1 implements S1 {} + final class C2 implements S2 {} + final class C12 implements S1, S2 {} + + void test(I i) { + switch (i) { + case C12 c -> + System.out.println("C12"); + case C1 c -> + System.out.println("C1"); + case C2 c -> + System.out.println("C2"); + } + } + } + """); + } + + @Test + public void testDiamondInheritance4(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + sealed interface S1 extends I {} + sealed interface S2 extends I {} + final class C1 implements S1 {} + final class C2 implements S2 {} + final class C12 implements S1, S2 {} + record R(I i) {} + + void test(R r) { + switch (r) { + case R(C1 c) -> + System.out.println("C1"); + case R(C2 c) -> + System.out.println("C2"); + case R(C12 c) -> + System.out.println("C12"); + } + } + } + """); + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + sealed interface S1 extends I {} + sealed interface S2 extends I {} + final class C1 implements S1 {} + final class C2 implements S2 {} + final class C12 implements S1, S2 {} + record R(I i) {} + + void test(R r) { + switch (r) { + case R(C12 c) -> + System.out.println("C12"); + case R(C1 c) -> + System.out.println("C1"); + case R(C2 c) -> + System.out.println("C2"); + } + } + } + """); + } + + @Test + public void testDiamondInheritance5(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + sealed interface S1 extends I {} + sealed interface S2 extends I {} + sealed interface S3 extends I {} + final class C13 implements S1, S3 {} + final class C23 implements S2, S3 {} + + void test(I i) { + switch (i) { + case C13 c -> + System.out.println("C13"); + case C23 c -> + System.out.println("C23"); + } + } + } + """); + } + + @Test + public void testDiamondInheritance6(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + sealed interface S1 extends I {} + sealed interface S2 extends I {} + final class C1 implements S1 {} + sealed abstract class C2 implements S2 {} + final class C2Prime extends C2 {} + final class C12 implements S1, S2 {} + + void test(I i) { + switch (i) { + case C1 c -> + System.out.println("C1"); + case C2Prime c -> + System.out.println("C2"); + case C12 c -> + System.out.println("C12"); + } + } + } + """); + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + sealed interface S1 extends I {} + sealed interface S2 extends I {} + final class C1 implements S1 {} + sealed abstract class C2 implements S2 {} + final class C2Prime extends C2 {} + final class C12 implements S1, S2 {} + + void test(I i) { + switch (i) { + case C2Prime c -> + System.out.println("C2"); + case C1 c -> + System.out.println("C1"); + case C12 c -> + System.out.println("C12"); + } + } + } + """); + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + sealed interface S1 extends I {} + sealed interface S2 extends I {} + final class C1 implements S1 {} + sealed abstract class C2 implements S2 {} + final class C2Prime extends C2 {} + final class C12 implements S1, S2 {} + + void test(I i) { + switch (i) { + case C12 c -> + System.out.println("C12"); + case C1 c -> + System.out.println("C1"); + case C2Prime c -> + System.out.println("C2"); + } + } + } + """); + } + private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException { + doTest(base, libraryCode, testCode, false, expectedErrors); + } + + private void doTest(Path base, String[] libraryCode, String testCode, boolean stopAtFlow, String... expectedErrors) throws IOException { Path current = base.resolve("."); Path libClasses = current.resolve("libClasses"); @@ -1207,8 +1966,6 @@ public class Exhaustiveness extends TestRunner { } new JavacTask(tb) - .options("--enable-preview", - "-source", JAVA_VERSION) .outdir(libClasses) .files(tb.findJavaFiles(libSrc)) .run(); @@ -1223,18 +1980,18 @@ public class Exhaustiveness extends TestRunner { var log = new JavacTask(tb) - .options("--enable-preview", - "-source", JAVA_VERSION, - "-XDrawDiagnostics", + .options("-XDrawDiagnostics", "-Xlint:-preview", "--class-path", libClasses.toString(), - "-XDshould-stop.at=FLOW") + "-XDshould-stop.at=FLOW", + stopAtFlow ? "-XDshould-stop.ifNoError=FLOW" + : "-XDnoop") .outdir(classes) .files(tb.findJavaFiles(src)) .run(expectedErrors.length > 0 ? Task.Expect.FAIL : Task.Expect.SUCCESS) .writeAll() .getOutputLines(Task.OutputKind.DIRECT); - if (expectedErrors.length > 0 && !List.of(expectedErrors).equals(log)) { + if (expectedErrors.length > 0 && expectedErrors[0] != null && !List.of(expectedErrors).equals(log)) { throw new AssertionError("Incorrect errors, expected: " + List.of(expectedErrors) + ", actual: " + log); } diff --git a/test/langtools/tools/javac/patterns/ForEachPatterns.java b/test/langtools/tools/javac/patterns/ForEachPatterns.java deleted file mode 100644 index d56997dcb6a..00000000000 --- a/test/langtools/tools/javac/patterns/ForEachPatterns.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * @test /nodynamiccopyright/ - * @summary - * @enablePreview - */ -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.function.Function; - -public class ForEachPatterns { - public static void main(String[] args) { - - List in = List.of(new Point(1, 2), new Point(2, 3)); - List in_iface = List.of(new Point(1, 2), new Point(2, 3)); - List inRaw = List.of(new Point(1, 2), new Point(2, 3), new Frog(3, 4)); - List inWithPointEx = List.of(new PointEx(1, 2)); - byte[] inBytes = { (byte) 127, (byte) 127 }; - List inWithNullComponent = List.of(new Point(1, null), new Point(2, 3)); - Point[] inArray = in.toArray(Point[]::new); - List inWithPrimitives = List.of(new WithPrimitives(1, 2), new WithPrimitives(2, 3)); - IParent recs [] = { new Rec(1) }; - List inWithNull = new ArrayList<>(); - { - inWithNull.add(new Point(2, 3)); - inWithNull.add(null); - } - - assertEquals(8, iteratorEnhancedFor(in)); - assertEquals(8, arrayEnhancedFor(inArray)); - assertEquals(8, simpleDecostructionPatternWithAccesses(in)); - assertEx(ForEachPatterns::simpleDecostructionPatternWithAccesses, null, NullPointerException.class); - assertMatchExceptionWithNested(ForEachPatterns::simpleDecostructionPatternWithAccesses, inWithNull, NullPointerException.class); - assertEx(ForEachPatterns::simpleDecostructionPatternWithAccesses, inWithNullComponent, NullPointerException.class); - assertMatchExceptionWithNested(ForEachPatterns::simpleDecostructionPatternException, inWithPointEx, TestPatternFailed.class); - assertEx(ForEachPatterns::simpleDecostructionPatternWithAccesses, (List) inRaw, ClassCastException.class); - assertEquals(2, simpleDecostructionPatternNoComponentAccess(in)); - assertMatchExceptionWithNested(ForEachPatterns::simpleDecostructionPatternNoComponentAccess, inWithNull, NullPointerException.class); - assertEquals(2, simpleDecostructionPatternNoComponentAccess(inWithNullComponent)); - assertEquals(8, varAndConcrete(in)); - assertEquals(3, returnFromEnhancedFor(in)); - assertEquals(0, breakFromEnhancedFor(in)); - assertEquals(254, primitiveWidening(inBytes)); - assertEquals(8, sealedRecordPassBaseType(in_iface)); - assertEquals(8, withPrimitives(inWithPrimitives)); - assertEquals(List.of(Color.RED), JEPExample()); - assertEquals(1, arrayWithSealed(recs)); - } - - static int iteratorEnhancedFor(List points) { - int result = 0; - for (Point(Integer a, Integer b) : points) { - result += a + b; - } - return result; - } - - static int arrayEnhancedFor(Point[] points) { - int result = 0; - for (Point(Integer a, Integer b) : points) { - result += a + b; - } - return result; - } - - static int simpleDecostructionPatternWithAccesses(List points) { - int result = 0; - for (Point(var a, var b): points) { - result += a + b; - } - return result; - } - - static int simpleDecostructionPatternException(List points) { - int result = 0; - for (PointEx(var a, var b): points) { - result += a + b; - } - return result; - } - - static int simpleDecostructionPatternNoComponentAccess(List points) { - int result = 0; - for (Point(var a, var b): points) { - result += 1; - } - return result; - } - - static int varAndConcrete(List points) { - int result = 0; - for (Point(Integer a, var b): points) { - result += a + b; - } - return result; - } - - static int returnFromEnhancedFor(List points) { - for (Point(var a, var b): points) { - return a + b; - } - return -1; - } - - static int breakFromEnhancedFor(List points) { - int i = 1; - int result = 0; - for (Point(var a, var b): points) { - if (i == 1) break; - else result += a + b; - } - return result; - } - - static int sealedRecordPassBaseType(List points) { - int result = 0; - - for(Point(var x, var y) : points) { - result += (x + y); - } - - return result; - } - - static int withPrimitives(List points) { - int result = 0; - for (WithPrimitives(int a, double b): points) { - result += a + (int) b; - } - return result; - } - - // Simpler pos tests with local variable declarations - // Should pass now and in the future if local variable - // declaration is subsumed by patterns (not just record patterns) - static int primitiveWidening(byte[] inBytes) { - int acc = 0; - for (int i: inBytes) { - acc += i; - } - return acc; - } - - static int applicability1(List points) { - for (IPoint p: points) { - System.out.println(p); - } - return -1; - } - - static int applicability2(List points) { - for (Object p: points) { - System.out.println(p); - } - return -1; - } - - static List JEPExample() { - Rectangle rect = new Rectangle( - new ColoredPoint(new Point(1,2), Color.RED), - new ColoredPoint(new Point(3,4), Color.GREEN) - ); - Rectangle[] rArr = {rect}; - return printUpperLeftColors(rArr); - } - //where - static List printUpperLeftColors(Rectangle[] r) { - List ret = new ArrayList<>(); - for (Rectangle(ColoredPoint(Point p, Color c), ColoredPoint lr): r) { - ret.add(c); - } - return ret; - } - - static int arrayWithSealed(IParent[] recs){ - for (Rec(int a) : recs) { - return a; - } - return -1; - } - - enum Color { RED, GREEN, BLUE } - record ColoredPoint(Point p, Color c) {} - record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {} - - sealed interface IParent permits Rec {} - record Rec(int a) implements IParent {} - - sealed interface IPoint permits Point {} - record Point(Integer x, Integer y) implements IPoint { } - - record GPoint(T x, T y) { } - record VoidPoint() { } - record RecordOfLists(List o) {} - record RecordOfLists2(List> o) {} - - @Target({ElementType.TYPE_USE, ElementType.LOCAL_VARIABLE}) - @interface Annot { - String field(); - } - record Frog(Integer x, Integer y) { } - record PointEx(Integer x, Integer y) { - @Override - public Integer x() { - throw new TestPatternFailed(EXCEPTION_MESSAGE); - } - } - record WithPrimitives(int x, double y) { } - static final String EXCEPTION_MESSAGE = "exception-message"; - public static class TestPatternFailed extends AssertionError { - public TestPatternFailed(String message) { - super(message); - } - } - - // error handling - static void fail(String message) { - throw new AssertionError(message); - } - - static void assertEquals(Object expected, Object actual) { - if (!Objects.equals(expected, actual)) { - throw new AssertionError("Expected: " + expected + "," + - "got: " + actual); - } - } - - static void assertMatchExceptionWithNested(Function, Integer> f, List points, Class nestedExceptionClass) { - try { - f.apply(points); - fail("Expected an exception, but none happened!"); - } - catch(Exception ex) { - assertEquals(MatchException.class, ex.getClass()); - - MatchException me = (MatchException) ex; - - assertEquals(nestedExceptionClass, me.getCause().getClass()); - } - } - - static void assertEx(Function, Integer> f, List points, Class exceptionClass) { - try { - f.apply(points); - fail("Expected an exception, but none happened!"); - } - catch(Exception ex) { - assertEquals(exceptionClass, ex.getClass()); - } - } -} diff --git a/test/langtools/tools/javac/patterns/ForEachPatternsErrors.java b/test/langtools/tools/javac/patterns/ForEachPatternsErrors.java deleted file mode 100644 index 408c1b2dff4..00000000000 --- a/test/langtools/tools/javac/patterns/ForEachPatternsErrors.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * @test /nodynamiccopyright/ - * @summary - * @enablePreview - * @compile/fail/ref=ForEachPatternsErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW ForEachPatternsErrors.java - */ - -import java.util.List; - -public class ForEachPatternsErrors { - - static void exhaustivity_error1(List points) { - for (Point(var x, var y): points) { - System.out.println(); - } - } - - static void exhaustivity_error2(List points) { - for (Point(var x, var y): points) { - System.out.println(); - } - } - - static void exhaustivity_error3(List opoints) { - for (OPoint(String s, String t) : opoints) { - System.out.println(s); - } - } - - static void exhaustivity_error4(List f) { - for (Rec(var x): f){ - } - } - - static void applicability_error(List points) { - for (Interface p: points) { - System.out.println(p); - } - } - - record Rec(String x) { } - interface Interface {} - sealed interface IPoint permits Point {} - record Point(Integer x, Integer y) implements IPoint { } - record OPoint(Object x, Object y) { } -} diff --git a/test/langtools/tools/javac/patterns/ForEachPatternsErrors.out b/test/langtools/tools/javac/patterns/ForEachPatternsErrors.out deleted file mode 100644 index f93c15e4a5e..00000000000 --- a/test/langtools/tools/javac/patterns/ForEachPatternsErrors.out +++ /dev/null @@ -1,8 +0,0 @@ -ForEachPatternsErrors.java:36:27: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Object, ForEachPatternsErrors.Interface) -ForEachPatternsErrors.java:13:9: compiler.err.foreach.not.exhaustive.on.type: ForEachPatternsErrors.Point, java.lang.Object -ForEachPatternsErrors.java:19:9: compiler.err.foreach.not.exhaustive.on.type: ForEachPatternsErrors.Point, java.lang.Object -ForEachPatternsErrors.java:25:9: compiler.err.foreach.not.exhaustive.on.type: ForEachPatternsErrors.OPoint, ForEachPatternsErrors.OPoint -ForEachPatternsErrors.java:31:9: compiler.err.foreach.not.exhaustive.on.type: ForEachPatternsErrors.Rec, compiler.misc.type.captureof: 1, ? -- compiler.note.preview.filename: ForEachPatternsErrors.java, DEFAULT -- compiler.note.preview.recompile -5 errors \ No newline at end of file diff --git a/test/langtools/tools/javac/patterns/ForEachTestAllAnalyzers.java b/test/langtools/tools/javac/patterns/ForEachTestAllAnalyzers.java deleted file mode 100644 index 3b8e4d897cd..00000000000 --- a/test/langtools/tools/javac/patterns/ForEachTestAllAnalyzers.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * @test /nodynamiccopyright/ - * @summary - * @enablePreview - * @compile -XDfind=all ForEachTestAllAnalyzers.java - */ -public class ForEachTestAllAnalyzers { - private void test(Iterable l) { - for (R(Object a) : l) { } - } - record R(Object a) {} -} \ No newline at end of file diff --git a/test/langtools/tools/javac/patterns/GenericRecordDeconstructionPattern.java b/test/langtools/tools/javac/patterns/GenericRecordDeconstructionPattern.java index fa59657970e..d87c025a7d6 100644 --- a/test/langtools/tools/javac/patterns/GenericRecordDeconstructionPattern.java +++ b/test/langtools/tools/javac/patterns/GenericRecordDeconstructionPattern.java @@ -24,7 +24,6 @@ /** * @test * @bug 8298184 - * @enablePreview * @compile GenericRecordDeconstructionPattern.java * @run main GenericRecordDeconstructionPattern */ @@ -47,8 +46,6 @@ public class GenericRecordDeconstructionPattern { runTest(this::runSwitchInference3); runTest(this::runSwitchInference4); testInference3(); - assertEquals(0, forEachInference(List.of(new Box("")))); - assertEquals(1, forEachInference(List.of(new Box(null)))); assertEquals(1, runIfSuperBound(new Box<>(new StringBuilder()))); assertEquals(1, runIfSuperBound(new Box<>(0))); } @@ -106,13 +103,6 @@ public class GenericRecordDeconstructionPattern { : -1; } - int forEachInference(Iterable> b) { - for (Box(var s) : b) { - return s == null ? 1 : s.length(); - } - return -1; - } - void testInference3() { I> b = new Box<>(new Box<>(null)); assertEquals(1, runSwitchInferenceNested(b)); diff --git a/test/langtools/tools/javac/patterns/Guards.java b/test/langtools/tools/javac/patterns/Guards.java index 3292bdc9fb0..2c8484ec073 100644 --- a/test/langtools/tools/javac/patterns/Guards.java +++ b/test/langtools/tools/javac/patterns/Guards.java @@ -25,7 +25,6 @@ * @test * @bug 8262891 8268663 8289894 * @summary Check guards implementation. - * @enablePreview */ import java.util.Objects; @@ -134,24 +133,24 @@ public class Guards { String typeGuardAfterParenthesizedTrueSwitchStatement(Object o) { switch (o) { - case (Integer i) when i == 0: o = String.valueOf(i); return "true"; - case (Integer i) when i == 2: o = String.valueOf(i); return "second"; + case Integer i when i == 0: o = String.valueOf(i); return "true"; + case Integer i when i == 2: o = String.valueOf(i); return "second"; case Object x: return "any"; } } String typeGuardAfterParenthesizedTrueSwitchExpression(Object o) { return switch (o) { - case (Integer i) when i == 0: o = String.valueOf(i); yield "true"; - case (Integer i) when i == 2: o = String.valueOf(i); yield "second"; + case Integer i when i == 0: o = String.valueOf(i); yield "true"; + case Integer i when i == 2: o = String.valueOf(i); yield "second"; case Object x: yield "any"; }; } String typeGuardAfterParenthesizedTrueIfStatement(Object o) { - if (o != null && o instanceof (Integer i) && i == 0) { + if (o != null && o instanceof Integer i && i == 0) { return "true"; - } else if (o != null && o instanceof (Integer i) && i == 2 && (o = i) != null) { + } else if (o != null && o instanceof Integer i && i == 2 && (o = i) != null) { return "second"; } else { return "any"; diff --git a/test/langtools/tools/javac/patterns/GuardsErrors.java b/test/langtools/tools/javac/patterns/GuardsErrors.java index 65bb1998a00..788c46e4e14 100644 --- a/test/langtools/tools/javac/patterns/GuardsErrors.java +++ b/test/langtools/tools/javac/patterns/GuardsErrors.java @@ -25,7 +25,6 @@ * @test * @bug 8262891 * @summary Check errors reported for guarded patterns. - * @enablePreview * @compile/fail/ref=GuardsErrors.out -XDrawDiagnostics GuardsErrors.java */ @@ -41,4 +40,35 @@ public class GuardsErrors { } + void variablesInGuards(Object o) { + final int i1; + int i2 = 0; + switch (o) { + case Integer v when (i1 = 0) == 0 -> {} + case Integer v when i2++ == 0 -> {} + case Integer v when ++i2 == 0 -> {} + case Integer v when new Predicate() { + public boolean test() { + final int i; + i = 2; + return i == 2; + } + }.test() -> {} + case Number v1 when v1 instanceof Integer v2 && (v2 = 0) == 0 -> {} + default -> {} + } + } + + GuardsErrors(Object o) { + switch (o) { + case Integer v when (f = 0) == 0 -> {} + default -> throw new RuntimeException(); + } + } + + final int f; + + interface Predicate { + public boolean test(); + } } diff --git a/test/langtools/tools/javac/patterns/GuardsErrors.out b/test/langtools/tools/javac/patterns/GuardsErrors.out index 800721afe2f..a87e4f1867c 100644 --- a/test/langtools/tools/javac/patterns/GuardsErrors.out +++ b/test/langtools/tools/javac/patterns/GuardsErrors.out @@ -1,4 +1,6 @@ -GuardsErrors.java:37:38: compiler.err.cant.ref.non.effectively.final.var: check, (compiler.misc.guard) -- compiler.note.preview.filename: GuardsErrors.java, DEFAULT -- compiler.note.preview.recompile -1 error +GuardsErrors.java:36:38: compiler.err.cant.ref.non.effectively.final.var: check, (compiler.misc.guard) +GuardsErrors.java:47:34: compiler.err.cannot.assign.not.declared.guard: i1 +GuardsErrors.java:48:33: compiler.err.cant.ref.non.effectively.final.var: i2, (compiler.misc.guard) +GuardsErrors.java:49:35: compiler.err.cant.ref.non.effectively.final.var: i2, (compiler.misc.guard) +GuardsErrors.java:64:34: compiler.err.cannot.assign.not.declared.guard: f +5 errors diff --git a/test/langtools/tools/javac/patterns/InstanceofTotalPattern-15.out b/test/langtools/tools/javac/patterns/InstanceofTotalPattern-15.out index e5db9129570..be3f95b66d9 100644 --- a/test/langtools/tools/javac/patterns/InstanceofTotalPattern-15.out +++ b/test/langtools/tools/javac/patterns/InstanceofTotalPattern-15.out @@ -1,2 +1,2 @@ -InstanceofTotalPattern.java:18:37: compiler.err.feature.not.supported.in.source: (compiler.misc.feature.pattern.matching.instanceof), 15, 16 +InstanceofTotalPattern.java:19:37: compiler.err.feature.not.supported.in.source: (compiler.misc.feature.pattern.matching.instanceof), 15, 16 1 error diff --git a/test/langtools/tools/javac/patterns/InstanceofTotalPattern-16.out b/test/langtools/tools/javac/patterns/InstanceofTotalPattern-16.out index c8308c36291..6d7ecaa75ec 100644 --- a/test/langtools/tools/javac/patterns/InstanceofTotalPattern-16.out +++ b/test/langtools/tools/javac/patterns/InstanceofTotalPattern-16.out @@ -1,3 +1,2 @@ -InstanceofTotalPattern.java:18:19: compiler.err.instanceof.pattern.no.subtype: java.lang.String, java.lang.String -InstanceofTotalPattern.java:22:17: compiler.err.instanceof.pattern.no.subtype: java.lang.String, java.lang.String -2 errors +InstanceofTotalPattern.java:19:19: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.unconditional.patterns.in.instanceof), 16, 21 +1 error diff --git a/test/langtools/tools/javac/patterns/InstanceofTotalPattern-20.out b/test/langtools/tools/javac/patterns/InstanceofTotalPattern-20.out new file mode 100644 index 00000000000..12495c674ed --- /dev/null +++ b/test/langtools/tools/javac/patterns/InstanceofTotalPattern-20.out @@ -0,0 +1,2 @@ +InstanceofTotalPattern.java:19:19: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.unconditional.patterns.in.instanceof), 20, 21 +1 error diff --git a/test/langtools/tools/javac/patterns/InstanceofTotalPattern-preview.out b/test/langtools/tools/javac/patterns/InstanceofTotalPattern-preview.out deleted file mode 100644 index a96cbd78002..00000000000 --- a/test/langtools/tools/javac/patterns/InstanceofTotalPattern-preview.out +++ /dev/null @@ -1,3 +0,0 @@ -InstanceofTotalPattern.java:18:30: compiler.warn.preview.feature.use.plural: (compiler.misc.feature.unconditional.patterns.in.instanceof) -InstanceofTotalPattern.java:22:28: compiler.warn.preview.feature.use.plural: (compiler.misc.feature.unconditional.patterns.in.instanceof) -2 warnings diff --git a/test/langtools/tools/javac/patterns/InstanceofTotalPattern.java b/test/langtools/tools/javac/patterns/InstanceofTotalPattern.java index 03c2a33f5f5..db3d1909035 100644 --- a/test/langtools/tools/javac/patterns/InstanceofTotalPattern.java +++ b/test/langtools/tools/javac/patterns/InstanceofTotalPattern.java @@ -3,8 +3,9 @@ * @summary Verify behavior of total patterns in instanceof * @compile/fail/ref=InstanceofTotalPattern-15.out --release 15 -XDrawDiagnostics InstanceofTotalPattern.java * @compile/fail/ref=InstanceofTotalPattern-16.out --release 16 -XDrawDiagnostics InstanceofTotalPattern.java - * @compile/ref=InstanceofTotalPattern-preview.out --enable-preview -source ${jdk.version} -Xlint:-options,preview -XDrawDiagnostics InstanceofTotalPattern.java - * @run main/othervm --enable-preview InstanceofTotalPattern + * @compile/fail/ref=InstanceofTotalPattern-20.out --release 20 -XDrawDiagnostics InstanceofTotalPattern.java + * @compile InstanceofTotalPattern.java + * @run main InstanceofTotalPattern */ public class InstanceofTotalPattern { diff --git a/test/langtools/tools/javac/patterns/LambdaCannotCapturePatternVariables.java b/test/langtools/tools/javac/patterns/LambdaCannotCapturePatternVariables.java index 5ba92bb47ab..0e9c7c0af61 100644 --- a/test/langtools/tools/javac/patterns/LambdaCannotCapturePatternVariables.java +++ b/test/langtools/tools/javac/patterns/LambdaCannotCapturePatternVariables.java @@ -26,7 +26,6 @@ * @bug 8267610 8269738 * @summary LambdaToMethod cannot capture pattern variables. So the TransPatterns should * transform the pattern variables and symbols to normal variables and symbols. - * @enablePreview */ import java.util.function.Supplier; diff --git a/test/langtools/tools/javac/patterns/MatchExceptionTest.java b/test/langtools/tools/javac/patterns/MatchExceptionTest.java index 9fad0dff4b1..397e30862c0 100644 --- a/test/langtools/tools/javac/patterns/MatchExceptionTest.java +++ b/test/langtools/tools/javac/patterns/MatchExceptionTest.java @@ -30,7 +30,7 @@ * jdk.compiler/com.sun.tools.javac.main * jdk.jdeps/com.sun.tools.classfile * @build toolbox.ToolBox toolbox.JavacTask - * @run main/othervm --enable-preview MatchExceptionTest + * @run main MatchExceptionTest */ import java.nio.file.Path; @@ -90,11 +90,10 @@ public class MatchExceptionTest extends TestRunner { } } Setup[] variants = new Setup[] { - new Setup(false, "-source", "20"), - new Setup(false, "-source", JAVA_VERSION), - new Setup(true, "-source", JAVA_VERSION, "--enable-preview"), + new Setup(false, "--release", "20"), + new Setup(true), }; - record Source(String source, boolean needsPreview) {} + record Source(String source, boolean needs21) {} Source[] sources = new Source[] { new Source(codeStatement, true), new Source(codeExpression, false), @@ -102,8 +101,8 @@ public class MatchExceptionTest extends TestRunner { Path curPath = Path.of("."); for (Source source : sources) { for (Setup variant : variants) { - if (source.needsPreview && - !Arrays.asList(variant.options).contains("--enable-preview")) { + if (source.needs21 && + !Arrays.asList(variant.options).contains("21")) { continue; } new JavacTask(tb) diff --git a/test/langtools/tools/javac/patterns/NestedDeconstructionPattern.java b/test/langtools/tools/javac/patterns/NestedDeconstructionPattern.java index ff07ec9e393..4c4dae73d6b 100644 --- a/test/langtools/tools/javac/patterns/NestedDeconstructionPattern.java +++ b/test/langtools/tools/javac/patterns/NestedDeconstructionPattern.java @@ -23,7 +23,6 @@ /** * @test - * @enablePreview */ import java.util.Objects; diff --git a/test/langtools/tools/javac/patterns/NestedPatternVariablesBytecode.java b/test/langtools/tools/javac/patterns/NestedPatternVariablesBytecode.java index 0e5c7d81aeb..6ceca9d95a6 100644 --- a/test/langtools/tools/javac/patterns/NestedPatternVariablesBytecode.java +++ b/test/langtools/tools/javac/patterns/NestedPatternVariablesBytecode.java @@ -79,7 +79,6 @@ public class NestedPatternVariablesBytecode extends TestRunner { }"""; Path curPath = Path.of("."); new JavacTask(tb) - .options("--enable-preview", "-source", JAVA_VERSION) .sources(code) .outdir(curPath) .run(); diff --git a/test/langtools/tools/javac/patterns/NestedPrimitiveDeconstructionPattern.java b/test/langtools/tools/javac/patterns/NestedPrimitiveDeconstructionPattern.java index 754454e3f58..95107d60ffe 100644 --- a/test/langtools/tools/javac/patterns/NestedPrimitiveDeconstructionPattern.java +++ b/test/langtools/tools/javac/patterns/NestedPrimitiveDeconstructionPattern.java @@ -23,7 +23,6 @@ /** * @test - * @enablePreview */ import java.util.Objects; diff --git a/test/langtools/tools/javac/patterns/NewCaseStructureTest.java b/test/langtools/tools/javac/patterns/NewCaseStructureTest.java index edb9d3265f0..faa23f1979d 100644 --- a/test/langtools/tools/javac/patterns/NewCaseStructureTest.java +++ b/test/langtools/tools/javac/patterns/NewCaseStructureTest.java @@ -68,9 +68,7 @@ public class NewCaseStructureTest extends TestRunner { @Test public void testCorrectMultiLabelCaseStructure(Path base) throws Exception { for (String pattern : new String[] {"String s", - "(String s)", "R(int i)", - "(R(int i))", "null, default", "null", "1", @@ -327,7 +325,7 @@ public class NewCaseStructureTest extends TestRunner { public class Test { private int test(Integer o) { return switch (o) { - case (Integer i) when i > 0 -> 0; + case Integer i when i > 0 -> 0; case 0 -> 0; case Integer i -> 0; }; @@ -344,8 +342,8 @@ public class NewCaseStructureTest extends TestRunner { private int test(Integer o) { return switch (o) { default -> 0; - case (Integer i) when i > 0 -> 0; - case (Integer i) when i > 0 -> 0; + case Integer i when i > 0 -> 0; + case Integer i when i > 0 -> 0; }; } } @@ -358,7 +356,7 @@ public class NewCaseStructureTest extends TestRunner { public class Test { private int test(Integer o) { return switch (o) { - case (Integer i) when i > 0 -> 0; + case Integer i when i > 0 -> 0; default -> 0; case null -> 0; }; @@ -375,7 +373,7 @@ public class NewCaseStructureTest extends TestRunner { private int test(Integer o) { return switch (o) { case null, default -> 0; - case (Integer i) when i > 0 -> 0; + case Integer i when i > 0 -> 0; }; } } @@ -431,9 +429,7 @@ public class NewCaseStructureTest extends TestRunner { Files.createDirectories(classes); List actual = new JavacTask(tb) - .options("--enable-preview", - "-source", JAVA_VERSION, - "-XDrawDiagnostics", + .options("-XDrawDiagnostics", "-Xlint:-preview", "-XDshould-stop.at=FLOW") .outdir(classes) diff --git a/test/langtools/tools/javac/patterns/NoSubtypeCheck.java b/test/langtools/tools/javac/patterns/NoSubtypeCheck.java index 973ffb5a6ec..ed56e547822 100644 --- a/test/langtools/tools/javac/patterns/NoSubtypeCheck.java +++ b/test/langtools/tools/javac/patterns/NoSubtypeCheck.java @@ -2,21 +2,101 @@ * @test /nodynamiccopyright/ * @bug 8250625 * @summary Verify pattern matching test which is always true produces an error - * @compile/fail/ref=NoSubtypeCheck.out -XDrawDiagnostics NoSubtypeCheck.java + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run main NoSubtypeCheck */ -public class NoSubtypeCheck { - public static void main(Object o, String s, List l) { - boolean b1 = o instanceof Object v1; - boolean b2 = o instanceof String v2; - boolean b3 = s instanceof Object v3; - boolean b4 = s instanceof String v4; - boolean b5 = l instanceof List v5; - boolean b6 = l instanceof List2 v6; - boolean b7 = undef instanceof String v7; - boolean b8 = o instanceof Undef v7; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import toolbox.TestRunner; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class NoSubtypeCheck extends TestRunner { + + ToolBox tb; + + public static void main(String... args) throws Exception { + new NoSubtypeCheck().runTests(); + } + + NoSubtypeCheck() { + super(System.err); + tb = new ToolBox(); + } + + public void runTests() throws Exception { + runTests(m -> new Object[] { Paths.get(m.getName()) }); + } + + @Test + public void testSimple(Path base) throws Exception { + record TestCase(String test, String expectedError) {} + TestCase[] testCases = new TestCase[] { + new TestCase("boolean b1 = o instanceof Object v1;", + "Test.java:3:24: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.unconditional.patterns.in.instanceof), 20, 21"), + new TestCase("boolean b2 = o instanceof String v2;", null), + new TestCase("boolean b3 = s instanceof Object v3;", + "Test.java:3:24: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.unconditional.patterns.in.instanceof), 20, 21"), + new TestCase("boolean b4 = s instanceof String v4;", + "Test.java:3:24: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.unconditional.patterns.in.instanceof), 20, 21"), + new TestCase("boolean b5 = l instanceof List v5;", + "Test.java:3:24: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.unconditional.patterns.in.instanceof), 20, 21"), + new TestCase("boolean b6 = l instanceof List2 v6;", null), + new TestCase("boolean b7 = undef instanceof String v7;", + "Test.java:3:22: compiler.err.cant.resolve.location: kindname.variable, undef, , , (compiler.misc.location: kindname.class, Test, null)"), + new TestCase("boolean b8 = o instanceof Undef v7;", + "Test.java:3:35: compiler.err.cant.resolve.location: kindname.class, Undef, , , (compiler.misc.location: kindname.class, Test, null)"), + }; + + for (TestCase testCase : testCases) { + System.err.println("==running testcase: " + testCase.test); + Path current = base.resolve("."); + Path src = current.resolve("src"); + + tb.writeJavaFiles(src, """ + public class Test { + public static void main(Object o, String s, List l) { + {testCase.test} + } + + public interface List {} + public interface List2 extends List {} + } + """.replace("{testCase.test}", testCase.test)); + + Path classes = current.resolve("classes"); + + Files.createDirectories(classes); + + List log = new JavacTask(tb) + .options("-XDrawDiagnostics", + "--release", "20") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(testCase.expectedError != null ? Task.Expect.FAIL : Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + if (testCase.expectedError != null) { + List expectedOutput = new ArrayList<>(); + + expectedOutput.add(testCase.expectedError); + expectedOutput.add("1 error"); + + if (!expectedOutput.equals(log)) { + throw new AssertionError("Unexpected output:\n" + log); + } + } + } } - public interface List {} - public interface List2 extends List {} } diff --git a/test/langtools/tools/javac/patterns/NullSwitch.java b/test/langtools/tools/javac/patterns/NullSwitch.java index 0211aaa4de3..c74abc03d9e 100644 --- a/test/langtools/tools/javac/patterns/NullSwitch.java +++ b/test/langtools/tools/javac/patterns/NullSwitch.java @@ -2,7 +2,6 @@ * @test /nodynamiccopyright/ * @bug 8262891 8272776 * @summary Check null handling for non-pattern switches. - * @enablePreview */ public class NullSwitch { diff --git a/test/langtools/tools/javac/patterns/NullsInDeconstructionPatterns.java b/test/langtools/tools/javac/patterns/NullsInDeconstructionPatterns.java index 0d08a85cea4..2bcb718d2dd 100644 --- a/test/langtools/tools/javac/patterns/NullsInDeconstructionPatterns.java +++ b/test/langtools/tools/javac/patterns/NullsInDeconstructionPatterns.java @@ -1,7 +1,6 @@ /* * @test /nodynamiccopyright/ * @summary Testing record patterns against the null constant (14.30.2 Pattern Matching) - * @enablePreview */ public class NullsInDeconstructionPatterns { diff --git a/test/langtools/tools/javac/patterns/NullsInPatterns.java b/test/langtools/tools/javac/patterns/NullsInPatterns.java index 9f9afd425e9..be4c2bc1213 100644 --- a/test/langtools/tools/javac/patterns/NullsInPatterns.java +++ b/test/langtools/tools/javac/patterns/NullsInPatterns.java @@ -2,13 +2,13 @@ * @test /nodynamiccopyright/ * @bug 8231827 * @summary Testing pattern matching against the null constant - * @compile/fail/ref=NullsInPatterns.out -XDrawDiagnostics NullsInPatterns.java + * @run main NullsInPatterns */ import java.util.List; public class NullsInPatterns { - public static void meth() { + public static void main(String... args) { if (null instanceof List t) { throw new AssertionError("broken"); } else { diff --git a/test/langtools/tools/javac/patterns/Parenthesized.java b/test/langtools/tools/javac/patterns/Parenthesized.java index 0091067724b..abc73cb65fd 100644 --- a/test/langtools/tools/javac/patterns/Parenthesized.java +++ b/test/langtools/tools/javac/patterns/Parenthesized.java @@ -25,7 +25,7 @@ * @test * @bug 8262891 8269354 * @summary Test parenthesized pattern - * @enablePreview + * @compile/fail/ref=Parenthesized.out -XDrawDiagnostics Parenthesized.java */ public class Parenthesized { public static void main(String... args) { diff --git a/test/langtools/tools/javac/patterns/Parenthesized.out b/test/langtools/tools/javac/patterns/Parenthesized.out new file mode 100644 index 00000000000..e136f0a6636 --- /dev/null +++ b/test/langtools/tools/javac/patterns/Parenthesized.out @@ -0,0 +1,5 @@ +Parenthesized.java:38:18: compiler.err.illegal.start.of.type +Parenthesized.java:42:18: compiler.err.illegal.start.of.type +Parenthesized.java:45:26: compiler.err.illegal.start.of.type +Parenthesized.java:48:35: compiler.err.illegal.start.of.type +4 errors diff --git a/test/langtools/tools/javac/patterns/ParenthesizedCombo.java b/test/langtools/tools/javac/patterns/ParenthesizedCombo.java deleted file mode 100644 index 1780f03fb3b..00000000000 --- a/test/langtools/tools/javac/patterns/ParenthesizedCombo.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -import combo.ComboInstance; -import combo.ComboParameter; -import combo.ComboTask; -import combo.ComboTestHelper; -import toolbox.ToolBox; - -/* - * @test - * @bug 8269674 - * @summary Improve testing of parenthesized patterns - * @library /tools/lib /tools/javac/lib - * @modules - * jdk.compiler/com.sun.tools.javac.api - * jdk.compiler/com.sun.tools.javac.file - * jdk.compiler/com.sun.tools.javac.main - * jdk.compiler/com.sun.tools.javac.util - * @build toolbox.ToolBox toolbox.JavacTask - * @build combo.ComboTestHelper - * @compile ParenthesizedCombo.java - * @run main/othervm ParenthesizedCombo - */ -public class ParenthesizedCombo extends ComboInstance { - protected ToolBox tb; - - ParenthesizedCombo() { - super(); - tb = new ToolBox(); - } - - public static void main(String... args) throws Exception { - new ComboTestHelper() - .withDimension("PATTERN_USE", (x, patternUse) -> x.patternUse = patternUse, PATTERN_USE.values()) - .withDimension("CASE_LABEL", (x, caseLabel) -> x.caseLabel = caseLabel, CASE_LABEL.values()) - .withDimension("TYPE_PATTERN", (x, typePattern) -> x.typePattern = typePattern, TYPE_PATTERN.values()) - .run(ParenthesizedCombo::new); - } - - private PATTERN_USE patternUse; - private CASE_LABEL caseLabel; - private TYPE_PATTERN typePattern; - - private static final String MAIN_TEMPLATE = - """ - public class Test { - record StringBox(String s1) {} - record StringBox2(StringBox s) {} - public static void test(Object o) { - #{PATTERN_USE} - } - } - """; - - @Override - protected void doWork() throws Throwable { - ComboTask task = newCompilationTask() - .withSourceFromTemplate(MAIN_TEMPLATE, pname -> switch (pname) { - case "PATTERN_USE" -> patternUse; - case "CASE_LABEL" -> caseLabel; - case "TYPE_PATTERN" -> typePattern; - default -> throw new UnsupportedOperationException(pname); - }) - .withOption("--enable-preview") - .withOption("-source") - .withOption(String.valueOf(Runtime.version().feature())); - task.generate(result -> { - if (result.hasErrors()) { - throw new AssertionError("Unexpected result: " + result.compilationInfo()); - } - }); - } - - public enum TYPE_PATTERN implements ComboParameter { - SIMPLE("String s1"), - PARENTHESIZED_SIMPLE("(String s1)"); - - private final String code; - - private TYPE_PATTERN(String code) { - this.code = code; - } - - @Override - public String expand(String optParameter) { - return code; - } - } - - public enum CASE_LABEL implements ComboParameter { - TYPE_PATTERN("#{TYPE_PATTERN}"), - PARENTHESIZED_RECORD_PATTERN("(StringBox(#{TYPE_PATTERN}))"), - PARENTHESIZED_RECORD_PATTERN_DEEP("(StringBox2(StringBox(#{TYPE_PATTERN})))"); - - private final String code; - - private CASE_LABEL(String code) { - this.code = code; - } - - @Override - public String expand(String optParameter) { - return code; - } - } - - public enum PATTERN_USE implements ComboParameter { - SWITCH_EXPR_VOID( - """ - switch (o) { - case #{CASE_LABEL} when s1.isEmpty() -> System.err.println("OK: " + s1); - default -> throw new AssertionError(); - } - """), - SWITCH_STAT_VOID( - """ - switch (o) { - case #{CASE_LABEL} when s1.isEmpty(): - System.err.println("OK: " + s1); - break; - default: - throw new AssertionError(); - } - """), - SWITCH_EXPR_STRING( - """ - System.err.println(switch (o) { - case #{CASE_LABEL} when s1.isEmpty() -> "OK: " + s1; - default -> throw new AssertionError(); - }); - """), - IF_INSTANCEOF( - """ - if (o instanceof #{CASE_LABEL} && s1.isEmpty()) { - System.err.println("OK: " + s1); - } - """); - private final String body; - - private PATTERN_USE(String body) { - this.body = body; - } - - @Override - public String expand(String optParameter) { - return body; - } - } -} diff --git a/test/langtools/tools/javac/patterns/PatternCaseErrorRecovery.java b/test/langtools/tools/javac/patterns/PatternCaseErrorRecovery.java index 2e245d6f657..78440673df5 100644 --- a/test/langtools/tools/javac/patterns/PatternCaseErrorRecovery.java +++ b/test/langtools/tools/javac/patterns/PatternCaseErrorRecovery.java @@ -2,9 +2,9 @@ * @test /nodynamiccopyright/ * @bug 8268859 * @summary Verify error recovery/disambiguation of case labels that mix expressions and patterns - * @enablePreview * @compile/fail/ref=PatternCaseErrorRecovery.out -XDrawDiagnostics PatternCaseErrorRecovery.java */ + public class PatternCaseErrorRecovery { Object expressionLikeType(Object o1, Object o2) { final int a = 1; diff --git a/test/langtools/tools/javac/patterns/PatternDesugaring.java b/test/langtools/tools/javac/patterns/PatternDesugaring.java index 998fe5471cf..38c810cf53d 100644 --- a/test/langtools/tools/javac/patterns/PatternDesugaring.java +++ b/test/langtools/tools/javac/patterns/PatternDesugaring.java @@ -25,7 +25,6 @@ * @test * @bug 8291769 8300195 * @summary Verify the compiled code does not have unwanted constructs. - * @enablePreview * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main @@ -56,8 +55,6 @@ import toolbox.ToolBox; public class PatternDesugaring extends TestRunner { - private static final String JAVA_VERSION = System.getProperty("java.specification.version"); - ToolBox tb; public static void main(String... args) throws Exception { @@ -165,8 +162,6 @@ public class PatternDesugaring extends TestRunner { } new JavacTask(tb) - .options("--enable-preview", - "-source", JAVA_VERSION) .outdir(libClasses) .files(tb.findJavaFiles(libSrc)) .run(); @@ -181,9 +176,7 @@ public class PatternDesugaring extends TestRunner { var log = new JavacTask(tb) - .options("--enable-preview", - "-source", JAVA_VERSION, - "-XDrawDiagnostics", + .options("-XDrawDiagnostics", "-Xlint:-preview", "--class-path", libClasses.toString(), "-XDshould-stop.at=FLOW") @@ -261,8 +254,6 @@ public class PatternDesugaring extends TestRunner { } new JavacTask(tb) - .options("--enable-preview", - "-source", JAVA_VERSION) .outdir(libClasses) .files(tb.findJavaFiles(libSrc)) .run(); @@ -277,9 +268,7 @@ public class PatternDesugaring extends TestRunner { var log = new JavacTask(tb) - .options("--enable-preview", - "-source", JAVA_VERSION, - "-XDrawDiagnostics", + .options("-XDrawDiagnostics", "-Xlint:-preview", "--class-path", libClasses.toString(), "-XDshould-stop.at=FLOW") diff --git a/test/langtools/tools/javac/patterns/PatternErrorRecovery-no-preview.out b/test/langtools/tools/javac/patterns/PatternErrorRecovery-no-preview.out deleted file mode 100644 index f5a26bed89c..00000000000 --- a/test/langtools/tools/javac/patterns/PatternErrorRecovery-no-preview.out +++ /dev/null @@ -1,3 +0,0 @@ -PatternErrorRecovery.java:12:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch) -PatternErrorRecovery.java:11:18: compiler.err.const.expr.req -2 errors diff --git a/test/langtools/tools/javac/patterns/PatternErrorRecovery-old.out b/test/langtools/tools/javac/patterns/PatternErrorRecovery-old.out new file mode 100644 index 00000000000..ee25e829752 --- /dev/null +++ b/test/langtools/tools/javac/patterns/PatternErrorRecovery-old.out @@ -0,0 +1,3 @@ +PatternErrorRecovery.java:12:18: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.pattern.switch), 20, 21 +PatternErrorRecovery.java:11:18: compiler.err.const.expr.req +2 errors diff --git a/test/langtools/tools/javac/patterns/PatternErrorRecovery.java b/test/langtools/tools/javac/patterns/PatternErrorRecovery.java index 128f070e4d4..2e73de015b3 100644 --- a/test/langtools/tools/javac/patterns/PatternErrorRecovery.java +++ b/test/langtools/tools/javac/patterns/PatternErrorRecovery.java @@ -2,8 +2,8 @@ * @test /nodynamiccopyright/ * @bug 8268320 * @summary Verify user-friendly errors are reported for ill-formed pattern. - * @compile/fail/ref=PatternErrorRecovery.out -XDrawDiagnostics -XDshould-stop.at=FLOW --enable-preview -source ${jdk.version} PatternErrorRecovery.java - * @compile/fail/ref=PatternErrorRecovery-no-preview.out -XDrawDiagnostics -XDshould-stop.at=FLOW PatternErrorRecovery.java + * @compile/fail/ref=PatternErrorRecovery.out -XDrawDiagnostics -XDshould-stop.at=FLOW PatternErrorRecovery.java + * @compile/fail/ref=PatternErrorRecovery-old.out --release 20 -XDrawDiagnostics -XDshould-stop.at=FLOW PatternErrorRecovery.java */ public class PatternErrorRecovery { void errorRecoveryNoPattern1(Object o) { diff --git a/test/langtools/tools/javac/patterns/PatternErrorRecovery.out b/test/langtools/tools/javac/patterns/PatternErrorRecovery.out index 2fb00531de5..13cc0faf70c 100644 --- a/test/langtools/tools/javac/patterns/PatternErrorRecovery.out +++ b/test/langtools/tools/javac/patterns/PatternErrorRecovery.out @@ -1,4 +1,2 @@ PatternErrorRecovery.java:11:18: compiler.err.pattern.expected -- compiler.note.preview.filename: PatternErrorRecovery.java, DEFAULT -- compiler.note.preview.recompile 1 error diff --git a/test/langtools/tools/javac/patterns/PrettyTest.java b/test/langtools/tools/javac/patterns/PrettyTest.java index d51ebe4ccef..535d1ae002f 100644 --- a/test/langtools/tools/javac/patterns/PrettyTest.java +++ b/test/langtools/tools/javac/patterns/PrettyTest.java @@ -25,7 +25,6 @@ * @test * @summary Test behavior of Pretty * @modules jdk.compiler - * @enablePreview */ import java.io.IOException; @@ -93,7 +92,7 @@ public class PrettyTest { StringWriter out = new StringWriter(); JavacTask ct = (JavacTask) tool.getTask(out, null, noErrors, - List.of("--enable-preview", "-source", Integer.toString(Runtime.version().feature())), null, + List.of(), null, Arrays.asList(new MyFileObject(code))); return ct.parse().iterator().next(); } diff --git a/test/langtools/tools/javac/patterns/ProxyMethodLookup.java b/test/langtools/tools/javac/patterns/ProxyMethodLookup.java index 171042c2ffd..3e26a3f9d77 100644 --- a/test/langtools/tools/javac/patterns/ProxyMethodLookup.java +++ b/test/langtools/tools/javac/patterns/ProxyMethodLookup.java @@ -25,7 +25,6 @@ * @test * @bug 8288120 * @summary Verify an appropriate accessor method is looked up. - * @enablePreview */ public class ProxyMethodLookup { diff --git a/test/langtools/tools/javac/patterns/RawTypeBindingWarning.java b/test/langtools/tools/javac/patterns/RawTypeBindingWarning.java index 6bab6046d0e..1fb0aea688a 100644 --- a/test/langtools/tools/javac/patterns/RawTypeBindingWarning.java +++ b/test/langtools/tools/javac/patterns/RawTypeBindingWarning.java @@ -2,9 +2,9 @@ * @test /nodynamiccopyright/ * @bug 8263590 * @summary Verify correct warnings are produced for raw types in bindings - * @enablePreview * @compile/ref=RawTypeBindingWarning.out -Xlint:rawtypes -XDrawDiagnostics RawTypeBindingWarning.java */ + public class RawTypeBindingWarning { public static boolean t(Object o) { return o instanceof RawTypeBindingWarning w; @@ -14,10 +14,6 @@ public class RawTypeBindingWarning { case RawTypeBindingWarning w -> {} default -> {} } - switch (o) { - case (RawTypeBindingWarning w) -> {} - default -> {} - } switch (o) { case RawTypeBindingWarning w when w == null -> {} default -> {} diff --git a/test/langtools/tools/javac/patterns/RawTypeBindingWarning.out b/test/langtools/tools/javac/patterns/RawTypeBindingWarning.out index ddbcda7c0ef..f46c2c32c1a 100644 --- a/test/langtools/tools/javac/patterns/RawTypeBindingWarning.out +++ b/test/langtools/tools/javac/patterns/RawTypeBindingWarning.out @@ -1,7 +1,4 @@ RawTypeBindingWarning.java:10:29: compiler.warn.raw.class.use: RawTypeBindingWarning, RawTypeBindingWarning RawTypeBindingWarning.java:14:18: compiler.warn.raw.class.use: RawTypeBindingWarning, RawTypeBindingWarning -RawTypeBindingWarning.java:18:19: compiler.warn.raw.class.use: RawTypeBindingWarning, RawTypeBindingWarning -RawTypeBindingWarning.java:22:18: compiler.warn.raw.class.use: RawTypeBindingWarning, RawTypeBindingWarning -- compiler.note.preview.filename: RawTypeBindingWarning.java, DEFAULT -- compiler.note.preview.recompile -4 warnings \ No newline at end of file +RawTypeBindingWarning.java:18:18: compiler.warn.raw.class.use: RawTypeBindingWarning, RawTypeBindingWarning +3 warnings diff --git a/test/langtools/tools/javac/patterns/SealedTypeChanges.java b/test/langtools/tools/javac/patterns/SealedTypeChanges.java index 1c995e3d94f..f6eb8c22e6e 100644 --- a/test/langtools/tools/javac/patterns/SealedTypeChanges.java +++ b/test/langtools/tools/javac/patterns/SealedTypeChanges.java @@ -25,7 +25,6 @@ * @test * @bug 8262891 8270151 * @summary Verify pattern switches work properly when the set of sealed types changes. - * @enablePreview * @compile SealedTypeChanges.java * @compile SealedTypeChanges2.java * @run main SealedTypeChanges diff --git a/test/langtools/tools/javac/patterns/SimpleAndGuardPattern.java b/test/langtools/tools/javac/patterns/SimpleAndGuardPattern.java index 5e3be45ab37..3a97601a520 100644 --- a/test/langtools/tools/javac/patterns/SimpleAndGuardPattern.java +++ b/test/langtools/tools/javac/patterns/SimpleAndGuardPattern.java @@ -23,7 +23,6 @@ /** * @test - * @enablePreview * @compile -doe SimpleAndGuardPattern.java * @run main SimpleAndGuardPattern */ diff --git a/test/langtools/tools/javac/patterns/SimpleDeconstructionPattern.java b/test/langtools/tools/javac/patterns/SimpleDeconstructionPattern.java index eb222d09bd9..7b086d09d75 100644 --- a/test/langtools/tools/javac/patterns/SimpleDeconstructionPattern.java +++ b/test/langtools/tools/javac/patterns/SimpleDeconstructionPattern.java @@ -1,8 +1,8 @@ /** * @test - * @compile/fail/ref=SimpleDeconstructionPatternNoPreview.out -XDrawDiagnostics SimpleDeconstructionPattern.java - * @compile --enable-preview -source ${jdk.version} SimpleDeconstructionPattern.java - * @run main/othervm --enable-preview SimpleDeconstructionPattern + * @compile/fail/ref=SimpleDeconstructionPatternOld.out --release 20 -XDrawDiagnostics SimpleDeconstructionPattern.java + * @compile SimpleDeconstructionPattern.java + * @run main SimpleDeconstructionPattern */ import java.util.ArrayList; diff --git a/test/langtools/tools/javac/patterns/SimpleDeconstructionPatternNoPreview.out b/test/langtools/tools/javac/patterns/SimpleDeconstructionPatternNoPreview.out deleted file mode 100644 index c808a37064d..00000000000 --- a/test/langtools/tools/javac/patterns/SimpleDeconstructionPatternNoPreview.out +++ /dev/null @@ -1,2 +0,0 @@ -SimpleDeconstructionPattern.java:118:27: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.deconstruction.patterns) -1 error diff --git a/test/langtools/tools/javac/patterns/SimpleDeconstructionPatternOld.out b/test/langtools/tools/javac/patterns/SimpleDeconstructionPatternOld.out new file mode 100644 index 00000000000..c3e1301d799 --- /dev/null +++ b/test/langtools/tools/javac/patterns/SimpleDeconstructionPatternOld.out @@ -0,0 +1,2 @@ +SimpleDeconstructionPattern.java:118:27: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.deconstruction.patterns), 20, 21 +1 error diff --git a/test/langtools/tools/javac/patterns/SourceLevelChecks.java b/test/langtools/tools/javac/patterns/SourceLevelChecks.java index 2188d2ea46f..cca3bf311c5 100644 --- a/test/langtools/tools/javac/patterns/SourceLevelChecks.java +++ b/test/langtools/tools/javac/patterns/SourceLevelChecks.java @@ -46,8 +46,6 @@ import toolbox.ToolBox; public class SourceLevelChecks extends TestRunner { - private static final String JAVA_VERSION = System.getProperty("java.specification.version"); - ToolBox tb; public static void main(String... args) throws Exception { @@ -76,7 +74,7 @@ public class SourceLevelChecks extends TestRunner { } } """, - "Test.java:5:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)", + "Test.java:5:18: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.pattern.switch), 17, 21", "1 error"); } @@ -92,7 +90,7 @@ public class SourceLevelChecks extends TestRunner { } } """, - "Test.java:4:26: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)", + "Test.java:4:26: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.pattern.switch), 17, 21", "1 error"); } @@ -109,7 +107,7 @@ public class SourceLevelChecks extends TestRunner { } } """, - "Test.java:5:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)", + "Test.java:5:18: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.pattern.switch), 17, 21", "1 error"); } @@ -121,12 +119,12 @@ public class SourceLevelChecks extends TestRunner { public class Test { private void test(Integer i) { switch (i) { - case default: + case null, default: } } } """, - "Test.java:5:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)", + "Test.java:5:24: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.pattern.switch), 17, 21", "1 error"); } @@ -140,8 +138,7 @@ public class SourceLevelChecks extends TestRunner { var log = new JavacTask(tb) - .options("-source", "11", - "-Xlint:-options", + .options("--release", "17", "-XDrawDiagnostics") .outdir(classes) .files(tb.findJavaFiles(src)) diff --git a/test/langtools/tools/javac/patterns/SwitchErrors.java b/test/langtools/tools/javac/patterns/SwitchErrors.java index 6127e49e9e4..68dbe67d563 100644 --- a/test/langtools/tools/javac/patterns/SwitchErrors.java +++ b/test/langtools/tools/javac/patterns/SwitchErrors.java @@ -2,9 +2,9 @@ * @test /nodynamiccopyright/ * @bug 8262891 8269146 8269113 * @summary Verify errors related to pattern switches. - * @enablePreview * @compile/fail/ref=SwitchErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW SwitchErrors.java */ + public class SwitchErrors { void incompatibleSelectorObjectString(Object o) { switch (o) { @@ -189,10 +189,10 @@ public class SwitchErrors { break; } } - void test8269146a2(Integer i) { + void test8269146a2a(Integer i) { switch (i) { //error - illegal combination of pattern and constant: - case Integer o when o != null, 1: + case Integer o, 1: break; default: break; @@ -217,14 +217,14 @@ public class SwitchErrors { void test8269301a(Integer i) { switch (i) { //error - illegal combination of pattern, constant and default - case 1, Integer o when o != null, default: + case 1, Integer o, default: break; } } - void test8269301b(Integer i) { + void test8269301ba(Integer i) { switch (i) { //error - illegal combination of pattern, constant and default - case Integer o when o != null, 1, default: + case Integer o, 1, default: break; } } @@ -252,28 +252,28 @@ public class SwitchErrors { void nullAndParenthesized1(Object o) { record R(Object o) {} switch (o) { - case null, ((R r)): break; + case null, R r: break; default: break; } } void nullAndParenthesized2(Object o) { record R(Object o) {} switch (o) { - case null, ((R(var v))): break; + case null, R(var v): break; default: break; } } void nullAndParenthesized3(Object o) { record R(Object o) {} switch (o) { - case ((R r)): case null: break; + case R r: case null: break; default: break; } } void nullAndParenthesized4(Object o) { record R(Object o) {} switch (o) { - case ((R(var v))): case null: break; + case R(var v): case null: break; default: break; } } @@ -300,4 +300,27 @@ public class SwitchErrors { if (i instanceof R()) { } } + void test8269146a2b(Integer i) { + switch (i) { + //error - illegal combination of pattern and constant: + case Integer o when o != null, 1: + break; + default: + break; + } + } + void test8269301ab(Integer i) { + switch (i) { + //error - illegal combination of pattern, constant and default + case 1, Integer o when o != null, default: + break; + } + } + void test8269301bb(Integer i) { + switch (i) { + //error - illegal combination of pattern, constant and default + case Integer o when o != null, 1, default: + break; + } + } } diff --git a/test/langtools/tools/javac/patterns/SwitchErrors.out b/test/langtools/tools/javac/patterns/SwitchErrors.out index de0413f16a4..413b33a73d3 100644 --- a/test/langtools/tools/javac/patterns/SwitchErrors.out +++ b/test/langtools/tools/javac/patterns/SwitchErrors.out @@ -6,10 +6,13 @@ SwitchErrors.java:144:18: compiler.err.default.label.not.allowed SwitchErrors.java:149:18: compiler.err.default.label.not.allowed SwitchErrors.java:154:18: compiler.err.default.label.not.allowed SwitchErrors.java:213:29: compiler.err.default.label.not.allowed -SwitchErrors.java:220:47: compiler.err.default.label.not.allowed -SwitchErrors.java:227:47: compiler.err.default.label.not.allowed +SwitchErrors.java:220:32: compiler.err.default.label.not.allowed +SwitchErrors.java:227:32: compiler.err.default.label.not.allowed SwitchErrors.java:283:20: compiler.err.illegal.start.of.type SwitchErrors.java:286:28: compiler.err.illegal.start.of.type +SwitchErrors.java:306:42: compiler.err.expected2: :, -> +SwitchErrors.java:315:45: compiler.err.expected2: :, -> +SwitchErrors.java:322:42: compiler.err.expected2: :, -> SwitchErrors.java:11:18: compiler.err.constant.label.not.compatible: java.lang.String, java.lang.Object SwitchErrors.java:17:18: compiler.err.constant.label.not.compatible: int, java.lang.Object SwitchErrors.java:23:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer) @@ -41,10 +44,11 @@ SwitchErrors.java:167:18: compiler.err.pattern.expected SwitchErrors.java:173:78: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null) SwitchErrors.java:179:73: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null) SwitchErrors.java:186:21: compiler.err.invalid.case.label.combination -SwitchErrors.java:195:44: compiler.err.flows.through.from.pattern +SwitchErrors.java:197:13: compiler.err.unconditional.pattern.and.default +SwitchErrors.java:195:29: compiler.err.flows.through.from.pattern SwitchErrors.java:204:24: compiler.err.invalid.case.label.combination SwitchErrors.java:220:21: compiler.err.invalid.case.label.combination -SwitchErrors.java:227:44: compiler.err.flows.through.from.pattern +SwitchErrors.java:227:29: compiler.err.flows.through.from.pattern SwitchErrors.java:239:18: compiler.err.duplicate.unconditional.pattern SwitchErrors.java:244:18: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, java.lang.Integer) SwitchErrors.java:249:18: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array) @@ -55,6 +59,7 @@ SwitchErrors.java:276:18: compiler.err.flows.through.from.pattern SwitchErrors.java:292:49: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location: kindname.class, java.lang.Object, null) SwitchErrors.java:294:55: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location: kindname.class, java.lang.Object, null) SwitchErrors.java:300:26: compiler.err.pattern.type.cannot.infer +SwitchErrors.java:315:21: compiler.err.invalid.case.label.combination SwitchErrors.java:10:9: compiler.err.not.exhaustive.statement SwitchErrors.java:16:9: compiler.err.not.exhaustive.statement SwitchErrors.java:22:9: compiler.err.not.exhaustive.statement @@ -67,6 +72,4 @@ SwitchErrors.java:98:9: compiler.err.not.exhaustive.statement SwitchErrors.java:105:9: compiler.err.not.exhaustive.statement SwitchErrors.java:159:9: compiler.err.not.exhaustive.statement SwitchErrors.java:232:9: compiler.err.not.exhaustive.statement -- compiler.note.preview.filename: SwitchErrors.java, DEFAULT -- compiler.note.preview.recompile -69 errors +74 errors \ No newline at end of file diff --git a/test/langtools/tools/javac/patterns/Switches.java b/test/langtools/tools/javac/patterns/Switches.java index 1c7ae41274e..0c607f7e3ce 100644 --- a/test/langtools/tools/javac/patterns/Switches.java +++ b/test/langtools/tools/javac/patterns/Switches.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -31,7 +30,6 @@ import java.util.function.Function; * @test * @bug 8262891 8268333 8268896 8269802 8269808 8270151 8269113 8277864 8290709 * @summary Check behavior of pattern switches. - * @enablePreview */ public class Switches { @@ -53,10 +51,12 @@ public class Switches { runEnumTest(this::testEnumWithGuards2); runEnumTest(this::testEnumWithGuards3); runEnumTest(this::testEnumWithGuards4); + runEnumTest(this::testEnumWithGuards5); runEnumTest(this::testEnumWithGuardsExpression1); runEnumTest(this::testEnumWithGuardsExpression2); runEnumTest(this::testEnumWithGuardsExpression3); runEnumTest(this::testEnumWithGuardsExpression4); + runEnumTest(this::testEnumWithGuardsExpression5); runEnumTest(this::testStringWithGuards1); runEnumTest(this::testStringWithGuardsExpression1); runEnumTest(this::testIntegerWithGuards1); @@ -349,6 +349,28 @@ public class Switches { }; } + String testEnumWithGuards5(Object e) { + switch (e) { + case E.A: return "a"; + case E.B: return "b"; + case Runnable x when "C".equals(x.toString()): return "C"; + case E x: return e == E.C ? "broken" : String.valueOf(x); + case null: return "null"; + default: throw new AssertionError("Unexpected case!"); + } + } + + String testEnumWithGuardsExpression5(Object e) { + return switch (e) { + case E.A -> "a"; + case E.B -> "b"; + case Runnable x when "C".equals(x.toString()) -> "C"; + case E x -> e == E.C ? "broken" : String.valueOf(x); + case null -> "null"; + default -> throw new AssertionError("Unexpected case!"); + }; + } + String testStringWithGuards1(E e) { switch (e != null ? e.name() : null) { case "A": return "a"; diff --git a/test/langtools/tools/javac/patterns/T8291657.java b/test/langtools/tools/javac/patterns/T8291657.java index 03b59d276bf..cecd705a8fb 100644 --- a/test/langtools/tools/javac/patterns/T8291657.java +++ b/test/langtools/tools/javac/patterns/T8291657.java @@ -24,7 +24,6 @@ * @test * @bug 8291657 * @summary Javac assertion when compiling a method call with switch expression as argument - * @enablePreview * @compile T8291657.java */ public class T8291657 { diff --git a/test/langtools/tools/javac/patterns/TranslationTest.java b/test/langtools/tools/javac/patterns/TranslationTest.java index f77b04fc9a3..b509b2be3f9 100644 --- a/test/langtools/tools/javac/patterns/TranslationTest.java +++ b/test/langtools/tools/javac/patterns/TranslationTest.java @@ -62,8 +62,6 @@ import toolbox.ToolBox; public class TranslationTest extends TestRunner { - private static final String JAVA_VERSION = System.getProperty("java.specification.version"); - ToolBox tb; public static void main(String... args) throws Exception { @@ -205,8 +203,6 @@ public class TranslationTest extends TestRunner { } new JavacTask(tb) - .options("--enable-preview", - "-source", JAVA_VERSION) .outdir(libClasses) .files(tb.findJavaFiles(libSrc)) .run(); @@ -222,9 +218,7 @@ public class TranslationTest extends TestRunner { List output = new ArrayList<>(); new JavacTask(tb) - .options("--enable-preview", - "-source", JAVA_VERSION, - "-Xlint:-preview", + .options("-Xlint:-preview", "--class-path", libClasses.toString(), "-XDshould-stop.at=FLOW") .outdir(classes) diff --git a/test/langtools/tools/javac/patterns/TypedDeconstructionPatternExc.java b/test/langtools/tools/javac/patterns/TypedDeconstructionPatternExc.java index 8dcae4e6f89..bdfcb2de5c7 100644 --- a/test/langtools/tools/javac/patterns/TypedDeconstructionPatternExc.java +++ b/test/langtools/tools/javac/patterns/TypedDeconstructionPatternExc.java @@ -23,7 +23,6 @@ /** * @test - * @enablePreview */ import java.util.Objects; diff --git a/test/langtools/tools/javac/patterns/VarErrors.java b/test/langtools/tools/javac/patterns/VarErrors.java index d5ac4618203..c773e7c91d1 100644 --- a/test/langtools/tools/javac/patterns/VarErrors.java +++ b/test/langtools/tools/javac/patterns/VarErrors.java @@ -1,9 +1,9 @@ /* * @test /nodynamiccopyright/ * @summary Verify errors related to var patterns - * @enablePreview * @compile/fail/ref=VarErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW -XDdev VarErrors.java */ + public class VarErrors { void testIf(CharSequence cs) { if (cs instanceof var v) {} diff --git a/test/langtools/tools/javac/patterns/VarErrors.out b/test/langtools/tools/javac/patterns/VarErrors.out index 61e6efd8bc3..fb7a8caa982 100644 --- a/test/langtools/tools/javac/patterns/VarErrors.out +++ b/test/langtools/tools/javac/patterns/VarErrors.out @@ -1,6 +1,4 @@ VarErrors.java:9:27: compiler.err.restricted.type.not.allowed.here: var VarErrors.java:13:18: compiler.err.restricted.type.not.allowed.here: var VarErrors.java:18:18: compiler.err.restricted.type.not.allowed.here: var -- compiler.note.preview.filename: VarErrors.java, DEFAULT -- compiler.note.preview.recompile 3 errors diff --git a/test/langtools/tools/javac/sealed/MissingPermittedSubtypes.java b/test/langtools/tools/javac/sealed/MissingPermittedSubtypes.java index ba75407099a..2f69c284c4f 100644 --- a/test/langtools/tools/javac/sealed/MissingPermittedSubtypes.java +++ b/test/langtools/tools/javac/sealed/MissingPermittedSubtypes.java @@ -93,8 +93,6 @@ public class MissingPermittedSubtypes extends TestRunner { Files.createDirectories(libClasses); new JavacTask(tb) - .options("--enable-preview", - "-source", JAVA_VERSION) .outdir(libClasses) .files(tb.findJavaFiles(libSrc)) .run(); @@ -129,10 +127,7 @@ public class MissingPermittedSubtypes extends TestRunner { var log = new JavacTask(tb) - .options("--enable-preview", - "-source", JAVA_VERSION, - "-XDrawDiagnostics", - "-Xlint:-preview", + .options("-XDrawDiagnostics", "--class-path", libClasses.toString()) .outdir(classes1) .files(tb.findJavaFiles(src1)) @@ -143,8 +138,6 @@ public class MissingPermittedSubtypes extends TestRunner { List expectedErrors = List.of( "Test1.java:5:24: compiler.err.cant.access: lib.B1, (compiler.misc.class.file.not.found: lib.B1)", "Test1.java:8:29: compiler.err.cant.ref.non.effectively.final.var: obj, (compiler.misc.lambda)", - "- compiler.note.preview.filename: Test1.java, DEFAULT", - "- compiler.note.preview.recompile", "2 errors"); if (!expectedErrors.equals(log)) { @@ -173,10 +166,7 @@ public class MissingPermittedSubtypes extends TestRunner { var log = new JavacTask(tb) - .options("--enable-preview", - "-source", JAVA_VERSION, - "-XDrawDiagnostics", - "-Xlint:-preview", + .options("-XDrawDiagnostics", "--class-path", libClasses.toString()) .outdir(classes2) .files(tb.findJavaFiles(src2)) diff --git a/test/langtools/tools/javac/switchexpr/ExhaustiveEnumSwitch.java b/test/langtools/tools/javac/switchexpr/ExhaustiveEnumSwitch.java index c45dfda4928..b2a4d7f6752 100644 --- a/test/langtools/tools/javac/switchexpr/ExhaustiveEnumSwitch.java +++ b/test/langtools/tools/javac/switchexpr/ExhaustiveEnumSwitch.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,24 +25,39 @@ * @test * @bug 8206986 8243548 * @summary Verify that an switch expression over enum can be exhaustive without default. + * @compile --release 20 ExhaustiveEnumSwitch.java + * @compile ExhaustiveEnumSwitchExtra.java + * @run main ExhaustiveEnumSwitch IncompatibleClassChangeError * @compile ExhaustiveEnumSwitch.java * @compile ExhaustiveEnumSwitchExtra.java - * @run main ExhaustiveEnumSwitch + * @run main ExhaustiveEnumSwitch MatchException */ public class ExhaustiveEnumSwitch { - public static void main(String... args) { - new ExhaustiveEnumSwitch().run(); + public static void main(String... args) throws ClassNotFoundException { + boolean matchException = "MatchException".equals(args[0]); + new ExhaustiveEnumSwitch().run(matchException); } - private void run() { + private void run(boolean matchException) throws ClassNotFoundException { ExhaustiveEnumSwitchEnum v = ExhaustiveEnumSwitchEnum.valueOf("F"); try { print(v); throw new AssertionError("Expected exception did not occur."); } catch (IncompatibleClassChangeError err) { - //ok + if (matchException) { + throw new AssertionError("Expected IncompatibleClassChangeError, but got MatchException!"); + } + } catch (Exception ex) { + //cannot refer to MatchException directly, as it used to be preview API in JDK 20: + if (ex.getClass() == Class.forName("java.lang.MatchException")) { + if (!matchException) { + throw new AssertionError("Expected MatchException, but got IncompatibleClassChangeError!"); + } + } else { + throw ex; + } } } diff --git a/test/langtools/tools/javac/switchexpr/SwitchExpressionNoValue.java b/test/langtools/tools/javac/switchexpr/SwitchExpressionNoValue.java index 7d0e1b1d099..dc18e3b48fc 100644 --- a/test/langtools/tools/javac/switchexpr/SwitchExpressionNoValue.java +++ b/test/langtools/tools/javac/switchexpr/SwitchExpressionNoValue.java @@ -97,10 +97,7 @@ public class SwitchExpressionNoValue extends ComboInstance expression; case "CONTEXT" -> context; default -> throw new UnsupportedOperationException(pname); - }) - .withOption("--enable-preview") - .withOption("-source") - .withOption(String.valueOf(Runtime.version().feature())); + }); task.generate(result -> { try { diff --git a/test/langtools/tools/javac/switchextra/EnumSwitchQualified.java b/test/langtools/tools/javac/switchextra/EnumSwitchQualified.java new file mode 100644 index 00000000000..390e4926db3 --- /dev/null +++ b/test/langtools/tools/javac/switchextra/EnumSwitchQualified.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8300543 + * @summary Check switches work correctly with qualified enum constants + * @compile EnumSwitchQualified.java + * @run main EnumSwitchQualified +*/ + +import java.util.Objects; + +public class EnumSwitchQualified { + + public static void main(String... args) { + new EnumSwitchQualified().run(); + } + + void run() { + assertEquals(1, testPatternMatchingSwitch1(E1.A)); + assertEquals(2, testPatternMatchingSwitch1(E1.B)); + assertEquals(3, testPatternMatchingSwitch1(E1.C)); + assertEquals(4, testPatternMatchingSwitch1(E2.B)); + assertEquals(5, testPatternMatchingSwitch1(E2.C)); + assertEquals(6, testPatternMatchingSwitch1(E2.D)); + + assertEquals(1, testPatternMatchingSwitch2(E1.A)); + assertEquals(2, testPatternMatchingSwitch2(E1.B)); + assertEquals(3, testPatternMatchingSwitch2(E1.C)); + + assertEquals(1, testPatternMatchingSwitch3(E1.A)); + assertEquals(2, testPatternMatchingSwitch3(E1.B)); + assertEquals(3, testPatternMatchingSwitch3(E1.C)); + assertEquals(4, testPatternMatchingSwitch3(E2.B)); + assertEquals(5, testPatternMatchingSwitch3(E2.C)); + assertEquals(6, testPatternMatchingSwitch3(E2.D)); + assertEquals(7, testPatternMatchingSwitch3("")); + } + + int testPatternMatchingSwitch1(I i) { + return switch(i) { + case E1.A -> 1; + case E1.B -> 2; + case E1.C -> 3; + case E2.B -> 4; + case E2.C -> 5; + case E2.D -> 6; + }; + } + + int testPatternMatchingSwitch2(E1 e) { + return switch(e) { + case E1.A -> 1; + case E1.B -> 2; + case E1.C -> 3; + }; + } + + int testPatternMatchingSwitch3(Object o) { + return switch(o) { + case E1.A -> 1; + case E1.B -> 2; + case E1.C -> 3; + case E2.B -> 4; + case E2.C -> 5; + case E2.D -> 6; + default -> 7; + }; + } + + private void assertEquals(Object expected, Object actual) { + if (!Objects.equals(expected, actual)) { + throw new AssertionError("Incorrect result, expected: " + expected + + ", actual: " + actual); + } + } + + sealed interface I {} + enum E1 implements I { A, B, C; } + enum E2 implements I { B, C, D; } + +} diff --git a/test/langtools/tools/javac/T8290379.java b/test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.java similarity index 56% rename from test/langtools/tools/javac/T8290379.java rename to test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.java index 3f3ee8b5510..11767d4a1e8 100644 --- a/test/langtools/tools/javac/T8290379.java +++ b/test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,32 +20,39 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -/* + +/** * @test - * @bug 8290379 - * @summary Parse error with parenthesized pattern and guard using an array - * @enablePreview - */ -public class T8290379 { - public static void main(String... args) { - assertEquals(0, test("test")); - assertEquals(1, test(Integer.valueOf(42))); - } + * @bug 8300543 + * @summary Check switches work correctly with qualified enum constants + * @compile/fail/ref=EnumSwitchQualifiedErrors.out -XDrawDiagnostics EnumSwitchQualifiedErrors.java +*/ - public static int test(Object o) - { - int[] arr = {0, 1}; +public class EnumSwitchQualifiedErrors { - return switch (o) { - case (String s) when (arr[0] == 0) -> 0; - case (Integer i) when arr[1] == 1 -> 1; - default -> 2; + int testPatternMatchingSwitch1(I i) { + return switch(i) { + case E1.A -> 1; + case E2.A -> 2; }; } - static void assertEquals(int expected, int actual) { - if (expected != actual) { - throw new AssertionError("Expected: " + expected + ", actual: " + actual); - } + int testPatternMatchingSwitch2(E1 e) { + return switch(e) { + case E1.A -> 1; + case E2.A -> 4; + }; } + + int testPatternMatchingSwitch3(Number n) { + return switch(n) { + case E1.A -> 1; + case E2.A -> 2; + }; + } + + sealed interface I {} + enum E1 implements I { A; } + enum E2 { A; } + } diff --git a/test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.out b/test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.out new file mode 100644 index 00000000000..ff23d531c75 --- /dev/null +++ b/test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.out @@ -0,0 +1,5 @@ +EnumSwitchQualifiedErrors.java:36:20: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: EnumSwitchQualifiedErrors.E2, EnumSwitchQualifiedErrors.I) +EnumSwitchQualifiedErrors.java:43:20: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: EnumSwitchQualifiedErrors.E2, EnumSwitchQualifiedErrors.E1) +EnumSwitchQualifiedErrors.java:49:20: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: EnumSwitchQualifiedErrors.E1, java.lang.Number) +EnumSwitchQualifiedErrors.java:50:20: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: EnumSwitchQualifiedErrors.E2, java.lang.Number) +4 errors diff --git a/test/langtools/tools/javac/switchextra/RuleParsingTest.java b/test/langtools/tools/javac/switchextra/RuleParsingTest.java index 491c653618f..26182b44f48 100644 --- a/test/langtools/tools/javac/switchextra/RuleParsingTest.java +++ b/test/langtools/tools/javac/switchextra/RuleParsingTest.java @@ -95,7 +95,7 @@ public class RuleParsingTest { String version = System.getProperty("java.specification.version"); StringWriter out = new StringWriter(); JavacTask ct = (JavacTask) tool.getTask(out, null, noErrors, - List.of("--enable-preview", "-source", version), null, + List.of(), null, Arrays.asList(new MyFileObject(code.toString()))); CompilationUnitTree cut = ct.parse().iterator().next(); Trees trees = Trees.instance(ct); diff --git a/test/langtools/tools/javac/switchextra/SwitchNoExtraTypes.out b/test/langtools/tools/javac/switchextra/SwitchNoExtraTypes.out index 0a0aadc2519..eb2e3eff4f6 100644 --- a/test/langtools/tools/javac/switchextra/SwitchNoExtraTypes.out +++ b/test/langtools/tools/javac/switchextra/SwitchNoExtraTypes.out @@ -1,6 +1,5 @@ -SwitchNoExtraTypes.java:11:16: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch) SwitchNoExtraTypes.java:12:18: compiler.err.constant.label.not.compatible: boolean, boolean SwitchNoExtraTypes.java:18:18: compiler.err.constant.label.not.compatible: int, long SwitchNoExtraTypes.java:24:18: compiler.err.constant.label.not.compatible: int, float SwitchNoExtraTypes.java:30:18: compiler.err.constant.label.not.compatible: int, double -5 errors +4 errors diff --git a/test/langtools/tools/javac/switchextra/SwitchObject.out b/test/langtools/tools/javac/switchextra/SwitchObject.out index 9fa56d0cde5..b4068573036 100644 --- a/test/langtools/tools/javac/switchextra/SwitchObject.out +++ b/test/langtools/tools/javac/switchextra/SwitchObject.out @@ -1,4 +1,3 @@ -SwitchObject.java:10:16: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch) SwitchObject.java:11:18: compiler.err.constant.label.not.compatible: int, java.lang.Object SwitchObject.java:12:18: compiler.err.constant.label.not.compatible: java.lang.String, java.lang.Object -3 errors +2 errors diff --git a/test/langtools/tools/javac/switchnull/SwitchNull.java b/test/langtools/tools/javac/switchnull/SwitchNull.java index 79e651348f1..6305c4052b6 100644 --- a/test/langtools/tools/javac/switchnull/SwitchNull.java +++ b/test/langtools/tools/javac/switchnull/SwitchNull.java @@ -25,7 +25,6 @@ * @test * @bug 8262891 * @summary Verify "case null" behavior. - * @enablePreview */ public class SwitchNull { diff --git a/test/langtools/tools/javac/switchnull/SwitchNullDisabled-preview.out b/test/langtools/tools/javac/switchnull/SwitchNullDisabled-preview.out deleted file mode 100644 index d8bf355a56c..00000000000 --- a/test/langtools/tools/javac/switchnull/SwitchNullDisabled-preview.out +++ /dev/null @@ -1,2 +0,0 @@ -SwitchNullDisabled.java:13:18: compiler.err.preview.feature.disabled: (compiler.misc.feature.case.null) -1 error diff --git a/test/langtools/tools/javac/switchnull/SwitchNullDisabled.java b/test/langtools/tools/javac/switchnull/SwitchNullDisabled.java index 1f9b0e065cf..5976d2e9d8f 100644 --- a/test/langtools/tools/javac/switchnull/SwitchNullDisabled.java +++ b/test/langtools/tools/javac/switchnull/SwitchNullDisabled.java @@ -1,10 +1,9 @@ /* * @test /nodynamiccopyright/ * @bug 8206986 - * @summary Verify "case null" is not allowed for --release 16 - * @compile/fail/ref=SwitchNullDisabled.out -XDrawDiagnostics --release 16 SwitchNullDisabled.java - * @compile/fail/ref=SwitchNullDisabled-preview.out -XDrawDiagnostics SwitchNullDisabled.java - * @compile --enable-preview -source ${jdk.version} SwitchNullDisabled.java + * @summary Verify "case null" is not allowed for --release 16, 20 + * @compile/fail/ref=SwitchNullDisabled.out -XDrawDiagnostics --release 20 SwitchNullDisabled.java + * @compile SwitchNullDisabled.java */ public class SwitchNullDisabled { diff --git a/test/langtools/tools/javac/switchnull/SwitchNullDisabled.out b/test/langtools/tools/javac/switchnull/SwitchNullDisabled.out index d8bf355a56c..67260c6ff79 100644 --- a/test/langtools/tools/javac/switchnull/SwitchNullDisabled.out +++ b/test/langtools/tools/javac/switchnull/SwitchNullDisabled.out @@ -1,2 +1,2 @@ -SwitchNullDisabled.java:13:18: compiler.err.preview.feature.disabled: (compiler.misc.feature.case.null) +SwitchNullDisabled.java:12:18: compiler.err.feature.not.supported.in.source: (compiler.misc.feature.case.null), 20, 21 1 error diff --git a/test/langtools/tools/lib/toolbox/TestRunner.java b/test/langtools/tools/lib/toolbox/TestRunner.java index cdc63f635ac..e93370a6e33 100644 --- a/test/langtools/tools/lib/toolbox/TestRunner.java +++ b/test/langtools/tools/lib/toolbox/TestRunner.java @@ -79,21 +79,24 @@ public abstract class TestRunner { * @throws java.lang.Exception if any errors occur */ protected void runTests(Function f) throws Exception { + String testQuery = System.getProperty("test.query"); for (Method m : getClass().getDeclaredMethods()) { Annotation a = m.getAnnotation(Test.class); if (a != null) { testName = m.getName(); - try { - testCount++; - out.println("test: " + testName); - m.invoke(this, f.apply(m)); - } catch (InvocationTargetException e) { - errorCount++; - Throwable cause = e.getCause(); - out.println("Exception running test " + testName + ": " + e.getCause()); - cause.printStackTrace(out); + if (testQuery == null || testQuery.equals(testName)) { + try { + testCount++; + out.println("test: " + testName); + m.invoke(this, f.apply(m)); + } catch (InvocationTargetException e) { + errorCount++; + Throwable cause = e.getCause(); + out.println("Exception running test " + testName + ": " + e.getCause()); + cause.printStackTrace(out); + } + out.println(); } - out.println(); } }