8300543: Compiler Implementation for Pattern Matching for switch

8300545: Compiler Implementation for Record Patterns

Co-authored-by: Aggelos Biboudis <abimpoudis@openjdk.org>
Reviewed-by: vromero, mcimadamore
This commit is contained in:
Jan Lahoda 2023-05-22 04:24:06 +00:00
parent 5ccc962942
commit eaa80ad08c
157 changed files with 2591 additions and 2233 deletions

View File

@ -25,48 +25,57 @@
package java.lang; package java.lang;
import jdk.internal.javac.PreviewFeature;
/** /**
* Thrown to indicate an unexpected failure in pattern matching. * Thrown to indicate an unexpected failure in pattern matching.
* *
* <p>{@code MatchException} may be thrown when an exhaustive pattern matching language construct * <p>{@code MatchException} may be thrown when an exhaustive pattern matching
* (such as a switch expression) encounters a value that does not match any of the provided * language construct (such as a {@code switch} expression) encounters a value
* patterns at runtime. This can arise from a number of cases: * that does not match any of the specified patterns at run time, even though
* the construct has been deemed exhaustive. This is intentional and can arise
* from a number of cases:
*
* <ul> * <ul>
* <li>Separate compilation anomalies, where a sealed interface has a different set of permitted * <li>Separate compilation anomalies, where parts of the type hierarchy that
* subtypes at runtime than it had at compilation time, an enum has a different set of * the patterns reference have been changed, but the pattern matching
* constants at runtime than it had at compilation time, or the type hierarchy has changed * construct has not been recompiled. For example, if a sealed interface
* in incompatible ways between compile time and run time.</li> * has a different set of permitted subtypes at run time than it had at
* <li>{@code null} values and nested patterns using sealed types. If an interface or abstract * compile time, or if an enum class has a different set of enum constants
* class {@code C} is sealed to permit {@code A} and {@code B}, then the set of record * at runtime than it had at compile time, or if the type hierarchy has
* patterns {@code R(A a)} and {@code R(B b)} are exhaustive on a record {@code R} whose * been changed in some incompatible way between compile time and run time.</li>
* sole component is of type {@code C}, but neither of these patterns will match *
* {@code new R(null)}.</li> * <li>{@code null} values and nested patterns involving sealed classes. If,
* <li>Null targets and nested record patterns. Given a record type {@code R} whose sole * for example, an interface {@code I} is {@code sealed} with two permitted
* component is {@code S}, which in turn is a record whose sole component is {@code String}, * subclasses {@code A} and {@code B}, and a record class {@code R} has a
* then the nested record pattern {@code R(S(String s))} will not match {@code new R(null)}.</li> * single component of type {@code I}, then the two record patterns {@code
* R(A a)} and {@code R(B b)} together are considered to be exhaustive for
* the type {@code R}, but neither of these patterns will match against the
* result of {@code new R(null)}.</li>
*
* <li>{@code null} values and nested record patterns. Given a record class
* {@code S} with a single component of type {@code T}, where {@code T} is
* another record class with a single component of type {@code String},
* then the nested record pattern {@code R(S(var s))} is considered
* exhaustive for the type {@code R} but it does not match against the
* result of {@code new R(null)} (whereas it does match against the result
* of {@code new R(new S(null))} does).</li>
* </ul> * </ul>
* *
* <p>Match failures arising from unexpected inputs will generally throw {@code MatchException} only * <p>{@code MatchException} may also be thrown by the process of pattern matching
* after all patterns have been tried; even if {@code R(S(String s))} does not match * a value against a pattern. For example, pattern matching involving a record
* {@code new R(null)}, a later pattern (such as {@code R r}) may still match the target. * pattern may require accessor methods to be implicitly invoked in order to
* extract the component values. If any of these accessor methods throws an
* exception, pattern matching completes abruptly and throws {@code
* MatchException}. The original exception will be set as a {@link
* Throwable#getCause() cause} of the {@code MatchException}. No {@link
* Throwable#addSuppressed(java.lang.Throwable) suppressed} exceptions will be
* recorded.
* *
* <p>MatchException may also be thrown when operations performed as part of pattern matching throw * @jls 14.11.3 Execution of a {@code switch} Statement
* an unexpected exception. For example, pattern matching may cause methods such as record component
* accessors to be implicitly invoked in order to extract pattern bindings. If these methods throw
* an exception, execution of the pattern matching construct may fail with {@code MatchException}.
* The original exception will be set as a {@link Throwable#getCause() cause} of
* the {@code MatchException}. No {@link Throwable#addSuppressed(java.lang.Throwable) suppressed}
* exceptions will be recorded.
*
* @jls 14.11.3 Execution of a switch Statement
* @jls 14.30.2 Pattern Matching * @jls 14.30.2 Pattern Matching
* @jls 15.28.2 Run-Time Evaluation of switch Expressions * @jls 15.28.2 Run-Time Evaluation of {@code switch} Expressions
* *
* @since 19 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING)
public final class MatchException extends RuntimeException { public final class MatchException extends RuntimeException {
@java.io.Serial @java.io.Serial
private static final long serialVersionUID = 0L; private static final long serialVersionUID = 0L;

View File

@ -25,17 +25,18 @@
package java.lang.runtime; package java.lang.runtime;
import java.lang.Enum.EnumDesc;
import java.lang.invoke.CallSite; import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantBootstraps; import java.lang.invoke.ConstantBootstraps;
import java.lang.invoke.ConstantCallSite; import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.util.Objects;
import java.util.stream.Stream; import java.util.stream.Stream;
import jdk.internal.javac.PreviewFeature;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import jdk.internal.vm.annotation.Stable;
/** /**
* Bootstrap methods for linking {@code invokedynamic} call sites that implement * Bootstrap methods for linking {@code invokedynamic} call sites that implement
@ -43,9 +44,8 @@ import static java.util.Objects.requireNonNull;
* take additional static arguments corresponding to the {@code case} labels * take additional static arguments corresponding to the {@code case} labels
* of the {@code switch}, implicitly numbered sequentially from {@code [0..N)}. * of the {@code switch}, implicitly numbered sequentially from {@code [0..N)}.
* *
* @since 17 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING)
public class SwitchBootstraps { public class SwitchBootstraps {
private SwitchBootstraps() {} private SwitchBootstraps() {}
@ -60,7 +60,8 @@ public class SwitchBootstraps {
DO_TYPE_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "doTypeSwitch", DO_TYPE_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "doTypeSwitch",
MethodType.methodType(int.class, Object.class, int.class, Object[].class)); MethodType.methodType(int.class, Object.class, int.class, Object[].class));
DO_ENUM_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "doEnumSwitch", DO_ENUM_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "doEnumSwitch",
MethodType.methodType(int.class, Enum.class, int.class, Object[].class)); MethodType.methodType(int.class, Enum.class, int.class, Object[].class,
MethodHandles.Lookup.class, Class.class, ResolvedEnumLabels.class));
} }
catch (ReflectiveOperationException e) { catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e); throw new ExceptionInInitializerError(e);
@ -71,7 +72,7 @@ public class SwitchBootstraps {
* Bootstrap method for linking an {@code invokedynamic} call site that * Bootstrap method for linking an {@code invokedynamic} call site that
* implements a {@code switch} on a target of a reference type. The static * implements a {@code switch} on a target of a reference type. The static
* arguments are an array of case labels which must be non-null and of type * arguments are an array of case labels which must be non-null and of type
* {@code String} or {@code Integer} or {@code Class}. * {@code String} or {@code Integer} or {@code Class} or {@code EnumDesc}.
* <p> * <p>
* The type of the returned {@code CallSite}'s method handle will have * The type of the returned {@code CallSite}'s method handle will have
* a return type of {@code int}. It has two parameters: the first argument * a return type of {@code int}. It has two parameters: the first argument
@ -89,10 +90,16 @@ public class SwitchBootstraps {
* from the target's class; or</li> * from the target's class; or</li>
* <li>the element is of type {@code String} or {@code Integer} and * <li>the element is of type {@code String} or {@code Integer} and
* equals to the target.</li> * equals to the target.</li>
* <li>the element is of type {@code EnumDesc}, that describes a constant that is
* equals to the target.</li>
* </ul> * </ul>
* <p> * <p>
* If no element in the {@code labels} array matches the target, then * If no element in the {@code labels} array matches the target, then
* the method of the call site return the length of the {@code labels} array. * the method of the call site return the length of the {@code labels} array.
* <p>
* The value of the {@code restart} index must be between {@code 0} (inclusive) and
* the length of the {@code labels} array (inclusive),
* both or an {@link IndexOutOfBoundsException} is thrown.
* *
* @param lookup Represents a lookup context with the accessibility * @param lookup Represents a lookup context with the accessibility
* privileges of the caller. When used with {@code invokedynamic}, * privileges of the caller. When used with {@code invokedynamic},
@ -101,7 +108,7 @@ public class SwitchBootstraps {
* @param invocationType The invocation type of the {@code CallSite} with two parameters, * @param invocationType The invocation type of the {@code CallSite} with two parameters,
* a reference type, an {@code int}, and {@code int} as a return type. * a reference type, an {@code int}, and {@code int} as a return type.
* @param labels case labels - {@code String} and {@code Integer} constants * @param labels case labels - {@code String} and {@code Integer} constants
* and {@code Class} instances, in any combination * and {@code Class} and {@code EnumDesc} instances, in any combination
* @return a {@code CallSite} returning the first matching element as described above * @return a {@code CallSite} returning the first matching element as described above
* *
* @throws NullPointerException if any argument is {@code null} * @throws NullPointerException if any argument is {@code null}
@ -109,7 +116,7 @@ public class SwitchBootstraps {
* invocation type is not not a method type of first parameter of a reference type, * invocation type is not not a method type of first parameter of a reference type,
* second parameter of type {@code int} and with {@code int} as its return type, * second parameter of type {@code int} and with {@code int} as its return type,
* or if {@code labels} contains an element that is not of type {@code String}, * or if {@code labels} contains an element that is not of type {@code String},
* {@code Integer} or {@code Class}. * {@code Integer}, {@code Class} or {@code EnumDesc}.
* @jvms 4.4.6 The CONSTANT_NameAndType_info Structure * @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
* @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures * @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
*/ */
@ -138,12 +145,15 @@ public class SwitchBootstraps {
Class<?> labelClass = label.getClass(); Class<?> labelClass = label.getClass();
if (labelClass != Class.class && if (labelClass != Class.class &&
labelClass != String.class && labelClass != String.class &&
labelClass != Integer.class) { labelClass != Integer.class &&
labelClass != EnumDesc.class) {
throw new IllegalArgumentException("label with illegal type found: " + label.getClass()); throw new IllegalArgumentException("label with illegal type found: " + label.getClass());
} }
} }
private static int doTypeSwitch(Object target, int startIndex, Object[] labels) { private static int doTypeSwitch(Object target, int startIndex, Object[] labels) {
Objects.checkIndex(startIndex, labels.length + 1);
if (target == null) if (target == null)
return -1; return -1;
@ -160,6 +170,11 @@ public class SwitchBootstraps {
} else if (target instanceof Character input && constant.intValue() == input.charValue()) { } else if (target instanceof Character input && constant.intValue() == input.charValue()) {
return i; return i;
} }
} else if (label instanceof EnumDesc<?> enumDesc) {
if (target.getClass().isEnum() &&
((Enum<?>) target).describeConstable().stream().anyMatch(d -> d.equals(enumDesc))) {
return i;
}
} else if (label.equals(target)) { } else if (label.equals(target)) {
return i; return i;
} }
@ -200,6 +215,10 @@ public class SwitchBootstraps {
* <p> * <p>
* If no element in the {@code labels} array matches the target, then * If no element in the {@code labels} array matches the target, then
* the method of the call site return the length of the {@code labels} array. * the method of the call site return the length of the {@code labels} array.
* <p>
* The value of the {@code restart} index must be between {@code 0} (inclusive) and
* the length of the {@code labels} array (inclusive),
* both or an {@link IndexOutOfBoundsException} is thrown.
* *
* @param lookup Represents a lookup context with the accessibility * @param lookup Represents a lookup context with the accessibility
* privileges of the caller. When used with {@code invokedynamic}, * privileges of the caller. When used with {@code invokedynamic},
@ -235,13 +254,28 @@ public class SwitchBootstraps {
labels = labels.clone(); labels = labels.clone();
Class<?> enumClass = invocationType.parameterType(0); Class<?> enumClass = invocationType.parameterType(0);
labels = Stream.of(labels).map(l -> convertEnumConstants(lookup, enumClass, l)).toArray(); Stream.of(labels).forEach(l -> validateEnumLabel(enumClass, l));
MethodHandle temporary =
MethodHandles.insertArguments(DO_ENUM_SWITCH, 2, labels, lookup, enumClass, new ResolvedEnumLabels());
temporary = temporary.asType(invocationType);
MethodHandle target = return new ConstantCallSite(temporary);
MethodHandles.insertArguments(DO_ENUM_SWITCH, 2, (Object) labels); }
target = target.asType(invocationType);
return new ConstantCallSite(target); private static <E extends Enum<E>> void validateEnumLabel(Class<?> enumClassTemplate, Object label) {
if (label == null) {
throw new IllegalArgumentException("null label found");
}
Class<?> labelClass = label.getClass();
if (labelClass == Class.class) {
if (label != enumClassTemplate) {
throw new IllegalArgumentException("the Class label: " + label +
", expected the provided enum class: " + enumClassTemplate);
}
} else if (labelClass != String.class) {
throw new IllegalArgumentException("label with illegal type found: " + labelClass +
", expected label of type either String or Class");
}
} }
private static <E extends Enum<E>> Object convertEnumConstants(MethodHandles.Lookup lookup, Class<?> enumClassTemplate, Object label) { private static <E extends Enum<E>> Object convertEnumConstants(MethodHandles.Lookup lookup, Class<?> enumClassTemplate, Object label) {
@ -269,10 +303,22 @@ public class SwitchBootstraps {
} }
} }
private static int doEnumSwitch(Enum<?> target, int startIndex, Object[] labels) { private static int doEnumSwitch(Enum<?> target, int startIndex, Object[] unresolvedLabels,
MethodHandles.Lookup lookup, Class<?> enumClass,
ResolvedEnumLabels resolvedLabels) {
Objects.checkIndex(startIndex, unresolvedLabels.length + 1);
if (target == null) if (target == null)
return -1; return -1;
if (resolvedLabels.resolvedLabels == null) {
resolvedLabels.resolvedLabels = Stream.of(unresolvedLabels)
.map(l -> convertEnumConstants(lookup, enumClass, l))
.toArray();
}
Object[] labels = resolvedLabels.resolvedLabels;
// Dumbest possible strategy // Dumbest possible strategy
Class<?> targetClass = target.getClass(); Class<?> targetClass = target.getClass();
for (int i = startIndex; i < labels.length; i++) { for (int i = startIndex; i < labels.length; i++) {
@ -288,4 +334,8 @@ public class SwitchBootstraps {
return labels.length; return labels.length;
} }
private static final class ResolvedEnumLabels {
@Stable
public Object[] resolvedLabels;
}
} }

View File

@ -480,12 +480,10 @@ public abstract class CallArranger {
StorageCalculator.StructStorage[] structStorages StorageCalculator.StructStorage[] structStorages
= storageCalculator.structStorages((GroupLayout) layout, forHFA); = storageCalculator.structStorages((GroupLayout) layout, forHFA);
for (StorageCalculator.StructStorage( for (StorageCalculator.StructStorage structStorage : structStorages) {
long offset, Class<?> ca, int byteWidth, VMStorage storage
) : structStorages) {
bindings.dup(); bindings.dup();
bindings.vmLoad(storage, ca) bindings.vmLoad(structStorage.storage(), structStorage.carrier())
.bufferStore(offset, ca, byteWidth); .bufferStore(structStorage.offset(), structStorage.carrier(), structStorage.byteWidth());
} }
} }
case STRUCT_REFERENCE -> { case STRUCT_REFERENCE -> {

View File

@ -64,10 +64,6 @@ public @interface PreviewFeature {
* Values should be annotated with the feature's {@code JEP}. * Values should be annotated with the feature's {@code JEP}.
*/ */
public enum Feature { public enum Feature {
@JEP(number=433, title="Pattern Matching for switch", status="Fourth Preview")
SWITCH_PATTERN_MATCHING(),
@JEP(number=432, title="Record Patterns", status="Second Preview")
RECORD_PATTERNS,
// not used // not used
VIRTUAL_THREADS, VIRTUAL_THREADS,
@JEP(number=442, title="Foreign Function & Memory API", status="Third Preview") @JEP(number=442, title="Foreign Function & Memory API", status="Third Preview")

View File

@ -25,12 +25,9 @@
package com.sun.source.tree; package com.sun.source.tree;
import jdk.internal.javac.PreviewFeature;
/** /**
* A marker interface for {@code Tree}s that may be used as {@link CaseTree} labels. * A marker interface for {@code Tree}s that may be used as {@link CaseTree} labels.
* *
* @since 17 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public interface CaseLabelTree extends Tree {} public interface CaseLabelTree extends Tree {}

View File

@ -27,8 +27,6 @@ package com.sun.source.tree;
import java.util.List; import java.util.List;
import jdk.internal.javac.PreviewFeature;
/** /**
* A tree node for a {@code case} in a {@code switch} statement or expression. * A tree node for a {@code case} in a {@code switch} statement or expression.
* *
@ -72,11 +70,18 @@ public interface CaseTree extends Tree {
* For {@code default} case return a list with a single element, {@link DefaultCaseLabelTree}. * For {@code default} case return a list with a single element, {@link DefaultCaseLabelTree}.
* *
* @return labels for this case * @return labels for this case
* @since 17 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
List<? extends CaseLabelTree> getLabels(); List<? extends CaseLabelTree> getLabels();
/**
* The guard for the case.
*
* @return the guard
* @since 21
*/
ExpressionTree getGuard();
/** /**
* For case with kind {@linkplain CaseKind#STATEMENT}, * For case with kind {@linkplain CaseKind#STATEMENT},
* returns the statements labeled by the case. * returns the statements labeled by the case.

View File

@ -25,13 +25,10 @@
package com.sun.source.tree; package com.sun.source.tree;
import jdk.internal.javac.PreviewFeature;
/** /**
* A case label element that refers to a constant expression * A case label element that refers to a constant expression
* @since 19 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public interface ConstantCaseLabelTree extends CaseLabelTree { public interface ConstantCaseLabelTree extends CaseLabelTree {
/** /**

View File

@ -26,14 +26,12 @@
package com.sun.source.tree; package com.sun.source.tree;
import java.util.List; import java.util.List;
import jdk.internal.javac.PreviewFeature;
/** /**
* A deconstruction pattern tree. * A deconstruction pattern tree.
* *
* @since 19 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
public interface DeconstructionPatternTree extends PatternTree { public interface DeconstructionPatternTree extends PatternTree {
/** /**

View File

@ -24,12 +24,9 @@
*/ */
package com.sun.source.tree; package com.sun.source.tree;
import jdk.internal.javac.PreviewFeature;
/** /**
* A case label that marks {@code default} in {@code case null, default}. * A case label that marks {@code default} in {@code case null, default}.
* *
* @since 17 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public interface DefaultCaseLabelTree extends CaseLabelTree {} public interface DefaultCaseLabelTree extends CaseLabelTree {}

View File

@ -25,8 +25,6 @@
package com.sun.source.tree; package com.sun.source.tree;
import jdk.internal.javac.PreviewFeature;
/** /**
* A tree node for an "enhanced" {@code for} loop statement. * A tree node for an "enhanced" {@code for} loop statement.
* *
@ -43,37 +41,12 @@ import jdk.internal.javac.PreviewFeature;
* @since 1.6 * @since 1.6
*/ */
public interface EnhancedForLoopTree extends StatementTree { public interface EnhancedForLoopTree extends StatementTree {
/**
* "Enhanced" {@code for} declarations come in two forms:
* <ul>
* <li> local variable declarations and
* <li> record patterns
* </ul>
*
* @since 20
*/
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
public enum DeclarationKind {
/** enum constant for local variable declarations */
VARIABLE,
/** enum constant for record pattern declarations */
PATTERN
}
/** /**
* Returns the control variable for the loop. * Returns the control variable for the loop.
* @return the control variable, or {@code null} if this "enhanced" {@code for} uses a pattern * @return the control variable
*/ */
VariableTree getVariable(); VariableTree getVariable();
/**
* Returns the control variable or pattern for the loop.
* @return the control variable or pattern
* @since 20
*/
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
Tree getVariableOrRecordPattern();
/** /**
* Returns the expression yielding the values for the control variable. * Returns the expression yielding the values for the control variable.
* @return the expression * @return the expression
@ -85,12 +58,4 @@ public interface EnhancedForLoopTree extends StatementTree {
* @return the body of the loop * @return the body of the loop
*/ */
StatementTree getStatement(); StatementTree getStatement();
/**
* Returns the kind of the declaration of the "enhanced" {@code for}.
* @return the kind of the declaration
* @since 20
*/
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
DeclarationKind getDeclarationKind();
} }

View File

@ -25,8 +25,6 @@
package com.sun.source.tree; package com.sun.source.tree;
import jdk.internal.javac.PreviewFeature;
/** /**
* A tree node for an {@code instanceof} expression. * A tree node for an {@code instanceof} expression.
* *
@ -43,22 +41,6 @@ import jdk.internal.javac.PreviewFeature;
*/ */
public interface InstanceOfTree extends ExpressionTree { public interface InstanceOfTree extends ExpressionTree {
/**
* Two possible variants of instanceof expressions:
* <ul>
* <li> testing types, and
* <li> performing pattern matching
* </ul>
* @since 20
*/
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
public enum TestKind {
/** instanceof only testing a type */
TYPE,
/** instanceof doing a pattern matching */
PATTERN
}
/** /**
* Returns the expression to be tested. * Returns the expression to be tested.
* @return the expression * @return the expression
@ -93,12 +75,4 @@ public interface InstanceOfTree extends ExpressionTree {
*/ */
PatternTree getPattern(); PatternTree getPattern();
/**
* Returns the kind of this instanceof expression.
*
* @return the kind of this instanceof expression
* @since 20
*/
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
TestKind getTestKind();
} }

View File

@ -1,49 +0,0 @@
/*
* Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.source.tree;
import jdk.internal.javac.PreviewFeature;
/**
* A tree node for a parenthesized pattern.
*
* For example:
* <pre>
* ( <em>pattern</em> )
* </pre>
*
* @jls 14.30.1 Kinds of Patterns
*
* @since 17
*/
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public interface ParenthesizedPatternTree extends PatternTree {
/**
* Returns the pattern within the parentheses.
* @return the pattern
*/
PatternTree getPattern();
}

View File

@ -25,13 +25,10 @@
package com.sun.source.tree; package com.sun.source.tree;
import jdk.internal.javac.PreviewFeature;
/** /**
* A case label element that refers to an expression * A case label element that refers to an expression
* @since 19 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public interface PatternCaseLabelTree extends CaseLabelTree { public interface PatternCaseLabelTree extends CaseLabelTree {
/** /**
@ -41,11 +38,4 @@ public interface PatternCaseLabelTree extends CaseLabelTree {
*/ */
public PatternTree getPattern(); public PatternTree getPattern();
/**
* The guard for the case.
*
* @return the guard
*/
ExpressionTree getGuard();
} }

View File

@ -234,44 +234,32 @@ public interface Tree {
*/ */
BINDING_PATTERN(BindingPatternTree.class), BINDING_PATTERN(BindingPatternTree.class),
/**
* Used for instances of {@link ParenthesizedPatternTree}.
*
* @since 17
*/
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
PARENTHESIZED_PATTERN(ParenthesizedPatternTree.class),
/** /**
* Used for instances of {@link DefaultCaseLabelTree}. * Used for instances of {@link DefaultCaseLabelTree}.
* *
* @since 17 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
DEFAULT_CASE_LABEL(DefaultCaseLabelTree.class), DEFAULT_CASE_LABEL(DefaultCaseLabelTree.class),
/** /**
* Used for instances of {@link ConstantCaseLabelTree}. * Used for instances of {@link ConstantCaseLabelTree}.
* *
* @since 19 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
CONSTANT_CASE_LABEL(ConstantCaseLabelTree.class), CONSTANT_CASE_LABEL(ConstantCaseLabelTree.class),
/** /**
* Used for instances of {@link PatternCaseLabelTree}. * Used for instances of {@link PatternCaseLabelTree}.
* *
* @since 19 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
PATTERN_CASE_LABEL(PatternCaseLabelTree.class), PATTERN_CASE_LABEL(PatternCaseLabelTree.class),
/** /**
* Used for instances of {@link DeconstructionPatternTree}. * Used for instances of {@link DeconstructionPatternTree}.
* *
* @since 19 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
DECONSTRUCTION_PATTERN(DeconstructionPatternTree.class), DECONSTRUCTION_PATTERN(DeconstructionPatternTree.class),
/** /**

View File

@ -282,9 +282,8 @@ public interface TreeVisitor<R,P> {
* @param node the node being visited * @param node the node being visited
* @param p a parameter value * @param p a parameter value
* @return a result value * @return a result value
* @since 17 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p); R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p);
/** /**
@ -292,9 +291,8 @@ public interface TreeVisitor<R,P> {
* @param node the node being visited * @param node the node being visited
* @param p a parameter value * @param p a parameter value
* @return a result value * @return a result value
* @since 19 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
R visitConstantCaseLabel(ConstantCaseLabelTree node, P p); R visitConstantCaseLabel(ConstantCaseLabelTree node, P p);
/** /**
@ -302,9 +300,8 @@ public interface TreeVisitor<R,P> {
* @param node the node being visited * @param node the node being visited
* @param p a parameter value * @param p a parameter value
* @return a result value * @return a result value
* @since 19 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
R visitPatternCaseLabel(PatternCaseLabelTree node, P p); R visitPatternCaseLabel(PatternCaseLabelTree node, P p);
/** /**
@ -312,9 +309,8 @@ public interface TreeVisitor<R,P> {
* @param node the node being visited * @param node the node being visited
* @param p a parameter value * @param p a parameter value
* @return a result value * @return a result value
* @since 19 * @since 21
*/ */
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
R visitDeconstructionPattern(DeconstructionPatternTree node, P p); R visitDeconstructionPattern(DeconstructionPatternTree node, P p);
/** /**
@ -341,16 +337,6 @@ public interface TreeVisitor<R,P> {
*/ */
R visitNewArray(NewArrayTree node, P p); R visitNewArray(NewArrayTree node, P p);
/**
* Visits a {@code ParenthesizedPatternTree} node.
* @param node the node being visited
* @param p a parameter value
* @return a result value
* @since 17
*/
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
R visitParenthesizedPattern(ParenthesizedPatternTree node, P p);
/** /**
* Visits a {@code NewClassTree} node. * Visits a {@code NewClassTree} node.
* @param node the node being visited * @param node the node being visited

View File

@ -664,10 +664,9 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
* @param node {@inheritDoc} * @param node {@inheritDoc}
* @param p {@inheritDoc} * @param p {@inheritDoc}
* @return the result of {@code defaultAction} * @return the result of {@code defaultAction}
* @since 17 * @since 21
*/ */
@Override @Override
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) { public R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) {
return defaultAction(node, p); return defaultAction(node, p);
} }
@ -680,10 +679,9 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
* @param node {@inheritDoc} * @param node {@inheritDoc}
* @param p {@inheritDoc} * @param p {@inheritDoc}
* @return the result of {@code defaultAction} * @return the result of {@code defaultAction}
* @since 19 * @since 21
*/ */
@Override @Override
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitConstantCaseLabel(ConstantCaseLabelTree node, P p) { public R visitConstantCaseLabel(ConstantCaseLabelTree node, P p) {
return defaultAction(node, p); return defaultAction(node, p);
} }
@ -696,10 +694,9 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
* @param node {@inheritDoc} * @param node {@inheritDoc}
* @param p {@inheritDoc} * @param p {@inheritDoc}
* @return the result of {@code defaultAction} * @return the result of {@code defaultAction}
* @since 19 * @since 21
*/ */
@Override @Override
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
public R visitDeconstructionPattern(DeconstructionPatternTree node, P p) { public R visitDeconstructionPattern(DeconstructionPatternTree node, P p) {
return defaultAction(node, p); return defaultAction(node, p);
} }
@ -712,10 +709,9 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
* @param node {@inheritDoc} * @param node {@inheritDoc}
* @param p {@inheritDoc} * @param p {@inheritDoc}
* @return the result of {@code defaultAction} * @return the result of {@code defaultAction}
* @since 19 * @since 21
*/ */
@Override @Override
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitPatternCaseLabel(PatternCaseLabelTree node, P p) { public R visitPatternCaseLabel(PatternCaseLabelTree node, P p) {
return defaultAction(node, p); return defaultAction(node, p);
} }
@ -748,22 +744,6 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
return defaultAction(node, p); return defaultAction(node, p);
} }
/**
* {@inheritDoc}
*
* @implSpec This implementation calls {@code defaultAction}.
*
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
* @since 17
*/
@Override
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitParenthesizedPattern(ParenthesizedPatternTree node, P p) {
return defaultAction(node, p);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
* *

View File

@ -333,7 +333,7 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
*/ */
@Override @Override
public R visitEnhancedForLoop(EnhancedForLoopTree node, P p) { public R visitEnhancedForLoop(EnhancedForLoopTree node, P p) {
R r = scan(node.getVariableOrRecordPattern(), p); R r = scan(node.getVariable(), p);
r = scanAndReduce(node.getExpression(), p, r); r = scanAndReduce(node.getExpression(), p, r);
r = scanAndReduce(node.getStatement(), p, r); r = scanAndReduce(node.getStatement(), p, r);
return r; return r;
@ -397,6 +397,7 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
@Override @Override
public R visitCase(CaseTree node, P p) { public R visitCase(CaseTree node, P p) {
R r = scan(node.getLabels(), p); R r = scan(node.getLabels(), p);
r = scanAndReduce(node.getGuard(), p, r);
if (node.getCaseKind() == CaseTree.CaseKind.RULE) if (node.getCaseKind() == CaseTree.CaseKind.RULE)
r = scanAndReduce(node.getBody(), p, r); r = scanAndReduce(node.getBody(), p, r);
else else
@ -799,10 +800,9 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
* @param node {@inheritDoc} * @param node {@inheritDoc}
* @param p {@inheritDoc} * @param p {@inheritDoc}
* @return the result of scanning * @return the result of scanning
* @since 17 * @since 21
*/ */
@Override @Override
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) { public R visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) {
return null; return null;
} }
@ -815,10 +815,9 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
* @param node {@inheritDoc} * @param node {@inheritDoc}
* @param p {@inheritDoc} * @param p {@inheritDoc}
* @return the result of scanning * @return the result of scanning
* @since 19 * @since 21
*/ */
@Override @Override
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitConstantCaseLabel(ConstantCaseLabelTree node, P p) { public R visitConstantCaseLabel(ConstantCaseLabelTree node, P p) {
return scan(node.getConstantExpression(), p); return scan(node.getConstantExpression(), p);
} }
@ -831,14 +830,11 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
* @param node {@inheritDoc} * @param node {@inheritDoc}
* @param p {@inheritDoc} * @param p {@inheritDoc}
* @return the result of scanning * @return the result of scanning
* @since 19 * @since 21
*/ */
@Override @Override
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitPatternCaseLabel(PatternCaseLabelTree node, P p) { public R visitPatternCaseLabel(PatternCaseLabelTree node, P p) {
R r = scan(node.getPattern(), p); return scan(node.getPattern(), p);
r = scanAndReduce(node.getGuard(), p, r);
return r;
} }
/** /**
@ -849,10 +845,9 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
* @param node {@inheritDoc} * @param node {@inheritDoc}
* @param p {@inheritDoc} * @param p {@inheritDoc}
* @return the result of scanning * @return the result of scanning
* @since 19 * @since 21
*/ */
@Override @Override
@PreviewFeature(feature=PreviewFeature.Feature.RECORD_PATTERNS, reflective=true)
public R visitDeconstructionPattern(DeconstructionPatternTree node, P p) { public R visitDeconstructionPattern(DeconstructionPatternTree node, P p) {
R r = scan(node.getDeconstructor(), p); R r = scan(node.getDeconstructor(), p);
r = scanAndReduce(node.getNestedPatterns(), p, r); r = scanAndReduce(node.getNestedPatterns(), p, r);
@ -887,22 +882,6 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
return scan(node.getExpression(), p); return scan(node.getExpression(), p);
} }
/**
* {@inheritDoc}
*
* @implSpec This implementation scans the children in left to right order.
*
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of scanning
* @since 17
*/
@Override
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public R visitParenthesizedPattern(ParenthesizedPatternTree node, P p) {
return scan(node.getPattern(), p);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
* *

View File

@ -209,11 +209,7 @@ public class Preview {
*/ */
public boolean isPreview(Feature feature) { public boolean isPreview(Feature feature) {
return switch (feature) { return switch (feature) {
case CASE_NULL -> true;
case PATTERN_SWITCH -> true;
case STRING_TEMPLATES -> true; case STRING_TEMPLATES -> true;
case UNCONDITIONAL_PATTERN_IN_INSTANCEOF -> true;
case RECORD_PATTERNS -> true;
//Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing). //Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
//When real preview features will be added, this method can be implemented to return 'true' //When real preview features will be added, this method can be implemented to return 'true'

View File

@ -232,12 +232,12 @@ public enum Source {
REIFIABLE_TYPES_INSTANCEOF(JDK16, Fragments.FeatureReifiableTypesInstanceof, DiagKind.PLURAL), REIFIABLE_TYPES_INSTANCEOF(JDK16, Fragments.FeatureReifiableTypesInstanceof, DiagKind.PLURAL),
RECORDS(JDK16, Fragments.FeatureRecords, DiagKind.PLURAL), RECORDS(JDK16, Fragments.FeatureRecords, DiagKind.PLURAL),
SEALED_CLASSES(JDK17, Fragments.FeatureSealedClasses, DiagKind.PLURAL), SEALED_CLASSES(JDK17, Fragments.FeatureSealedClasses, DiagKind.PLURAL),
CASE_NULL(JDK17, Fragments.FeatureCaseNull, DiagKind.NORMAL), CASE_NULL(JDK21, Fragments.FeatureCaseNull, DiagKind.NORMAL),
PATTERN_SWITCH(JDK17, Fragments.FeaturePatternSwitch, DiagKind.PLURAL), PATTERN_SWITCH(JDK21, Fragments.FeaturePatternSwitch, DiagKind.PLURAL),
REDUNDANT_STRICTFP(JDK17), REDUNDANT_STRICTFP(JDK17),
UNCONDITIONAL_PATTERN_IN_INSTANCEOF(JDK21, Fragments.FeatureUnconditionalPatternsInInstanceof, DiagKind.PLURAL),
RECORD_PATTERNS(JDK21, Fragments.FeatureDeconstructionPatterns, DiagKind.PLURAL),
STRING_TEMPLATES(JDK21, Fragments.FeatureStringTemplates, DiagKind.PLURAL), STRING_TEMPLATES(JDK21, Fragments.FeatureStringTemplates, DiagKind.PLURAL),
UNCONDITIONAL_PATTERN_IN_INSTANCEOF(JDK19, Fragments.FeatureUnconditionalPatternsInInstanceof, DiagKind.PLURAL),
RECORD_PATTERNS(JDK19, Fragments.FeatureDeconstructionPatterns, DiagKind.PLURAL),
WARN_ON_ILLEGAL_UTF8(MIN, JDK21), WARN_ON_ILLEGAL_UTF8(MIN, JDK21),
; ;

View File

@ -191,7 +191,6 @@ public class Symtab {
public final Type incompatibleClassChangeErrorType; public final Type incompatibleClassChangeErrorType;
public final Type cloneNotSupportedExceptionType; public final Type cloneNotSupportedExceptionType;
public final Type matchExceptionType; public final Type matchExceptionType;
public final Type nullPointerExceptionType;
public final Type annotationType; public final Type annotationType;
public final TypeSymbol enumSym; public final TypeSymbol enumSym;
public final Type listType; public final Type listType;
@ -224,8 +223,11 @@ public class Symtab {
public final Type typeDescriptorType; public final Type typeDescriptorType;
public final Type recordType; public final Type recordType;
public final Type switchBootstrapsType; public final Type switchBootstrapsType;
public final Type constantBootstrapsType;
public final Type valueBasedType; public final Type valueBasedType;
public final Type valueBasedInternalType; public final Type valueBasedInternalType;
public final Type classDescType;
public final Type enumDescType;
// For serialization lint checking // For serialization lint checking
public final Type objectStreamFieldType; public final Type objectStreamFieldType;
@ -565,7 +567,6 @@ public class Symtab {
incompatibleClassChangeErrorType = enterClass("java.lang.IncompatibleClassChangeError"); incompatibleClassChangeErrorType = enterClass("java.lang.IncompatibleClassChangeError");
cloneNotSupportedExceptionType = enterClass("java.lang.CloneNotSupportedException"); cloneNotSupportedExceptionType = enterClass("java.lang.CloneNotSupportedException");
matchExceptionType = enterClass("java.lang.MatchException"); matchExceptionType = enterClass("java.lang.MatchException");
nullPointerExceptionType = enterClass("java.lang.NullPointerException");
annotationType = enterClass("java.lang.annotation.Annotation"); annotationType = enterClass("java.lang.annotation.Annotation");
classLoaderType = enterClass("java.lang.ClassLoader"); classLoaderType = enterClass("java.lang.ClassLoader");
enumSym = enterClass(java_base, names.java_lang_Enum); enumSym = enterClass(java_base, names.java_lang_Enum);
@ -609,8 +610,11 @@ public class Symtab {
typeDescriptorType = enterClass("java.lang.invoke.TypeDescriptor"); typeDescriptorType = enterClass("java.lang.invoke.TypeDescriptor");
recordType = enterClass("java.lang.Record"); recordType = enterClass("java.lang.Record");
switchBootstrapsType = enterClass("java.lang.runtime.SwitchBootstraps"); switchBootstrapsType = enterClass("java.lang.runtime.SwitchBootstraps");
constantBootstrapsType = enterClass("java.lang.invoke.ConstantBootstraps");
valueBasedType = enterClass("jdk.internal.ValueBased"); valueBasedType = enterClass("jdk.internal.ValueBased");
valueBasedInternalType = enterSyntheticAnnotation("jdk.internal.ValueBased+Annotation"); valueBasedInternalType = enterSyntheticAnnotation("jdk.internal.ValueBased+Annotation");
classDescType = enterClass("java.lang.constant.ClassDesc");
enumDescType = enterClass("java.lang.Enum$EnumDesc");
// For serialization lint checking // For serialization lint checking
objectStreamFieldType = enterClass("java.io.ObjectStreamField"); objectStreamFieldType = enterClass("java.io.ObjectStreamField");
objectInputStreamType = enterClass("java.io.ObjectInputStream"); objectInputStreamType = enterClass("java.io.ObjectInputStream");

View File

@ -33,7 +33,6 @@ import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.LambdaExpressionTree; import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.NewClassTree; import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.VariableTree; import com.sun.source.tree.VariableTree;
@ -426,24 +425,18 @@ public class Analyzer {
@Override @Override
boolean match(JCEnhancedForLoop tree){ boolean match(JCEnhancedForLoop tree){
return tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE && return !isImplicitlyTyped(tree.var);
!isImplicitlyTyped((JCVariableDecl) tree.varOrRecordPattern);
} }
@Override @Override
List<JCEnhancedForLoop> rewrite(JCEnhancedForLoop oldTree) { List<JCEnhancedForLoop> rewrite(JCEnhancedForLoop oldTree) {
Assert.check(oldTree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE);
JCEnhancedForLoop newTree = copier.copy(oldTree); JCEnhancedForLoop newTree = copier.copy(oldTree);
newTree.varOrRecordPattern = rewriteVarType((JCVariableDecl) oldTree.varOrRecordPattern); newTree.var = rewriteVarType(oldTree.var);
newTree.body = make.at(oldTree.body).Block(0, List.nil()); newTree.body = make.at(oldTree.body).Block(0, List.nil());
return List.of(newTree); return List.of(newTree);
} }
@Override @Override
void process(JCEnhancedForLoop oldTree, JCEnhancedForLoop newTree, boolean hasErrors){ void process(JCEnhancedForLoop oldTree, JCEnhancedForLoop newTree, boolean hasErrors){
Assert.check(oldTree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE); processVar(oldTree.var, newTree.var, hasErrors);
processVar((JCVariableDecl) oldTree.varOrRecordPattern,
(JCVariableDecl) newTree.varOrRecordPattern, hasErrors);
} }
} }

View File

@ -34,7 +34,6 @@ import javax.lang.model.element.ElementKind;
import javax.tools.JavaFileObject; import javax.tools.JavaFileObject;
import com.sun.source.tree.CaseTree; import com.sun.source.tree.CaseTree;
import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberReferenceTree.ReferenceMode; import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MemberSelectTree;
@ -171,8 +170,8 @@ public class Attr extends JCTree.Visitor {
allowRecords = Feature.RECORDS.allowedInSource(source); allowRecords = Feature.RECORDS.allowedInSource(source);
allowPatternSwitch = (preview.isEnabled() || !preview.isPreview(Feature.PATTERN_SWITCH)) && allowPatternSwitch = (preview.isEnabled() || !preview.isPreview(Feature.PATTERN_SWITCH)) &&
Feature.PATTERN_SWITCH.allowedInSource(source); Feature.PATTERN_SWITCH.allowedInSource(source);
allowUnconditionalPatternsInstanceOf = (preview.isEnabled() || !preview.isPreview(Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF)) && allowUnconditionalPatternsInstanceOf =
Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF.allowedInSource(source); Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF.allowedInSource(source);
sourceName = source.name; sourceName = source.name;
useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning"); useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning");
@ -1514,25 +1513,24 @@ public class Attr extends JCTree.Visitor {
public void visitForeachLoop(JCEnhancedForLoop tree) { public void visitForeachLoop(JCEnhancedForLoop tree) {
Env<AttrContext> loopEnv = Env<AttrContext> loopEnv =
env.dup(env.tree, env.info.dup(env.info.scope.dup())); env.dup(env.tree, env.info.dup(env.info.scope.dup()));
try { try {
//the Formal Parameter of a for-each loop is not in the scope when //the Formal Parameter of a for-each loop is not in the scope when
//attributing the for-each expression; we mimic this by attributing //attributing the for-each expression; we mimic this by attributing
//the for-each expression first (against original scope). //the for-each expression first (against original scope).
Type exprType = types.cvarUpperBound(attribExpr(tree.expr, loopEnv)); Type exprType = types.cvarUpperBound(attribExpr(tree.expr, loopEnv));
chk.checkNonVoid(tree.pos(), exprType); chk.checkNonVoid(tree.pos(), exprType);
tree.elementType = types.elemtype(exprType); // perhaps expr is an array? Type elemtype = types.elemtype(exprType); // perhaps expr is an array?
if (tree.elementType == null) { if (elemtype == null) {
// or perhaps expr implements Iterable<T>? // or perhaps expr implements Iterable<T>?
Type base = types.asSuper(exprType, syms.iterableType.tsym); Type base = types.asSuper(exprType, syms.iterableType.tsym);
if (base == null) { if (base == null) {
log.error(tree.expr.pos(), log.error(tree.expr.pos(),
Errors.ForeachNotApplicableToType(exprType, Errors.ForeachNotApplicableToType(exprType,
Fragments.TypeReqArrayOrIterable)); Fragments.TypeReqArrayOrIterable));
tree.elementType = types.createErrorType(exprType); elemtype = types.createErrorType(exprType);
} else { } else {
List<Type> iterableParams = base.allparams(); List<Type> iterableParams = base.allparams();
tree.elementType = iterableParams.isEmpty() elemtype = iterableParams.isEmpty()
? syms.objectType ? syms.objectType
: types.wildUpperBound(iterableParams.head); : types.wildUpperBound(iterableParams.head);
@ -1546,40 +1544,14 @@ public class Attr extends JCTree.Visitor {
} }
} }
} }
if (tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) { if (tree.var.isImplicitlyTyped()) {
if (jcVariableDecl.isImplicitlyTyped()) { Type inferredType = chk.checkLocalVarType(tree.var, elemtype, tree.var.name);
Type inferredType = chk.checkLocalVarType(jcVariableDecl, tree.elementType, jcVariableDecl.name); setSyntheticVariableType(tree.var, inferredType);
setSyntheticVariableType(jcVariableDecl, inferredType);
}
attribStat(jcVariableDecl, loopEnv);
chk.checkType(tree.expr.pos(), tree.elementType, jcVariableDecl.sym.type);
loopEnv.tree = tree; // before, we were not in loop!
attribStat(tree.body, loopEnv);
} else {
Assert.check(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.PATTERN);
JCRecordPattern jcRecordPattern = (JCRecordPattern) tree.varOrRecordPattern;
attribExpr(jcRecordPattern, loopEnv, tree.elementType);
// for(<pattern> x : xs) { y }
// we include x's bindings when true in y
// we don't do anything with x's bindings when false
MatchBindings forWithRecordPatternBindings = matchBindings;
Env<AttrContext> recordPatternEnv = bindingEnv(loopEnv, forWithRecordPatternBindings.bindingsWhenTrue);
Type clazztype = jcRecordPattern.type;
checkCastablePattern(tree.expr.pos(), tree.elementType, clazztype);
recordPatternEnv.tree = tree; // before, we were not in loop!
try {
attribStat(tree.body, recordPatternEnv);
} finally {
recordPatternEnv.info.scope.leave();
}
} }
attribStat(tree.var, loopEnv);
chk.checkType(tree.expr.pos(), elemtype, tree.var.sym.type);
loopEnv.tree = tree; // before, we were not in loop!
attribStat(tree.body, loopEnv);
result = null; result = null;
} }
finally { finally {
@ -1717,7 +1689,8 @@ public class Attr extends JCTree.Visitor {
} }
MatchBindings currentBindings = null; MatchBindings currentBindings = null;
boolean wasUnconditionalPattern = hasUnconditionalPattern; boolean wasUnconditionalPattern = hasUnconditionalPattern;
for (JCCaseLabel label : c.labels) { for (List<JCCaseLabel> labels = c.labels; labels.nonEmpty(); labels = labels.tail) {
JCCaseLabel label = labels.head;
if (label instanceof JCConstantCaseLabel constLabel) { if (label instanceof JCConstantCaseLabel constLabel) {
JCExpression expr = constLabel.expr; JCExpression expr = constLabel.expr;
if (TreeInfo.isNull(expr)) { if (TreeInfo.isNull(expr)) {
@ -1731,7 +1704,11 @@ public class Attr extends JCTree.Visitor {
} else if (enumSwitch) { } else if (enumSwitch) {
Symbol sym = enumConstant(expr, seltype); Symbol sym = enumConstant(expr, seltype);
if (sym == null) { if (sym == null) {
log.error(expr.pos(), Errors.EnumLabelMustBeUnqualifiedEnum); if (allowPatternSwitch) {
attribTree(expr, switchEnv, caseLabelResultInfo(seltype));
} else {
log.error(expr.pos(), Errors.EnumLabelMustBeUnqualifiedEnum);
}
} else if (!constants.add(sym)) { } else if (!constants.add(sym)) {
log.error(label.pos(), Errors.DuplicateCaseLabel); log.error(label.pos(), Errors.DuplicateCaseLabel);
} }
@ -1747,17 +1724,14 @@ public class Attr extends JCTree.Visitor {
rs.basicLogResolveHelper = prevResolveHelper; rs.basicLogResolveHelper = prevResolveHelper;
} }
} else { } else {
ResultInfo valTypInfo = new ResultInfo(KindSelector.VAL_TYP, Type pattype = attribTree(expr, switchEnv, caseLabelResultInfo(seltype));
!seltype.hasTag(ERROR) ? seltype
: Type.noType);
Type pattype = attribTree(expr, switchEnv, valTypInfo);
if (!pattype.hasTag(ERROR)) { if (!pattype.hasTag(ERROR)) {
if (pattype.constValue() == null) { if (pattype.constValue() == null) {
Symbol s = TreeInfo.symbol(expr); Symbol s = TreeInfo.symbol(expr);
if (s != null && s.kind == TYP && allowPatternSwitch) { if (s != null && s.kind == TYP && allowPatternSwitch) {
log.error(expr.pos(), log.error(expr.pos(),
Errors.PatternExpected); Errors.PatternExpected);
} else { } else if ((s != null && !s.isEnum()) || !allowPatternSwitch) {
log.error(expr.pos(), log.error(expr.pos(),
(stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq)); (stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq));
} }
@ -1786,8 +1760,8 @@ public class Attr extends JCTree.Visitor {
} }
checkCastablePattern(pat.pos(), seltype, primaryType); checkCastablePattern(pat.pos(), seltype, primaryType);
Type patternType = types.erasure(primaryType); Type patternType = types.erasure(primaryType);
JCExpression guard = patternlabel.guard; JCExpression guard = c.guard;
if (guard != null) { if (labels.tail.isEmpty() && guard != null) {
MatchBindings afterPattern = matchBindings; MatchBindings afterPattern = matchBindings;
Env<AttrContext> bodyEnv = bindingEnv(switchEnv, matchBindings.bindingsWhenTrue); Env<AttrContext> bodyEnv = bindingEnv(switchEnv, matchBindings.bindingsWhenTrue);
try { try {
@ -1801,7 +1775,7 @@ public class Attr extends JCTree.Visitor {
log.error(guard.pos(), Errors.GuardHasConstantExpressionFalse); log.error(guard.pos(), Errors.GuardHasConstantExpressionFalse);
} }
} }
boolean unguarded = TreeInfo.unguardedCaseLabel(label) && !pat.hasTag(RECORDPATTERN); boolean unguarded = TreeInfo.unguardedCase(c) && !pat.hasTag(RECORDPATTERN);
boolean unconditional = boolean unconditional =
unguarded && unguarded &&
!patternType.isErroneous() && !patternType.isErroneous() &&
@ -1854,6 +1828,11 @@ public class Attr extends JCTree.Visitor {
} }
} }
// where // where
private ResultInfo caseLabelResultInfo(Type seltype) {
return new ResultInfo(KindSelector.VAL_TYP,
!seltype.hasTag(ERROR) ? seltype
: Type.noType);
}
/** Add any variables defined in stats to the switch scope. */ /** Add any variables defined in stats to the switch scope. */
private static void addVars(List<JCStatement> stats, WriteableScope switchScope) { private static void addVars(List<JCStatement> stats, WriteableScope switchScope) {
for (;stats.nonEmpty(); stats = stats.tail) { for (;stats.nonEmpty(); stats = stats.tail) {
@ -4101,7 +4080,6 @@ public class Attr extends JCTree.Visitor {
Type clazztype; Type clazztype;
JCTree typeTree; JCTree typeTree;
if (tree.pattern.getTag() == BINDINGPATTERN || if (tree.pattern.getTag() == BINDINGPATTERN ||
tree.pattern.getTag() == PARENTHESIZEDPATTERN ||
tree.pattern.getTag() == RECORDPATTERN) { tree.pattern.getTag() == RECORDPATTERN) {
attribExpr(tree.pattern, env, exprtype); attribExpr(tree.pattern, env, exprtype);
clazztype = tree.pattern.type; clazztype = tree.pattern.type;
@ -4109,9 +4087,8 @@ public class Attr extends JCTree.Visitor {
!exprtype.isErroneous() && !clazztype.isErroneous() && !exprtype.isErroneous() && !clazztype.isErroneous() &&
tree.pattern.getTag() != RECORDPATTERN) { tree.pattern.getTag() != RECORDPATTERN) {
if (!allowUnconditionalPatternsInstanceOf) { if (!allowUnconditionalPatternsInstanceOf) {
log.error(tree.pos(), Errors.InstanceofPatternNoSubtype(exprtype, clazztype)); log.error(DiagnosticFlag.SOURCE_LEVEL, tree.pos(),
} else if (preview.isPreview(Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF)) { Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF.error(this.sourceName));
preview.warnPreview(tree.pattern.pos(), Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF);
} }
} }
typeTree = TreeInfo.primaryPatternTypeTree((JCPattern) tree.pattern); typeTree = TreeInfo.primaryPatternTypeTree((JCPattern) tree.pattern);
@ -4253,11 +4230,6 @@ public class Attr extends JCTree.Visitor {
matchBindings = new MatchBindings(outBindings.toList(), List.nil()); matchBindings = new MatchBindings(outBindings.toList(), List.nil());
} }
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
attribExpr(tree.pattern, env);
result = tree.type = tree.pattern.type;
}
public void visitIndexed(JCArrayAccess tree) { public void visitIndexed(JCArrayAccess tree) {
Type owntype = types.createErrorType(tree.type); Type owntype = types.createErrorType(tree.type);
Type atype = attribExpr(tree.indexed, env); Type atype = attribExpr(tree.indexed, env);

View File

@ -4626,7 +4626,7 @@ public class Check {
return false; return false;
} }
void checkSwitchCaseLabelDominated(List<JCCase> cases) { void checkSwitchCaseLabelDominated(List<JCCase> cases) {
List<JCCaseLabel> caseLabels = List.nil(); List<Pair<JCCase, JCCaseLabel>> caseLabels = List.nil();
boolean seenDefault = false; boolean seenDefault = false;
boolean seenDefaultLabel = false; boolean seenDefaultLabel = false;
boolean warnDominatedByDefault = false; boolean warnDominatedByDefault = false;
@ -4653,7 +4653,9 @@ public class Check {
} }
} }
Type currentType = labelType(label); Type currentType = labelType(label);
for (JCCaseLabel testCaseLabel : caseLabels) { for (Pair<JCCase, JCCaseLabel> caseAndLabel : caseLabels) {
JCCase testCase = caseAndLabel.fst;
JCCaseLabel testCaseLabel = caseAndLabel.snd;
Type testType = labelType(testCaseLabel); Type testType = labelType(testCaseLabel);
if (types.isSubtype(currentType, testType) && if (types.isSubtype(currentType, testType) &&
!currentType.hasTag(ERROR) && !testType.hasTag(ERROR)) { !currentType.hasTag(ERROR) && !testType.hasTag(ERROR)) {
@ -4663,7 +4665,7 @@ public class Check {
dominated |= !(testCaseLabel instanceof JCConstantCaseLabel); dominated |= !(testCaseLabel instanceof JCConstantCaseLabel);
} else if (label instanceof JCPatternCaseLabel patternCL && } else if (label instanceof JCPatternCaseLabel patternCL &&
testCaseLabel instanceof JCPatternCaseLabel testPatternCaseLabel && testCaseLabel instanceof JCPatternCaseLabel testPatternCaseLabel &&
TreeInfo.unguardedCaseLabel(testCaseLabel)) { TreeInfo.unguardedCase(testCase)) {
dominated = patternDominated(testPatternCaseLabel.pat, dominated = patternDominated(testPatternCaseLabel.pat,
patternCL.pat); patternCL.pat);
} }
@ -4672,7 +4674,7 @@ public class Check {
} }
} }
} }
caseLabels = caseLabels.prepend(label); caseLabels = caseLabels.prepend(Pair.of(c, label));
} }
} }
} }
@ -4697,12 +4699,6 @@ public class Check {
return false; return false;
} }
} }
while (existingPattern instanceof JCParenthesizedPattern parenthesized) {
existingPattern = parenthesized.pattern;
}
while (currentPattern instanceof JCParenthesizedPattern parenthesized) {
currentPattern = parenthesized.pattern;
}
if (currentPattern instanceof JCBindingPattern) { if (currentPattern instanceof JCBindingPattern) {
return existingPattern instanceof JCBindingPattern; return existingPattern instanceof JCBindingPattern;
} else if (currentPattern instanceof JCRecordPattern currentRecordPattern) { } else if (currentPattern instanceof JCRecordPattern currentRecordPattern) {

View File

@ -32,13 +32,11 @@ import java.util.Map.Entry;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import com.sun.source.tree.CaseTree;
import com.sun.source.tree.LambdaExpressionTree.BodyKind; import com.sun.source.tree.LambdaExpressionTree.BodyKind;
import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Scope.WriteableScope; import com.sun.tools.javac.code.Scope.WriteableScope;
import com.sun.tools.javac.code.Source.Feature;
import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.resources.CompilerProperties.Errors;
import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.resources.CompilerProperties.Warnings;
import com.sun.tools.javac.tree.*; import com.sun.tools.javac.tree.*;
@ -52,15 +50,22 @@ import com.sun.tools.javac.tree.JCTree.*;
import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Flags.BLOCK; import static com.sun.tools.javac.code.Flags.BLOCK;
import com.sun.tools.javac.code.Kinds.Kind;
import static com.sun.tools.javac.code.Kinds.Kind.*; import static com.sun.tools.javac.code.Kinds.Kind.*;
import com.sun.tools.javac.code.Type.TypeVar; import com.sun.tools.javac.code.Type.TypeVar;
import static com.sun.tools.javac.code.TypeTag.BOOLEAN; import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
import static com.sun.tools.javac.code.TypeTag.NONE;
import static com.sun.tools.javac.code.TypeTag.VOID; import static com.sun.tools.javac.code.TypeTag.VOID;
import com.sun.tools.javac.code.Types.UniqueType;
import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.resources.CompilerProperties.Fragments;
import static com.sun.tools.javac.tree.JCTree.Tag.*; import static com.sun.tools.javac.tree.JCTree.Tag.*;
import com.sun.tools.javac.util.JCDiagnostic.Fragment; import com.sun.tools.javac.util.JCDiagnostic.Fragment;
import java.util.Arrays;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.groupingBy;
/** This pass implements dataflow analysis for Java programs though /** This pass implements dataflow analysis for Java programs though
* different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that * different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that
@ -211,6 +216,7 @@ public class Flow {
private final JCDiagnostic.Factory diags; private final JCDiagnostic.Factory diags;
private Env<AttrContext> attrEnv; private Env<AttrContext> attrEnv;
private Lint lint; private Lint lint;
private final Infer infer;
public static Flow instance(Context context) { public static Flow instance(Context context) {
Flow instance = context.get(flowKey); Flow instance = context.get(flowKey);
@ -334,6 +340,7 @@ public class Flow {
types = Types.instance(context); types = Types.instance(context);
chk = Check.instance(context); chk = Check.instance(context);
lint = Lint.instance(context); lint = Lint.instance(context);
infer = Infer.instance(context);
rs = Resolve.instance(context); rs = Resolve.instance(context);
diags = JCDiagnostic.Factory.instance(context); diags = JCDiagnostic.Factory.instance(context);
Source source = Source.instance(context); Source source = Source.instance(context);
@ -647,21 +654,7 @@ public class Flow {
} }
public void visitForeachLoop(JCEnhancedForLoop tree) { public void visitForeachLoop(JCEnhancedForLoop tree) {
if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) { visitVarDef(tree.var);
visitVarDef(jcVariableDecl);
} else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
visitRecordPattern(jcRecordPattern);
Set<Symbol> coveredSymbols =
coveredSymbols(jcRecordPattern.pos(), List.of(jcRecordPattern));
boolean isExhaustive =
isExhaustive(jcRecordPattern.pos(), tree.elementType, coveredSymbols);
if (!isExhaustive) {
log.error(tree, Errors.ForeachNotExhaustiveOnType(jcRecordPattern.type, tree.elementType));
}
}
ListBuffer<PendingExit> prevPendingExits = pendingExits; ListBuffer<PendingExit> prevPendingExits = pendingExits;
scan(tree.expr); scan(tree.expr);
pendingExits = new ListBuffer<>(); pendingExits = new ListBuffer<>();
@ -705,8 +698,7 @@ public class Flow {
tree.isExhaustive = tree.hasUnconditionalPattern || tree.isExhaustive = tree.hasUnconditionalPattern ||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases); TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases);
if (exhaustiveSwitch) { if (exhaustiveSwitch) {
Set<Symbol> coveredSymbols = coveredSymbolsForCases(tree.pos(), tree.cases); tree.isExhaustive |= exhausts(tree.selector, tree.cases);
tree.isExhaustive |= isExhaustive(tree.selector.pos(), tree.selector.type, coveredSymbols);
if (!tree.isExhaustive) { if (!tree.isExhaustive) {
log.error(tree, Errors.NotExhaustiveStatement); log.error(tree, Errors.NotExhaustiveStatement);
} }
@ -740,10 +732,9 @@ public class Flow {
} }
} }
} }
Set<Symbol> coveredSymbols = coveredSymbolsForCases(tree.pos(), tree.cases);
tree.isExhaustive = tree.hasUnconditionalPattern || tree.isExhaustive = tree.hasUnconditionalPattern ||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) || TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) ||
isExhaustive(tree.selector.pos(), tree.selector.type, coveredSymbols); exhausts(tree.selector, tree.cases);
if (!tree.isExhaustive) { if (!tree.isExhaustive) {
log.error(tree, Errors.NotExhaustive); log.error(tree, Errors.NotExhaustive);
} }
@ -751,211 +742,444 @@ public class Flow {
alive = alive.or(resolveYields(tree, prevPendingExits)); alive = alive.or(resolveYields(tree, prevPendingExits));
} }
private Set<Symbol> coveredSymbolsForCases(DiagnosticPosition pos, sealed interface PatternDescription {
List<JCCase> cases) { public static PatternDescription from(Types types, Type selectorType, JCPattern pattern) {
HashSet<JCTree> labelValues = cases.stream() if (pattern instanceof JCBindingPattern binding) {
.flatMap(c -> c.labels.stream()) Type type = types.isSubtype(selectorType, binding.type)
.filter(TreeInfo::unguardedCaseLabel) ? selectorType : binding.type;
.filter(l -> !l.hasTag(DEFAULTCASELABEL)) return new BindingPattern(type);
.map(l -> l.hasTag(CONSTANTCASELABEL) ? ((JCConstantCaseLabel) l).expr } else if (pattern instanceof JCRecordPattern record) {
: ((JCPatternCaseLabel) l).pat) Type[] componentTypes = ((ClassSymbol) record.type.tsym).getRecordComponents()
.collect(Collectors.toCollection(HashSet::new)); .map(r -> types.memberType(record.type, r))
return coveredSymbols(pos, labelValues); .toArray(s -> new Type[s]);
PatternDescription[] nestedDescriptions =
new PatternDescription[record.nested.size()];
int i = 0;
for (List<JCPattern> it = record.nested;
it.nonEmpty();
it = it.tail, i++) {
nestedDescriptions[i] = PatternDescription.from(types, componentTypes[i], it.head);
}
return new RecordPattern(record.type, componentTypes, nestedDescriptions);
} else {
throw Assert.error();
}
}
} }
private Set<Symbol> coveredSymbols(DiagnosticPosition pos, record BindingPattern(Type type) implements PatternDescription {
Iterable<? extends JCTree> labels) { @Override
Set<Symbol> coveredSymbols = new HashSet<>(); public int hashCode() {
Map<UniqueType, List<JCRecordPattern>> deconstructionPatternsByType = new HashMap<>(); return type.tsym.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof BindingPattern other &&
type.tsym == other.type.tsym;
}
@Override
public String toString() {
return type.tsym + " _";
}
}
for (JCTree labelValue : labels) { record RecordPattern(Type recordType, int _hashCode, Type[] fullComponentTypes, PatternDescription... nested) implements PatternDescription {
switch (labelValue.getTag()) {
case BINDINGPATTERN, PARENTHESIZEDPATTERN -> { public RecordPattern(Type recordType, Type[] fullComponentTypes, PatternDescription[] nested) {
Type primaryPatternType = TreeInfo.primaryPatternType((JCPattern) labelValue); this(recordType, hashCode(-1, recordType, nested), fullComponentTypes, nested);
if (!primaryPatternType.hasTag(NONE)) { }
coveredSymbols.add(primaryPatternType.tsym);
@Override
public int hashCode() {
return _hashCode;
}
@Override
public boolean equals(Object o) {
return o instanceof RecordPattern other &&
recordType.tsym == other.recordType.tsym &&
Arrays.equals(nested, other.nested);
}
public int hashCode(int excludeComponent) {
return hashCode(excludeComponent, recordType, nested);
}
public static int hashCode(int excludeComponent, Type recordType, PatternDescription... nested) {
int hash = 5;
hash = 41 * hash + recordType.tsym.hashCode();
for (int i = 0; i < nested.length; i++) {
if (i != excludeComponent) {
hash = 41 * hash + nested[i].hashCode();
}
}
return hash;
}
@Override
public String toString() {
return recordType.tsym + "(" + Arrays.stream(nested)
.map(pd -> pd.toString())
.collect(Collectors.joining(", ")) + ")";
}
}
private boolean exhausts(JCExpression selector, List<JCCase> cases) {
Set<PatternDescription> patternSet = new HashSet<>();
Map<Symbol, Set<Symbol>> enum2Constants = new HashMap<>();
for (JCCase c : cases) {
if (!TreeInfo.unguardedCase(c))
continue;
for (var l : c.labels) {
if (l instanceof JCPatternCaseLabel patternLabel) {
for (Type component : components(selector.type)) {
patternSet.add(PatternDescription.from(types, component, patternLabel.pat));
} }
} } else if (l instanceof JCConstantCaseLabel constantLabel) {
case RECORDPATTERN -> { Symbol s = TreeInfo.symbol(constantLabel.expr);
JCRecordPattern dpat = (JCRecordPattern) labelValue; if (s != null && s.isEnum()) {
UniqueType type = new UniqueType(dpat.type, types); enum2Constants.computeIfAbsent(s.owner, x -> {
List<JCRecordPattern> augmentedPatterns = Set<Symbol> result = new HashSet<>();
deconstructionPatternsByType.getOrDefault(type, List.nil()) s.owner.members()
.prepend(dpat); .getSymbols(sym -> sym.kind == Kind.VAR && sym.isEnum())
.forEach(result::add);
deconstructionPatternsByType.put(type, augmentedPatterns); return result;
} }).remove(s);
default -> {
Assert.check(labelValue instanceof JCExpression, labelValue.getTag().name());
JCExpression expr = (JCExpression) labelValue;
if (expr.hasTag(IDENT) && ((JCIdent) expr).sym.isEnum())
coveredSymbols.add(((JCIdent) expr).sym);
}
}
}
for (Entry<UniqueType, List<JCRecordPattern>> e : deconstructionPatternsByType.entrySet()) {
if (e.getValue().stream().anyMatch(r -> r.nested.size() != r.record.getRecordComponents().size())) {
coveredSymbols.add(syms.errSymbol);
} else if (coversDeconstructionFromComponent(pos, e.getKey().type, e.getValue(), 0)) {
coveredSymbols.add(e.getKey().type.tsym);
}
}
return coveredSymbols;
}
private boolean coversDeconstructionFromComponent(DiagnosticPosition pos,
Type recordType,
List<JCRecordPattern> deconstructionPatterns,
int component) {
//Given a set of record patterns for the same record, and a starting component,
//this method checks, whether the nested patterns for the components are exhaustive,
//i.e. represent all possible combinations.
//This is done by categorizing the patterns based on the type covered by the given
//starting component.
//For each such category, it is then checked if the nested patterns starting at the next
//component are exhaustive, by recursivelly invoking this method. If these nested patterns
//are exhaustive, the given covered type is accepted.
//All such covered types are then checked whether they cover the declared type of
//the starting component's declaration. If yes, the given set of patterns starting at
//the given component cover the given record exhaustivelly, and true is returned.
List<? extends RecordComponent> components =
deconstructionPatterns.head.record.getRecordComponents();
if (components.size() == component) {
//no components remain to be checked:
return true;
}
//for the first tested component, gather symbols covered by the nested patterns:
Type instantiatedComponentType = types.memberType(recordType, components.get(component));
List<JCPattern> nestedComponentPatterns = deconstructionPatterns.map(d -> d.nested.get(component));
Set<Symbol> coveredSymbolsForComponent = coveredSymbols(pos,
nestedComponentPatterns);
//for each of the symbols covered by the starting component, find all deconstruction patterns
//that have the given type, or its supertype, as a type of the starting nested pattern:
Map<Symbol, List<JCRecordPattern>> coveredSymbol2Patterns = new HashMap<>();
for (JCRecordPattern deconstructionPattern : deconstructionPatterns) {
JCPattern nestedPattern = deconstructionPattern.nested.get(component);
Symbol componentPatternType;
switch (nestedPattern.getTag()) {
case BINDINGPATTERN, PARENTHESIZEDPATTERN -> {
Type primaryPatternType =
TreeInfo.primaryPatternType(nestedPattern);
componentPatternType = primaryPatternType.tsym;
}
case RECORDPATTERN -> {
componentPatternType = ((JCRecordPattern) nestedPattern).record;
}
default -> {
throw Assert.error("Unexpected tree kind: " + nestedPattern.getTag());
}
}
for (Symbol currentType : coveredSymbolsForComponent) {
if (types.isSubtype(types.erasure(currentType.type),
types.erasure(componentPatternType.type))) {
coveredSymbol2Patterns.put(currentType,
coveredSymbol2Patterns.getOrDefault(currentType,
List.nil())
.prepend(deconstructionPattern));
}
}
}
//Check the components following the starting component, for each of the covered symbol,
//if they are exhaustive. If yes, the given covered symbol should be part of the following
//exhaustiveness check:
Set<Symbol> covered = new HashSet<>();
for (Entry<Symbol, List<JCRecordPattern>> e : coveredSymbol2Patterns.entrySet()) {
if (coversDeconstructionFromComponent(pos, recordType, e.getValue(), component + 1)) {
covered.add(e.getKey());
}
}
//verify whether the filtered symbols cover the given record's declared type:
return isExhaustive(pos, instantiatedComponentType, covered);
}
private void transitiveCovers(DiagnosticPosition pos, Type seltype, Set<Symbol> covered) {
List<Symbol> todo = List.from(covered);
while (todo.nonEmpty()) {
Symbol sym = todo.head;
todo = todo.tail;
switch (sym.kind) {
case VAR -> {
Iterable<Symbol> constants = sym.owner
.members()
.getSymbols(s -> s.isEnum() &&
s.kind == VAR);
boolean hasAll = StreamSupport.stream(constants.spliterator(), false)
.allMatch(covered::contains);
if (hasAll && covered.add(sym.owner)) {
todo = todo.prepend(sym.owner);
}
}
case TYP -> {
for (Type sup : types.directSupertypes(sym.type)) {
if (sup.tsym.kind == TYP) {
if (isTransitivelyCovered(pos, seltype, sup.tsym, covered) &&
covered.add(sup.tsym)) {
todo = todo.prepend(sup.tsym);
}
}
} }
} }
} }
} }
} for (Entry<Symbol, Set<Symbol>> e : enum2Constants.entrySet()) {
if (e.getValue().isEmpty()) {
private boolean isTransitivelyCovered(DiagnosticPosition pos, Type seltype, patternSet.add(new BindingPattern(e.getKey().type));
Symbol sealed, Set<Symbol> covered) { }
}
List<PatternDescription> patterns = List.from(patternSet);
try { try {
if (covered.stream().anyMatch(c -> sealed.isSubClass(c, types))) boolean repeat = true;
return true; while (repeat) {
if (sealed.kind == TYP && sealed.isAbstract() && sealed.isSealed()) { List<PatternDescription> updatedPatterns;
return ((ClassSymbol) sealed).permitted updatedPatterns = reduceBindingPatterns(selector.type, patterns);
.stream() updatedPatterns = reduceNestedPatterns(updatedPatterns);
.filter(s -> { updatedPatterns = reduceRecordPatterns(updatedPatterns);
return types.isCastable(seltype, s.type/*, types.noWarnings*/); repeat = updatedPatterns != patterns;
}) patterns = updatedPatterns;
.allMatch(s -> isTransitivelyCovered(pos, seltype, s, covered)); if (checkCovered(selector.type, patterns)) {
return true;
}
} }
return false; return checkCovered(selector.type, patterns);
} catch (CompletionFailure cf) { } catch (CompletionFailure cf) {
chk.completionError(pos, cf); chk.completionError(selector.pos(), cf);
return true; return true; //error recovery
} }
} }
private boolean isExhaustive(DiagnosticPosition pos, Type seltype, Set<Symbol> covered) { private boolean checkCovered(Type seltype, List<PatternDescription> patterns) {
transitiveCovers(pos, seltype, covered); for (Type seltypeComponent : components(seltype)) {
for (PatternDescription pd : patterns) {
if (pd instanceof BindingPattern bp &&
types.isSubtype(seltypeComponent, types.erasure(bp.type))) {
return true;
}
}
}
return false;
}
private List<Type> components(Type seltype) {
return switch (seltype.getTag()) { return switch (seltype.getTag()) {
case CLASS -> { case CLASS -> {
if (seltype.isCompound()) { if (seltype.isCompound()) {
if (seltype.isIntersection()) { if (seltype.isIntersection()) {
yield ((Type.IntersectionClassType) seltype).getComponents() yield ((Type.IntersectionClassType) seltype).getComponents()
.stream() .stream()
.anyMatch(t -> isExhaustive(pos, t, covered)); .flatMap(t -> components(t).stream())
.collect(List.collector());
} }
yield false; yield List.nil();
} }
yield covered.stream() yield List.of(types.erasure(seltype));
.filter(coveredSym -> coveredSym.kind == TYP)
.anyMatch(coveredSym -> types.isSubtype(types.erasure(seltype),
types.erasure(coveredSym.type)));
}
case TYPEVAR -> isExhaustive(pos, ((TypeVar) seltype).getUpperBound(), covered);
default -> {
yield covered.contains(types.erasure(seltype).tsym);
} }
case TYPEVAR -> components(((TypeVar) seltype).getUpperBound());
default -> List.of(types.erasure(seltype));
}; };
} }
/* In a set of patterns, search for a sub-set of binding patterns that
* in combination exhaust their sealed supertype. If such a sub-set
* is found, it is removed, and replaced with a binding pattern
* for the sealed supertype.
*/
private List<PatternDescription> reduceBindingPatterns(Type selectorType, List<PatternDescription> patterns) {
Set<Symbol> existingBindings = patterns.stream()
.filter(pd -> pd instanceof BindingPattern)
.map(pd -> ((BindingPattern) pd).type.tsym)
.collect(Collectors.toSet());
for (PatternDescription pdOne : patterns) {
if (pdOne instanceof BindingPattern bpOne) {
Set<PatternDescription> toRemove = new HashSet<>();
Set<PatternDescription> toAdd = new HashSet<>();
for (Type sup : types.directSupertypes(bpOne.type)) {
ClassSymbol clazz = (ClassSymbol) sup.tsym;
if (clazz.isSealed() && clazz.isAbstract() &&
//if a binding pattern for clazz already exists, no need to analyze it again:
!existingBindings.contains(clazz)) {
ListBuffer<PatternDescription> bindings = new ListBuffer<>();
//do not reduce to types unrelated to the selector type:
Type clazzErasure = types.erasure(clazz.type);
if (components(selectorType).stream()
.map(types::erasure)
.noneMatch(c -> types.isSubtype(clazzErasure, c))) {
continue;
}
Set<Symbol> permitted = allPermittedSubTypes(clazz, csym -> {
Type instantiated;
if (csym.type.allparams().isEmpty()) {
instantiated = csym.type;
} else {
instantiated = infer.instantiatePatternType(selectorType, csym);
}
return instantiated != null && types.isCastable(selectorType, instantiated);
});
for (PatternDescription pdOther : patterns) {
if (pdOther instanceof BindingPattern bpOther) {
boolean reduces = false;
Set<Symbol> currentPermittedSubTypes =
allPermittedSubTypes((ClassSymbol) bpOther.type.tsym, s -> true);
PERMITTED: for (Iterator<Symbol> it = permitted.iterator(); it.hasNext();) {
Symbol perm = it.next();
for (Symbol currentPermitted : currentPermittedSubTypes) {
if (types.isSubtype(types.erasure(currentPermitted.type),
types.erasure(perm.type))) {
it.remove();
continue PERMITTED;
}
}
if (types.isSubtype(types.erasure(perm.type),
types.erasure(bpOther.type))) {
it.remove();
reduces = true;
}
}
if (reduces) {
bindings.append(pdOther);
}
}
}
if (permitted.isEmpty()) {
toRemove.addAll(bindings);
toAdd.add(new BindingPattern(clazz.type));
}
}
}
if (!toAdd.isEmpty() || !toRemove.isEmpty()) {
for (PatternDescription pd : toRemove) {
patterns = List.filter(patterns, pd);
}
for (PatternDescription pd : toAdd) {
patterns = patterns.prepend(pd);
}
return patterns;
}
}
}
return patterns;
}
private Set<Symbol> allPermittedSubTypes(ClassSymbol root, Predicate<ClassSymbol> accept) {
Set<Symbol> permitted = new HashSet<>();
List<ClassSymbol> permittedSubtypesClosure = List.of(root);
while (permittedSubtypesClosure.nonEmpty()) {
ClassSymbol current = permittedSubtypesClosure.head;
permittedSubtypesClosure = permittedSubtypesClosure.tail;
if (current.isSealed() && current.isAbstract()) {
for (Symbol sym : current.permitted) {
ClassSymbol csym = (ClassSymbol) sym;
if (accept.test(csym)) {
permittedSubtypesClosure = permittedSubtypesClosure.prepend(csym);
permitted.add(csym);
}
}
}
}
return permitted;
}
/* Among the set of patterns, find sub-set of patterns such:
* $record($prefix$, $nested, $suffix$)
* Where $record, $prefix$ and $suffix$ is the same for each pattern
* in the set, and the patterns only differ in one "column" in
* the $nested pattern.
* Then, the set of $nested patterns is taken, and passed recursively
* to reduceNestedPatterns and to reduceBindingPatterns, to
* simplify the pattern. If that succeeds, the original found sub-set
* of patterns is replaced with a new set of patterns of the form:
* $record($prefix$, $resultOfReduction, $suffix$)
*/
private List<PatternDescription> reduceNestedPatterns(List<PatternDescription> patterns) {
/* implementation note:
* finding a sub-set of patterns that only differ in a single
* column is time-consuming task, so this method speeds it up by:
* - group the patterns by their record class
* - for each column (nested pattern) do:
* -- group patterns by their hash
* -- in each such by-hash group, find sub-sets that only differ in
* the chosen column, and then call reduceBindingPatterns and reduceNestedPatterns
* on patterns in the chosen column, as described above
*/
var groupByRecordClass =
patterns.stream()
.filter(pd -> pd instanceof RecordPattern)
.map(pd -> (RecordPattern) pd)
.collect(groupingBy(pd -> (ClassSymbol) pd.recordType.tsym));
for (var e : groupByRecordClass.entrySet()) {
int nestedPatternsCount = e.getKey().getRecordComponents().size();
for (int mismatchingCandidate = 0;
mismatchingCandidate < nestedPatternsCount;
mismatchingCandidate++) {
int mismatchingCandidateFin = mismatchingCandidate;
var groupByHashes =
e.getValue()
.stream()
//error recovery, ignore patterns with incorrect number of nested patterns:
.filter(pd -> pd.nested.length == nestedPatternsCount)
.collect(groupingBy(pd -> pd.hashCode(mismatchingCandidateFin)));
for (var candidates : groupByHashes.values()) {
var candidatesArr = candidates.toArray(RecordPattern[]::new);
for (int firstCandidate = 0;
firstCandidate < candidatesArr.length;
firstCandidate++) {
RecordPattern rpOne = candidatesArr[firstCandidate];
ListBuffer<RecordPattern> join = new ListBuffer<>();
join.append(rpOne);
NEXT_PATTERN: for (int nextCandidate = 0;
nextCandidate < candidatesArr.length;
nextCandidate++) {
if (firstCandidate == nextCandidate) {
continue;
}
RecordPattern rpOther = candidatesArr[nextCandidate];
if (rpOne.recordType.tsym == rpOther.recordType.tsym) {
for (int i = 0; i < rpOne.nested.length; i++) {
if (i != mismatchingCandidate &&
!rpOne.nested[i].equals(rpOther.nested[i])) {
continue NEXT_PATTERN;
}
}
join.append(rpOther);
}
}
var nestedPatterns = join.stream().map(rp -> rp.nested[mismatchingCandidateFin]).collect(List.collector());
var updatedPatterns = reduceNestedPatterns(nestedPatterns);
updatedPatterns = reduceRecordPatterns(updatedPatterns);
updatedPatterns = reduceBindingPatterns(rpOne.fullComponentTypes()[mismatchingCandidateFin], updatedPatterns);
if (nestedPatterns != updatedPatterns) {
ListBuffer<PatternDescription> result = new ListBuffer<>();
Set<PatternDescription> toRemove = Collections.newSetFromMap(new IdentityHashMap<>());
toRemove.addAll(join);
for (PatternDescription p : patterns) {
if (!toRemove.contains(p)) {
result.append(p);
}
}
for (PatternDescription nested : updatedPatterns) {
PatternDescription[] newNested =
Arrays.copyOf(rpOne.nested, rpOne.nested.length);
newNested[mismatchingCandidateFin] = nested;
result.append(new RecordPattern(rpOne.recordType(),
rpOne.fullComponentTypes(),
newNested));
}
return result.toList();
}
}
}
}
}
return patterns;
}
/* In the set of patterns, find those for which, given:
* $record($nested1, $nested2, ...)
* all the $nestedX pattern cover the given record component,
* and replace those with a simple binding pattern over $record.
*/
private List<PatternDescription> reduceRecordPatterns(List<PatternDescription> patterns) {
var newPatterns = new ListBuffer<PatternDescription>();
boolean modified = false;
for (PatternDescription pd : patterns) {
if (pd instanceof RecordPattern rpOne) {
PatternDescription reducedPattern = reduceRecordPattern(rpOne);
if (reducedPattern != rpOne) {
newPatterns.append(reducedPattern);
modified = true;
continue;
}
}
newPatterns.append(pd);
}
return modified ? newPatterns.toList() : patterns;
}
private PatternDescription reduceRecordPattern(PatternDescription pattern) {
if (pattern instanceof RecordPattern rpOne) {
Type[] componentType = rpOne.fullComponentTypes();
//error recovery, ignore patterns with incorrect number of nested patterns:
if (componentType.length != rpOne.nested.length) {
return pattern;
}
PatternDescription[] reducedNestedPatterns = null;
boolean covered = true;
for (int i = 0; i < componentType.length; i++) {
PatternDescription newNested = reduceRecordPattern(rpOne.nested[i]);
if (newNested != rpOne.nested[i]) {
if (reducedNestedPatterns == null) {
reducedNestedPatterns = Arrays.copyOf(rpOne.nested, rpOne.nested.length);
}
reducedNestedPatterns[i] = newNested;
}
covered &= newNested instanceof BindingPattern bp &&
types.isSubtype(types.erasure(componentType[i]), types.erasure(bp.type));
}
if (covered) {
return new BindingPattern(rpOne.recordType);
} else if (reducedNestedPatterns != null) {
return new RecordPattern(rpOne.recordType, rpOne.fullComponentTypes(), reducedNestedPatterns);
}
}
return pattern;
}
public void visitTry(JCTry tree) { public void visitTry(JCTry tree) {
ListBuffer<PendingExit> prevPendingExits = pendingExits; ListBuffer<PendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>(); pendingExits = new ListBuffer<>();
@ -1374,11 +1598,7 @@ public class Flow {
} }
public void visitForeachLoop(JCEnhancedForLoop tree) { public void visitForeachLoop(JCEnhancedForLoop tree) {
if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) { visitVarDef(tree.var);
visitVarDef(jcVariableDecl);
} else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
visitRecordPattern(jcRecordPattern);
}
ListBuffer<PendingExit> prevPendingExits = pendingExits; ListBuffer<PendingExit> prevPendingExits = pendingExits;
scan(tree.expr); scan(tree.expr);
pendingExits = new ListBuffer<>(); pendingExits = new ListBuffer<>();
@ -2177,10 +2397,6 @@ public class Flow {
void scanPattern(JCTree tree) { void scanPattern(JCTree tree) {
scan(tree); scan(tree);
if (inits.isReset()) {
inits.assign(initsWhenTrue);
uninits.assign(uninitsWhenTrue);
}
} }
/** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse) /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse)
@ -2559,6 +2775,8 @@ public class Flow {
} }
public void visitForeachLoop(JCEnhancedForLoop tree) { public void visitForeachLoop(JCEnhancedForLoop tree) {
visitVarDef(tree.var);
ListBuffer<PendingExit> prevPendingExits = pendingExits; ListBuffer<PendingExit> prevPendingExits = pendingExits;
FlowKind prevFlowKind = flowKind; FlowKind prevFlowKind = flowKind;
flowKind = FlowKind.NORMAL; flowKind = FlowKind.NORMAL;
@ -2567,13 +2785,7 @@ public class Flow {
final Bits initsStart = new Bits(inits); final Bits initsStart = new Bits(inits);
final Bits uninitsStart = new Bits(uninits); final Bits uninitsStart = new Bits(uninits);
if(tree.varOrRecordPattern instanceof JCVariableDecl jcVariableDecl) { letInit(tree.pos(), tree.var.sym);
visitVarDef(jcVariableDecl);
letInit(tree.pos(), jcVariableDecl.sym);
} else if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
visitRecordPattern(jcRecordPattern);
}
pendingExits = new ListBuffer<>(); pendingExits = new ListBuffer<>();
int prevErrors = log.nerrors; int prevErrors = log.nerrors;
do { do {
@ -2625,17 +2837,10 @@ public class Flow {
for (JCCaseLabel pat : c.labels) { for (JCCaseLabel pat : c.labels) {
scanPattern(pat); scanPattern(pat);
} }
if (l.head.stats.isEmpty() && scan(c.guard);
l.tail.nonEmpty() && if (inits.isReset()) {
l.tail.head.labels.size() == 1 && inits.assign(initsWhenTrue);
TreeInfo.isNullCaseLabel(l.tail.head.labels.head)) { uninits.assign(uninitsWhenTrue);
//handling:
//case Integer i:
//case null:
//joining these two cases together - processing Integer i pattern,
//but statements from case null:
l = l.tail;
c = l.head;
} }
scan(c.stats); scan(c.stats);
if (c.completesNormally && c.caseKind == JCCase.RULE) { if (c.completesNormally && c.caseKind == JCCase.RULE) {
@ -3028,12 +3233,6 @@ public class Flow {
initParam(tree.var); initParam(tree.var);
} }
@Override
public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
scan(tree.pat);
scan(tree.guard);
}
void referenced(Symbol sym) { void referenced(Symbol sym) {
unrefdResources.remove(sym); unrefdResources.remove(sym);
} }
@ -3104,6 +3303,7 @@ public class Flow {
class CaptureAnalyzer extends BaseAnalyzer { class CaptureAnalyzer extends BaseAnalyzer {
JCTree currentTree; //local class or lambda JCTree currentTree; //local class or lambda
WriteableScope declaredInsideGuard;
@Override @Override
void markDead() { void markDead() {
@ -3117,7 +3317,7 @@ public class Flow {
sym.pos < currentTree.getStartPosition()) { sym.pos < currentTree.getStartPosition()) {
switch (currentTree.getTag()) { switch (currentTree.getTag()) {
case CLASSDEF: case CLASSDEF:
case PATTERNCASELABEL: case CASE:
case LAMBDA: case LAMBDA:
if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) { if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) {
reportEffectivelyFinalError(pos, sym); reportEffectivelyFinalError(pos, sym);
@ -3131,15 +3331,20 @@ public class Flow {
tree = TreeInfo.skipParens(tree); tree = TreeInfo.skipParens(tree);
if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) {
Symbol sym = TreeInfo.symbol(tree); Symbol sym = TreeInfo.symbol(tree);
if (currentTree != null && if (currentTree != null) {
sym.kind == VAR &&
sym.owner.kind == MTH &&
((VarSymbol)sym).pos < currentTree.getStartPosition()) {
switch (currentTree.getTag()) { switch (currentTree.getTag()) {
case CLASSDEF: case CLASSDEF, LAMBDA -> {
case CASE: if (sym.kind == VAR &&
case LAMBDA: sym.owner.kind == MTH &&
reportEffectivelyFinalError(tree, sym); ((VarSymbol)sym).pos < currentTree.getStartPosition()) {
reportEffectivelyFinalError(tree, sym);
}
}
case CASE -> {
if (!declaredInsideGuard.includes(sym)) {
log.error(tree.pos(), Errors.CannotAssignNotDeclaredGuard(sym));
}
}
} }
} }
} }
@ -3148,7 +3353,7 @@ public class Flow {
void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) { void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) {
Fragment subKey = switch (currentTree.getTag()) { Fragment subKey = switch (currentTree.getTag()) {
case LAMBDA -> Fragments.Lambda; case LAMBDA -> Fragments.Lambda;
case PATTERNCASELABEL -> Fragments.Guard; case CASE -> Fragments.Guard;
case CLASSDEF -> Fragments.InnerCls; case CLASSDEF -> Fragments.InnerCls;
default -> throw new AssertionError("Unexpected tree kind: " + currentTree.getTag()); default -> throw new AssertionError("Unexpected tree kind: " + currentTree.getTag());
}; };
@ -3188,20 +3393,21 @@ public class Flow {
} }
@Override @Override
public void visitParenthesizedPattern(JCParenthesizedPattern tree) { public void visitCase(JCCase tree) {
scan(tree.pattern); scan(tree.labels);
} if (tree.guard != null) {
JCTree prevTree = currentTree;
@Override WriteableScope prevDeclaredInsideGuard = declaredInsideGuard;
public void visitPatternCaseLabel(JCPatternCaseLabel tree) { try {
scan(tree.pat); currentTree = tree;
JCTree prevTree = currentTree; declaredInsideGuard = WriteableScope.create(attrEnv.enclClass.sym);
try { scan(tree.guard);
currentTree = tree; } finally {
scan(tree.guard); currentTree = prevTree;
} finally { declaredInsideGuard = prevDeclaredInsideGuard;
currentTree = prevTree; }
} }
scan(tree.stats);
} }
@Override @Override
@ -3256,6 +3462,14 @@ public class Flow {
super.visitTry(tree); super.visitTry(tree);
} }
@Override
public void visitVarDef(JCVariableDecl tree) {
if (declaredInsideGuard != null) {
declaredInsideGuard.enter(tree.sym);
}
super.visitVarDef(tree);
}
@Override @Override
public void visitYield(JCYield tree) { public void visitYield(JCYield tree) {
scan(tree.value); scan(tree.value);

View File

@ -703,7 +703,7 @@ public class LambdaToMethod extends TreeTranslator {
JCBreak br = make.Break(null); JCBreak br = make.Break(null);
breaks.add(br); breaks.add(br);
List<JCStatement> stmts = entry.getValue().append(br).toList(); List<JCStatement> stmts = entry.getValue().append(br).toList();
cases.add(make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(make.Literal(entry.getKey()))), stmts, null)); cases.add(make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(make.Literal(entry.getKey()))), null, stmts, null));
} }
JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList()); JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
for (JCBreak br : breaks) { for (JCBreak br : breaks) {

View File

@ -28,7 +28,6 @@ package com.sun.tools.javac.comp;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Kinds.KindSelector; import com.sun.tools.javac.code.Kinds.KindSelector;
import com.sun.tools.javac.code.Scope.WriteableScope; import com.sun.tools.javac.code.Scope.WriteableScope;
@ -3558,18 +3557,13 @@ public class Lower extends TreeTranslator {
Type elemtype = types.elemtype(tree.expr.type); Type elemtype = types.elemtype(tree.expr.type);
JCExpression loopvarinit = make.Indexed(make.Ident(arraycache), JCExpression loopvarinit = make.Indexed(make.Ident(arraycache),
make.Ident(index)).setType(elemtype); make.Ident(index)).setType(elemtype);
JCVariableDecl loopvardef = (JCVariableDecl)make.VarDef(tree.var.mods,
Assert.check(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE); tree.var.name,
JCVariableDecl jcVariableDecl = (JCVariableDecl) tree.varOrRecordPattern; tree.var.vartype,
loopvarinit).setType(tree.var.type);
JCVariableDecl loopvardef = (JCVariableDecl)make.VarDef(jcVariableDecl.mods, loopvardef.sym = tree.var.sym;
jcVariableDecl.name,
jcVariableDecl.vartype,
loopvarinit).setType(jcVariableDecl.type);
loopvardef.sym = jcVariableDecl.sym;
JCBlock body = make. JCBlock body = make.
Block(0, List.of(loopvardef, tree.body)); Block(0, List.of(loopvardef, tree.body));
result = translate(make. result = translate(make.
ForLoop(loopinit, ForLoop(loopinit,
@ -3648,26 +3642,22 @@ public class Lower extends TreeTranslator {
itvar.type, itvar.type,
List.nil()); List.nil());
JCExpression vardefinit = make.App(make.Select(make.Ident(itvar), next)); JCExpression vardefinit = make.App(make.Select(make.Ident(itvar), next));
if (tree.var.type.isPrimitive())
Assert.check(tree.getDeclarationKind() == EnhancedForLoopTree.DeclarationKind.VARIABLE);
JCVariableDecl var = (JCVariableDecl) tree.varOrRecordPattern;
if (var.type.isPrimitive())
vardefinit = make.TypeCast(types.cvarUpperBound(iteratorTarget), vardefinit); vardefinit = make.TypeCast(types.cvarUpperBound(iteratorTarget), vardefinit);
else else
vardefinit = make.TypeCast(var.type, vardefinit); vardefinit = make.TypeCast(tree.var.type, vardefinit);
JCVariableDecl indexDef = (JCVariableDecl) make.VarDef(var.mods, JCVariableDecl indexDef = (JCVariableDecl)make.VarDef(tree.var.mods,
var.name, tree.var.name,
var.vartype, tree.var.vartype,
vardefinit).setType(var.type); vardefinit).setType(tree.var.type);
indexDef.sym = var.sym; indexDef.sym = tree.var.sym;
JCBlock body = make.Block(0, List.of(indexDef, tree.body)); JCBlock body = make.Block(0, List.of(indexDef, tree.body));
body.endpos = TreeInfo.endPos(tree.body); body.endpos = TreeInfo.endPos(tree.body);
result = translate(make. result = translate(make.
ForLoop(List.of(init), ForLoop(List.of(init),
cond, cond,
List.nil(), List.nil(),
body)); body));
patchTargets(body, tree, result); patchTargets(body, tree, result);
} }
@ -3753,7 +3743,7 @@ public class Lower extends TreeTranslator {
List<JCExpression> params = matchException ? List.of(makeNull(), makeNull()) List<JCExpression> params = matchException ? List.of(makeNull(), makeNull())
: List.nil(); : List.nil();
JCThrow thr = make.Throw(makeNewClass(exception, params)); JCThrow thr = make.Throw(makeNewClass(exception, params));
JCCase c = make.Case(JCCase.STATEMENT, List.of(make.DefaultCaseLabel()), List.of(thr), null); JCCase c = make.Case(JCCase.STATEMENT, List.of(make.DefaultCaseLabel()), null, List.of(thr), null);
cases = cases.prepend(c); cases = cases.prepend(c);
} }
@ -3780,6 +3770,7 @@ public class Lower extends TreeTranslator {
while (patterns.tail.nonEmpty()) { while (patterns.tail.nonEmpty()) {
convertedCases.append(make_at(c.pos()).Case(JCCase.STATEMENT, convertedCases.append(make_at(c.pos()).Case(JCCase.STATEMENT,
List.of(patterns.head), List.of(patterns.head),
null,
List.nil(), List.nil(),
null)); null));
patterns = patterns.tail; patterns = patterns.tail;
@ -3874,7 +3865,7 @@ public class Lower extends TreeTranslator {
VarSymbol label = (VarSymbol)TreeInfo.symbol(((JCConstantCaseLabel) c.labels.head).expr); VarSymbol label = (VarSymbol)TreeInfo.symbol(((JCConstantCaseLabel) c.labels.head).expr);
pat = map.caseValue(label); pat = map.caseValue(label);
} }
newCases.append(make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(pat)), c.stats, null)); newCases.append(make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(pat)), null, c.stats, null));
} else { } else {
newCases.append(c); newCases.append(c);
} }
@ -4046,6 +4037,7 @@ public class Lower extends TreeTranslator {
caseBuffer.append(make.Case(JCCase.STATEMENT, caseBuffer.append(make.Case(JCCase.STATEMENT,
List.of(make.ConstantCaseLabel(make.Literal(hashCode))), List.of(make.ConstantCaseLabel(make.Literal(hashCode))),
null,
lb.toList(), lb.toList(),
null)); null));
} }
@ -4081,6 +4073,7 @@ public class Lower extends TreeTranslator {
lb.append(make.Case(JCCase.STATEMENT, caseExpr == null ? List.of(make.DefaultCaseLabel()) lb.append(make.Case(JCCase.STATEMENT, caseExpr == null ? List.of(make.DefaultCaseLabel())
: List.of(make.ConstantCaseLabel(caseExpr)), : List.of(make.ConstantCaseLabel(caseExpr)),
null,
oneCase.stats, null)); oneCase.stats, null));
} }

View File

@ -142,7 +142,7 @@ public class MatchBindingsComputer extends TreeScanner {
public MatchBindings finishBindings(JCTree tree, MatchBindings matchBindings) { public MatchBindings finishBindings(JCTree tree, MatchBindings matchBindings) {
switch (tree.getTag()) { switch (tree.getTag()) {
case NOT: case AND: case OR: case BINDINGPATTERN: case NOT: case AND: case OR: case BINDINGPATTERN:
case PARENTHESIZEDPATTERN: case TYPETEST: case TYPETEST:
case PARENS: case RECORDPATTERN: case PARENS: case RECORDPATTERN:
case CONDEXPR: //error recovery: case CONDEXPR: //error recovery:
return matchBindings; return matchBindings;

View File

@ -957,10 +957,6 @@ class ThisEscapeAnalyzer extends TreeScanner {
public void visitPatternCaseLabel(JCPatternCaseLabel tree) { public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
} }
@Override
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
}
@Override @Override
public void visitRecordPattern(JCRecordPattern that) { public void visitRecordPattern(JCRecordPattern that) {
} }

View File

@ -36,6 +36,8 @@ import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.BindingSymbol; import com.sun.tools.javac.code.Symbol.BindingSymbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol; import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
import com.sun.tools.javac.code.Symbol.DynamicVarSymbol;
import com.sun.tools.javac.code.Symbol.MethodHandleSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type.ClassType; import com.sun.tools.javac.code.Type.ClassType;
@ -55,7 +57,6 @@ import com.sun.tools.javac.tree.JCTree.JCSwitch;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.JCBindingPattern; import com.sun.tools.javac.tree.JCTree.JCBindingPattern;
import com.sun.tools.javac.tree.JCTree.JCWhileLoop; import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
import com.sun.tools.javac.tree.JCTree.JCThrow;
import com.sun.tools.javac.tree.JCTree.Tag; import com.sun.tools.javac.tree.JCTree.Tag;
import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator; import com.sun.tools.javac.tree.TreeTranslator;
@ -94,7 +95,6 @@ import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCLambda; import com.sun.tools.javac.tree.JCTree.JCLambda;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCNewClass; import com.sun.tools.javac.tree.JCTree.JCNewClass;
import com.sun.tools.javac.tree.JCTree.JCParenthesizedPattern;
import com.sun.tools.javac.tree.JCTree.JCPattern; import com.sun.tools.javac.tree.JCTree.JCPattern;
import com.sun.tools.javac.tree.JCTree.JCPatternCaseLabel; import com.sun.tools.javac.tree.JCTree.JCPatternCaseLabel;
import com.sun.tools.javac.tree.JCTree.JCRecordPattern; import com.sun.tools.javac.tree.JCTree.JCRecordPattern;
@ -105,6 +105,8 @@ import com.sun.tools.javac.tree.JCTree.LetExpr;
import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.tree.TreeScanner; import com.sun.tools.javac.tree.TreeScanner;
import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.List;
/** /**
@ -196,8 +198,7 @@ public class TransPatterns extends TreeTranslator {
@Override @Override
public void visitTypeTest(JCInstanceOf tree) { public void visitTypeTest(JCInstanceOf tree) {
if (tree.pattern instanceof JCPattern pattern) { if (tree.pattern instanceof JCPattern pattern) {
//first, resolve any parenthesized and record patterns: //first, resolve any record patterns:
pattern = TreeInfo.skipParens(pattern);
JCExpression extraConditions = null; JCExpression extraConditions = null;
if (pattern instanceof JCRecordPattern recordPattern) { if (pattern instanceof JCRecordPattern recordPattern) {
UnrolledRecordPattern unrolledRecordPattern = unrollRecordPattern(recordPattern); UnrolledRecordPattern unrolledRecordPattern = unrollRecordPattern(recordPattern);
@ -280,11 +281,6 @@ public class TransPatterns extends TreeTranslator {
} }
} }
@Override
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
result = translate(tree.pattern);
}
@Override @Override
public void visitRecordPattern(JCRecordPattern tree) { public void visitRecordPattern(JCRecordPattern tree) {
//record patterns should be resolved by the constructs that use them. //record patterns should be resolved by the constructs that use them.
@ -314,7 +310,7 @@ public class TransPatterns extends TreeTranslator {
while (components.nonEmpty()) { while (components.nonEmpty()) {
RecordComponent component = components.head; RecordComponent component = components.head;
Type componentType = types.erasure(nestedFullComponentTypes.head); Type componentType = types.erasure(nestedFullComponentTypes.head);
JCPattern nestedPattern = TreeInfo.skipParens(nestedPatterns.head); JCPattern nestedPattern = nestedPatterns.head;
JCBindingPattern nestedBinding; JCBindingPattern nestedBinding;
boolean allowNull; boolean allowNull;
if (nestedPattern instanceof JCRecordPattern nestedRecordPattern) { if (nestedPattern instanceof JCRecordPattern nestedRecordPattern) {
@ -390,8 +386,6 @@ public class TransPatterns extends TreeTranslator {
Type seltype = selector.type.hasTag(BOT) Type seltype = selector.type.hasTag(BOT)
? syms.objectType ? syms.objectType
: selector.type; : selector.type;
Assert.check(preview.isEnabled());
Assert.check(preview.usesPreview(env.toplevel.sourcefile));
//rewrite pattern matching switches, performed in several steps: //rewrite pattern matching switches, performed in several steps:
//1. record patterns are unrolled into a binding pattern and guards using unrollRecordPattern //1. record patterns are unrolled into a binding pattern and guards using unrollRecordPattern
@ -436,16 +430,19 @@ public class TransPatterns extends TreeTranslator {
//note the selector is evaluated only once and stored in a temporary variable //note the selector is evaluated only once and stored in a temporary variable
ListBuffer<JCCase> newCases = new ListBuffer<>(); ListBuffer<JCCase> newCases = new ListBuffer<>();
for (List<JCCase> c = cases; c.nonEmpty(); c = c.tail) { for (List<JCCase> c = cases; c.nonEmpty(); c = c.tail) {
c.head.labels = c.head.labels.map(l -> { JCCase cse = c.head;
cse.labels = cse.labels.map(l -> {
if (l instanceof JCPatternCaseLabel patternLabel) { if (l instanceof JCPatternCaseLabel patternLabel) {
JCPattern pattern = TreeInfo.skipParens(patternLabel.pat); JCPattern pattern = patternLabel.pat;
if (pattern instanceof JCRecordPattern recordPattern) { if (pattern instanceof JCRecordPattern recordPattern) {
UnrolledRecordPattern deconstructed = unrollRecordPattern(recordPattern); UnrolledRecordPattern deconstructed = unrollRecordPattern(recordPattern);
JCExpression guard = deconstructed.newGuard(); JCExpression guard = deconstructed.newGuard();
if (patternLabel.guard != null) { if (cse.guard != null) {
guard = mergeConditions(guard, patternLabel.guard); cse.guard = mergeConditions(guard, cse.guard);
} else {
cse.guard = guard;
} }
return make.PatternCaseLabel(deconstructed.primaryPattern(), guard); return make.PatternCaseLabel(deconstructed.primaryPattern());
} }
} }
return l; return l;
@ -534,8 +531,8 @@ public class TransPatterns extends TreeTranslator {
try { try {
currentValue = temp; currentValue = temp;
JCExpression test = (JCExpression) this.<JCTree>translate(label.pat); JCExpression test = (JCExpression) this.<JCTree>translate(label.pat);
if (label.guard != null) { if (c.guard != null) {
JCExpression guard = translate(label.guard); JCExpression guard = translate(c.guard);
test = makeBinary(Tag.AND, test, guard); test = makeBinary(Tag.AND, test, guard);
} }
c.stats = translate(c.stats); c.stats = translate(c.stats);
@ -716,12 +713,12 @@ public class TransPatterns extends TreeTranslator {
replaceNested.scan(accummulated); replaceNested.scan(accummulated);
JCExpression newGuard; JCExpression newGuard;
JCInstanceOf instanceofCheck; JCInstanceOf instanceofCheck;
if (accummulatedFirstLabel.guard instanceof JCBinary binOp) { if (accummulated.guard instanceof JCBinary binOp) {
newGuard = binOp.rhs; newGuard = binOp.rhs;
instanceofCheck = (JCInstanceOf) binOp.lhs; instanceofCheck = (JCInstanceOf) binOp.lhs;
} else { } else {
newGuard = null; newGuard = null;
instanceofCheck = (JCInstanceOf) accummulatedFirstLabel.guard; instanceofCheck = (JCInstanceOf) accummulated.guard;
} }
JCBindingPattern binding = (JCBindingPattern) instanceofCheck.pattern; JCBindingPattern binding = (JCBindingPattern) instanceofCheck.pattern;
hasUnconditional = hasUnconditional =
@ -730,11 +727,12 @@ public class TransPatterns extends TreeTranslator {
List<JCCaseLabel> newLabel; List<JCCaseLabel> newLabel;
if (hasUnconditional) { if (hasUnconditional) {
newLabel = List.of(make.ConstantCaseLabel(makeNull()), newLabel = List.of(make.ConstantCaseLabel(makeNull()),
make.PatternCaseLabel(binding, newGuard)); make.PatternCaseLabel(binding));
} else { } else {
newLabel = List.of(make.PatternCaseLabel(binding, newGuard)); newLabel = List.of(make.PatternCaseLabel(binding));
} }
nestedCases.add(make.Case(CaseKind.STATEMENT, newLabel, accummulated.stats, null)); nestedCases.add(make.Case(CaseKind.STATEMENT, newLabel, newGuard,
accummulated.stats, null));
lastGuard = newGuard; lastGuard = newGuard;
} }
if (lastGuard != null || !hasUnconditional) { if (lastGuard != null || !hasUnconditional) {
@ -745,6 +743,7 @@ public class TransPatterns extends TreeTranslator {
? List.of(make.DefaultCaseLabel()) ? List.of(make.DefaultCaseLabel())
: List.of(make.ConstantCaseLabel(makeNull()), : List.of(make.ConstantCaseLabel(makeNull()),
make.DefaultCaseLabel()), make.DefaultCaseLabel()),
null,
List.of(continueSwitch), List.of(continueSwitch),
null)); null));
} }
@ -752,9 +751,9 @@ public class TransPatterns extends TreeTranslator {
newSwitch.patternSwitch = true; newSwitch.patternSwitch = true;
JCPatternCaseLabel leadingTest = JCPatternCaseLabel leadingTest =
(JCPatternCaseLabel) accummulator.first().labels.head; (JCPatternCaseLabel) accummulator.first().labels.head;
leadingTest.guard = null;
result.add(make.Case(CaseKind.STATEMENT, result.add(make.Case(CaseKind.STATEMENT,
List.of(leadingTest), List.of(leadingTest),
null,
List.of(newSwitch), List.of(newSwitch),
null)); null));
} else { } else {
@ -776,14 +775,14 @@ public class TransPatterns extends TreeTranslator {
if (c.head.labels.size() == 1 && if (c.head.labels.size() == 1 &&
c.head.labels.head instanceof JCPatternCaseLabel patternLabel) { c.head.labels.head instanceof JCPatternCaseLabel patternLabel) {
if (patternLabel.guard instanceof JCBinary binOp && if (c.head.guard instanceof JCBinary binOp &&
binOp.lhs instanceof JCInstanceOf instanceofCheck && binOp.lhs instanceof JCInstanceOf instanceofCheck &&
instanceofCheck.pattern instanceof JCBindingPattern binding) { instanceofCheck.pattern instanceof JCBindingPattern binding) {
currentBinding = ((JCBindingPattern) patternLabel.pat).var.sym; currentBinding = ((JCBindingPattern) patternLabel.pat).var.sym;
currentNullable = instanceofCheck.allowNull; currentNullable = instanceofCheck.allowNull;
currentNestedExpression = instanceofCheck.expr; currentNestedExpression = instanceofCheck.expr;
currentNestedBinding = binding.var.sym; currentNestedBinding = binding.var.sym;
} else if (patternLabel.guard instanceof JCInstanceOf instanceofCheck && } else if (c.head.guard instanceof JCInstanceOf instanceofCheck &&
instanceofCheck.pattern instanceof JCBindingPattern binding) { instanceofCheck.pattern instanceof JCBindingPattern binding) {
currentBinding = ((JCBindingPattern) patternLabel.pat).var.sym; currentBinding = ((JCBindingPattern) patternLabel.pat).var.sym;
currentNullable = instanceofCheck.allowNull; currentNullable = instanceofCheck.allowNull;
@ -835,10 +834,15 @@ public class TransPatterns extends TreeTranslator {
} else { } else {
return (LoadableConstant) principalType; return (LoadableConstant) principalType;
} }
} else if (l.hasTag(Tag.CONSTANTCASELABEL)&& !TreeInfo.isNullCaseLabel(l)) { } else if (l.hasTag(Tag.CONSTANTCASELABEL) && !TreeInfo.isNullCaseLabel(l)) {
JCExpression expr = ((JCConstantCaseLabel) l).expr; JCExpression expr = ((JCConstantCaseLabel) l).expr;
if ((expr.type.tsym.flags_field & Flags.ENUM) != 0) { Symbol sym = TreeInfo.symbol(expr);
return LoadableConstant.String(((JCIdent) expr).name.toString()); if (sym != null && sym.isEnum() && sym.kind == Kind.VAR) {
if (selector.tsym.isEnum()) {
return LoadableConstant.String(sym.getSimpleName().toString());
} else {
return createEnumDesc(l.pos(), (ClassSymbol) sym.owner, sym.getSimpleName());
}
} else { } else {
Assert.checkNonNull(expr.type.constValue()); Assert.checkNonNull(expr.type.constValue());
@ -854,6 +858,38 @@ public class TransPatterns extends TreeTranslator {
} }
} }
private LoadableConstant createEnumDesc(DiagnosticPosition pos, ClassSymbol enumClass, Name constant) {
MethodSymbol classDesc = rs.resolveInternalMethod(pos, env, syms.classDescType, names.of, List.of(syms.stringType), List.nil());
MethodSymbol enumDesc = rs.resolveInternalMethod(pos, env, syms.enumDescType, names.of, List.of(syms.classDescType, syms.stringType), List.nil());
return invokeMethodWrapper(pos,
enumDesc.asHandle(),
invokeMethodWrapper(pos,
classDesc.asHandle(),
LoadableConstant.String(enumClass.flatname.toString())),
LoadableConstant.String(constant.toString()));
}
private LoadableConstant invokeMethodWrapper(DiagnosticPosition pos, MethodHandleSymbol toCall, LoadableConstant... params) {
List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
syms.stringType,
new ClassType(syms.classType.getEnclosingType(),
List.of(syms.botType), //XXX - botType
syms.classType.tsym),
syms.methodHandleType,
types.makeArrayType(syms.objectType));
MethodSymbol bsm = rs.resolveInternalMethod(pos, env, syms.constantBootstrapsType,
names.invoke, bsm_staticArgs, List.nil());
LoadableConstant[] actualParams = new LoadableConstant[params.length + 1];
actualParams[0] = toCall;
System.arraycopy(params, 0, actualParams, 1, params.length);
return new DynamicVarSymbol(bsm.name, bsm.owner, bsm.asHandle(), toCall.getReturnType(), actualParams);
}
@Override @Override
public void visitBinary(JCBinary tree) { public void visitBinary(JCBinary tree) {
bindingContext = new BasicBindingContext(); bindingContext = new BasicBindingContext();
@ -898,77 +934,6 @@ public class TransPatterns extends TreeTranslator {
} }
} }
@Override
public void visitForeachLoop(JCTree.JCEnhancedForLoop tree) {
bindingContext = new BasicBindingContext();
VarSymbol prevCurrentValue = currentValue;
try {
if (tree.varOrRecordPattern instanceof JCRecordPattern jcRecordPattern) {
/**
* A statement of the form
*
* <pre>
* for (<pattern> : coll ) stmt ;
* </pre>
*
* (where coll implements {@code Iterable<R>}) gets translated to
*
* <pre>{@code
* for (<type-of-coll-item> N$temp : coll) {
* switch (N$temp) {
* case <pattern>: stmt;
* case null: throw new MatchException();
* }
* }</pre>
*
*/
Type selectorType = types.classBound(tree.elementType);
currentValue = new VarSymbol(Flags.FINAL | Flags.SYNTHETIC,
names.fromString("patt" + tree.pos + target.syntheticNameChar() + "temp"),
selectorType,
currentMethodSym);
JCStatement newForVariableDeclaration =
make.at(tree.pos).VarDef(currentValue, null).setType(selectorType);
List<JCExpression> nestedNPEParams = List.of(makeNull());
JCNewClass nestedNPE = makeNewClass(syms.nullPointerExceptionType, nestedNPEParams);
List<JCExpression> matchExParams = List.of(makeNull(), nestedNPE);
JCThrow thr = make.Throw(makeNewClass(syms.matchExceptionType, matchExParams));
JCCase caseNull = make.Case(JCCase.STATEMENT, List.of(make.ConstantCaseLabel(makeNull())), List.of(thr), null);
JCCase casePattern = make.Case(CaseTree.CaseKind.STATEMENT,
List.of(make.PatternCaseLabel(jcRecordPattern, null)),
List.of(translate(tree.body)),
null);
JCSwitch switchBody =
make.Switch(make.Ident(currentValue).setType(selectorType),
List.of(caseNull, casePattern));
switchBody.patternSwitch = true;
// re-using the same node to eliminate the need to re-patch targets (break/continue)
tree.varOrRecordPattern = newForVariableDeclaration.setType(selectorType);
tree.expr = translate(tree.expr);
tree.body = translate(switchBody);
JCTree.JCEnhancedForLoop newForEach = tree;
result = bindingContext.decorateStatement(newForEach);
} else {
super.visitForeachLoop(tree);
result = bindingContext.decorateStatement(tree);
}
} finally {
currentValue = prevCurrentValue;
bindingContext.pop();
}
}
@Override @Override
public void visitWhileLoop(JCWhileLoop tree) { public void visitWhileLoop(JCWhileLoop tree) {
bindingContext = new BasicBindingContext(); bindingContext = new BasicBindingContext();

View File

@ -512,7 +512,7 @@ public class TransTypes extends TreeTranslator {
} }
public void visitForeachLoop(JCEnhancedForLoop tree) { public void visitForeachLoop(JCEnhancedForLoop tree) {
tree.varOrRecordPattern = translate(tree.varOrRecordPattern, null); tree.var = translate(tree.var, null);
Type iterableType = tree.expr.type; Type iterableType = tree.expr.type;
tree.expr = translate(tree.expr, erasure(tree.expr.type)); tree.expr = translate(tree.expr, erasure(tree.expr.type));
if (types.elemtype(tree.expr.type) == null) if (types.elemtype(tree.expr.type) == null)
@ -551,6 +551,7 @@ public class TransTypes extends TreeTranslator {
public void visitCase(JCCase tree) { public void visitCase(JCCase tree) {
tree.labels = translate(tree.labels, null); tree.labels = translate(tree.labels, null);
tree.guard = translate(tree.guard, syms.booleanType);
tree.stats = translate(tree.stats); tree.stats = translate(tree.stats);
result = tree; result = tree;
} }
@ -569,7 +570,6 @@ public class TransTypes extends TreeTranslator {
@Override @Override
public void visitPatternCaseLabel(JCPatternCaseLabel tree) { public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
tree.pat = translate(tree.pat, null); tree.pat = translate(tree.pat, null);
tree.guard = translate(tree.guard, syms.booleanType);
result = tree; result = tree;
} }
@ -584,12 +584,6 @@ public class TransTypes extends TreeTranslator {
result = retype(tree, tree.type, pt); result = retype(tree, tree.type, pt);
} }
@Override
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
tree.pattern = translate(tree.pattern, null);
result = tree;
}
public void visitRecordPattern(JCRecordPattern tree) { public void visitRecordPattern(JCRecordPattern tree) {
tree.fullComponentTypes = tree.record.getRecordComponents() tree.fullComponentTypes = tree.record.getRecordComponents()
.map(rc -> types.memberType(tree.type, rc)); .map(rc -> types.memberType(tree.type, rc));

View File

@ -297,7 +297,9 @@ public class TreeDiffer extends TreeScanner {
@Override @Override
public void visitCase(JCCase tree) { public void visitCase(JCCase tree) {
JCCase that = (JCCase) parameter; JCCase that = (JCCase) parameter;
result = scan(tree.labels, that.labels) && scan(tree.stats, that.stats); result = scan(tree.labels, that.labels) &&
scan(tree.guard, that.guard) &&
scan(tree.stats, that.stats);
} }
@Override @Override
@ -309,7 +311,7 @@ public class TreeDiffer extends TreeScanner {
@Override @Override
public void visitPatternCaseLabel(JCPatternCaseLabel tree) { public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
JCPatternCaseLabel that = (JCPatternCaseLabel) parameter; JCPatternCaseLabel that = (JCPatternCaseLabel) parameter;
result = scan(tree.pat, that.pat) && scan(tree.guard, that.guard); result = scan(tree.pat, that.pat);
} }
@Override @Override
@ -388,7 +390,7 @@ public class TreeDiffer extends TreeScanner {
public void visitForeachLoop(JCEnhancedForLoop tree) { public void visitForeachLoop(JCEnhancedForLoop tree) {
JCEnhancedForLoop that = (JCEnhancedForLoop) parameter; JCEnhancedForLoop that = (JCEnhancedForLoop) parameter;
result = result =
scan(tree.varOrRecordPattern, that.varOrRecordPattern) scan(tree.var, that.var)
&& scan(tree.expr, that.expr) && scan(tree.expr, that.expr)
&& scan(tree.body, that.body); && scan(tree.body, that.body);
} }

View File

@ -293,7 +293,7 @@ implements CRTFlags {
public void visitForeachLoop(JCEnhancedForLoop tree) { public void visitForeachLoop(JCEnhancedForLoop tree) {
SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
sr.mergeWith(csp(tree.varOrRecordPattern)); sr.mergeWith(csp(tree.var));
sr.mergeWith(csp(tree.expr)); sr.mergeWith(csp(tree.expr));
sr.mergeWith(csp(tree.body)); sr.mergeWith(csp(tree.body));
result = sr; result = sr;
@ -323,6 +323,7 @@ implements CRTFlags {
public void visitCase(JCCase tree) { public void visitCase(JCCase tree) {
SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
sr.mergeWith(csp(tree.labels)); sr.mergeWith(csp(tree.labels));
sr.mergeWith(csp(tree.guard));
sr.mergeWith(csp(tree.stats)); sr.mergeWith(csp(tree.stats));
result = sr; result = sr;
} }
@ -343,7 +344,6 @@ implements CRTFlags {
public void visitPatternCaseLabel(JCPatternCaseLabel tree) { public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
sr.mergeWith(csp(tree.pat)); sr.mergeWith(csp(tree.pat));
sr.mergeWith(csp(tree.guard));
result = sr; result = sr;
} }

View File

@ -941,6 +941,15 @@ public class ClassWriter extends ClassFile {
*/ */
void writeBootstrapMethods() { void writeBootstrapMethods() {
int alenIdx = writeAttr(names.BootstrapMethods); int alenIdx = writeAttr(names.BootstrapMethods);
int lastBootstrapMethods;
do {
lastBootstrapMethods = poolWriter.bootstrapMethods.size();
for (BsmKey bsmKey : java.util.List.copyOf(poolWriter.bootstrapMethods.keySet())) {
for (LoadableConstant arg : bsmKey.staticArgs) {
poolWriter.putConstant(arg);
}
}
} while (lastBootstrapMethods < poolWriter.bootstrapMethods.size());
databuf.appendChar(poolWriter.bootstrapMethods.size()); databuf.appendChar(poolWriter.bootstrapMethods.size());
for (BsmKey bsmKey : poolWriter.bootstrapMethods.keySet()) { for (BsmKey bsmKey : poolWriter.bootstrapMethods.keySet()) {
//write BSM handle //write BSM handle

View File

@ -1535,11 +1535,6 @@ public class JavaCompiler {
super.visitRecordPattern(that); super.visitRecordPattern(that);
} }
@Override @Override
public void visitParenthesizedPattern(JCTree.JCParenthesizedPattern tree) {
hasPatterns = true;
super.visitParenthesizedPattern(tree);
}
@Override
public void visitSwitch(JCSwitch tree) { public void visitSwitch(JCSwitch tree) {
hasPatterns |= tree.patternSwitch; hasPatterns |= tree.patternSwitch;
super.visitSwitch(tree); super.visitSwitch(tree);

View File

@ -830,48 +830,52 @@ public class JavacParser implements Parser {
/** parses patterns. /** parses patterns.
*/ */
public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType, public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType,
boolean allowVar, boolean checkGuard) { boolean allowVar, boolean checkGuard) {
JCPattern pattern; JCPattern pattern;
if (token.kind == LPAREN && parsedType == null) { mods = mods != null ? mods : optFinal(0);
//parenthesized pattern: JCExpression e;
int startPos = token.pos; if (parsedType == null) {
accept(LPAREN); boolean var = token.kind == IDENTIFIER && token.name() == names.var;
JCPattern p = parsePattern(token.pos, null, null, true, false); e = unannotatedType(allowVar, TYPE | NOLAMBDA);
accept(RPAREN); if (var) {
pattern = toP(F.at(startPos).ParenthesizedPattern(p)); e = null;
}
} else { } else {
mods = mods != null ? mods : optFinal(0); e = parsedType;
JCExpression e; }
if (parsedType == null) { if (token.kind == LPAREN) {
boolean var = token.kind == IDENTIFIER && token.name() == names.var; //deconstruction pattern:
e = unannotatedType(allowVar, TYPE | NOLAMBDA); checkSourceLevel(Feature.RECORD_PATTERNS);
if (var) { ListBuffer<JCPattern> nested = new ListBuffer<>();
e = null; if (!peekToken(RPAREN)) {
} do {
} else {
e = parsedType;
}
if (token.kind == LPAREN) {
//deconstruction pattern:
checkSourceLevel(Feature.RECORD_PATTERNS);
ListBuffer<JCPattern> nested = new ListBuffer<>();
if (!peekToken(RPAREN)) {
do {
nextToken();
JCPattern nestedPattern = parsePattern(token.pos, null, null, true, false);
nested.append(nestedPattern);
} while (token.kind == COMMA);
} else {
nextToken(); nextToken();
} JCPattern nestedPattern = parsePattern(token.pos, null, null, true, false);
accept(RPAREN); nested.append(nestedPattern);
pattern = toP(F.at(pos).RecordPattern(e, nested.toList())); } while (token.kind == COMMA);
} else { } else {
//type test pattern: nextToken();
JCVariableDecl var = toP(F.at(token.pos).VarDef(mods, ident(), e, null));
pattern = toP(F.at(pos).BindingPattern(var));
} }
accept(RPAREN);
pattern = toP(F.at(pos).RecordPattern(e, nested.toList()));
if (mods.annotations.nonEmpty()) {
log.error(mods.annotations.head.pos(), Errors.RecordPatternsAnnotationsNotAllowed);
}
new TreeScanner() {
@Override
public void visitAnnotatedType(JCAnnotatedType tree) {
log.error(tree.pos(), Errors.RecordPatternsAnnotationsNotAllowed);
}
}.scan(e);
} else {
//type test pattern:
JCVariableDecl var = toP(F.at(token.pos).VarDef(mods, ident(), e, null));
if (e == null) {
var.startPos = pos;
}
pattern = toP(F.at(pos).BindingPattern(var));
} }
return pattern; return pattern;
} }
@ -1620,6 +1624,7 @@ public class JavacParser implements Parser {
allowDefault = TreeInfo.isNullCaseLabel(label); allowDefault = TreeInfo.isNullCaseLabel(label);
}; };
} }
JCExpression guard = parseGuard(pats.last());
List<JCStatement> stats = null; List<JCStatement> stats = null;
JCTree body = null; JCTree body = null;
CaseTree.CaseKind kind; CaseTree.CaseKind kind;
@ -1645,7 +1650,7 @@ public class JavacParser implements Parser {
kind = JCCase.STATEMENT; kind = JCCase.STATEMENT;
break; break;
} }
caseExprs.append(toP(F.at(casePos).Case(kind, pats.toList(), stats, body))); caseExprs.append(toP(F.at(casePos).Case(kind, pats.toList(), guard, stats, body)));
return caseExprs.toList(); return caseExprs.toList();
} }
@ -2910,47 +2915,25 @@ public class JavacParser implements Parser {
case FOR: { case FOR: {
nextToken(); nextToken();
accept(LPAREN); accept(LPAREN);
JCTree pattern; List<JCStatement> inits = token.kind == SEMI ? List.nil() : forInit();
if (inits.length() == 1 &&
ForInitResult initResult = analyzeForInit(); inits.head.hasTag(VARDEF) &&
((JCVariableDecl) inits.head).init == null &&
if (initResult == ForInitResult.RecordPattern) { token.kind == COLON) {
int patternPos = token.pos; JCVariableDecl var = (JCVariableDecl)inits.head;
JCModifiers mods = optFinal(0);
int typePos = token.pos;
JCExpression type = unannotatedType(false);
pattern = parsePattern(patternPos, mods, type, false, false);
if (pattern != null) {
checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
}
accept(COLON); accept(COLON);
JCExpression expr = parseExpression(); JCExpression expr = parseExpression();
accept(RPAREN); accept(RPAREN);
JCStatement body = parseStatementAsBlock(); JCStatement body = parseStatementAsBlock();
return F.at(pos).ForeachLoop(pattern, expr, body); return F.at(pos).ForeachLoop(var, expr, body);
} else { } else {
List<JCStatement> inits = token.kind == SEMI ? List.nil() : forInit(); accept(SEMI);
if (inits.length() == 1 && JCExpression cond = token.kind == SEMI ? null : parseExpression();
inits.head.hasTag(VARDEF) && accept(SEMI);
((JCVariableDecl) inits.head).init == null && List<JCExpressionStatement> steps = token.kind == RPAREN ? List.nil() : forUpdate();
token.kind == COLON) { accept(RPAREN);
JCVariableDecl var = (JCVariableDecl) inits.head; JCStatement body = parseStatementAsBlock();
accept(COLON); return F.at(pos).ForLoop(inits, cond, steps, body);
JCExpression expr = parseExpression();
accept(RPAREN);
JCStatement body = parseStatementAsBlock();
return F.at(pos).ForeachLoop(var, expr, body);
} else {
accept(SEMI);
JCExpression cond = token.kind == SEMI ? null : parseExpression();
accept(SEMI);
List<JCExpressionStatement> steps = token.kind == RPAREN ? List.nil() : forUpdate();
accept(RPAREN);
JCStatement body = parseStatementAsBlock();
return F.at(pos).ForLoop(inits, cond, steps, body);
}
} }
} }
case WHILE: { case WHILE: {
@ -3067,91 +3050,6 @@ public class JavacParser implements Parser {
} }
} }
private enum ForInitResult {
LocalVarDecl,
RecordPattern
}
@SuppressWarnings("fallthrough")
ForInitResult analyzeForInit() {
boolean inType = false;
boolean inSelectionAndParenthesis = false;
int typeParameterPossibleStart = -1;
outer: for (int lookahead = 0; ; lookahead++) {
TokenKind tk = S.token(lookahead).kind;
switch (tk) {
case DOT:
if (inType) break; // in qualified type
case COMMA:
typeParameterPossibleStart = lookahead;
break;
case QUES:
// "?" only allowed in a type parameter position - otherwise it's an expression
if (typeParameterPossibleStart == lookahead - 1) break;
else return ForInitResult.LocalVarDecl;
case EXTENDS: case SUPER: case AMP:
case GTGTGT: case GTGT: case GT:
case FINAL: case ELLIPSIS:
break;
case BYTE: case SHORT: case INT: case LONG: case FLOAT:
case DOUBLE: case BOOLEAN: case CHAR: case VOID:
if (peekToken(lookahead, IDENTIFIER)) {
return inSelectionAndParenthesis ? ForInitResult.RecordPattern
: ForInitResult.LocalVarDecl;
}
break;
case LPAREN:
if (lookahead != 0 && inType) {
inSelectionAndParenthesis = true;
inType = false;
}
break;
case RPAREN:
// a method call in the init part or a record pattern?
if (inSelectionAndParenthesis) {
if (peekToken(lookahead, DOT) ||
peekToken(lookahead, SEMI) ||
peekToken(lookahead, ARROW)) {
return ForInitResult.LocalVarDecl;
}
else if(peekToken(lookahead, COLON)) {
return ForInitResult.RecordPattern;
}
break;
}
case UNDERSCORE:
case ASSERT:
case ENUM:
case IDENTIFIER:
if (lookahead == 0) {
inType = true;
}
break;
case MONKEYS_AT: {
int prevLookahead = lookahead;
lookahead = skipAnnotation(lookahead);
if (typeParameterPossibleStart == prevLookahead - 1) {
// move possible start of type param after the anno
typeParameterPossibleStart = lookahead;
}
break;
}
case LBRACKET:
if (peekToken(lookahead, RBRACKET)) {
return inSelectionAndParenthesis ? ForInitResult.RecordPattern
: ForInitResult.LocalVarDecl;
}
return ForInitResult.LocalVarDecl;
case LT:
typeParameterPossibleStart = lookahead;
break;
default:
//this includes EOF
return ForInitResult.LocalVarDecl;
}
}
}
@Override @Override
public JCStatement parseStatement() { public JCStatement parseStatement() {
return parseStatementAsBlock(); return parseStatementAsBlock();
@ -3234,6 +3132,7 @@ public class JavacParser implements Parser {
checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS); checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS);
allowDefault = TreeInfo.isNullCaseLabel(label); allowDefault = TreeInfo.isNullCaseLabel(label);
}; };
JCExpression guard = parseGuard(pats.last());
CaseTree.CaseKind caseKind; CaseTree.CaseKind caseKind;
JCTree body = null; JCTree body = null;
if (token.kind == ARROW) { if (token.kind == ARROW) {
@ -3251,7 +3150,7 @@ public class JavacParser implements Parser {
caseKind = JCCase.STATEMENT; caseKind = JCCase.STATEMENT;
stats = blockStatements(); stats = blockStatements();
} }
c = F.at(pos).Case(caseKind, pats.toList(), stats, body); c = F.at(pos).Case(caseKind, pats.toList(), guard, stats, body);
if (stats.isEmpty()) if (stats.isEmpty())
storeEnd(c, S.prevToken().endPos); storeEnd(c, S.prevToken().endPos);
return cases.append(c).toList(); return cases.append(c).toList();
@ -3259,6 +3158,7 @@ public class JavacParser implements Parser {
case DEFAULT: { case DEFAULT: {
nextToken(); nextToken();
JCCaseLabel defaultPattern = toP(F.at(pos).DefaultCaseLabel()); JCCaseLabel defaultPattern = toP(F.at(pos).DefaultCaseLabel());
JCExpression guard = parseGuard(defaultPattern);
CaseTree.CaseKind caseKind; CaseTree.CaseKind caseKind;
JCTree body = null; JCTree body = null;
if (token.kind == ARROW) { if (token.kind == ARROW) {
@ -3276,7 +3176,7 @@ public class JavacParser implements Parser {
caseKind = JCCase.STATEMENT; caseKind = JCCase.STATEMENT;
stats = blockStatements(); stats = blockStatements();
} }
c = F.at(pos).Case(caseKind, List.of(defaultPattern), stats, body); c = F.at(pos).Case(caseKind, List.of(defaultPattern), guard, stats, body);
if (stats.isEmpty()) if (stats.isEmpty())
storeEnd(c, S.prevToken().endPos); storeEnd(c, S.prevToken().endPos);
return cases.append(c).toList(); return cases.append(c).toList();
@ -3304,12 +3204,7 @@ public class JavacParser implements Parser {
if (pattern) { if (pattern) {
checkSourceLevel(token.pos, Feature.PATTERN_SWITCH); checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
JCPattern p = parsePattern(patternPos, mods, null, false, true); JCPattern p = parsePattern(patternPos, mods, null, false, true);
JCExpression guard = null; return toP(F.at(patternPos).PatternCaseLabel(p));
if (token.kind == IDENTIFIER && token.name() == names.when) {
nextToken();
guard = term(EXPR | NOLAMBDA);
}
return toP(F.at(patternPos).PatternCaseLabel(p, guard));
} else { } else {
JCExpression expr = term(EXPR | NOLAMBDA); JCExpression expr = term(EXPR | NOLAMBDA);
return toP(F.at(patternPos).ConstantCaseLabel(expr)); return toP(F.at(patternPos).ConstantCaseLabel(expr));
@ -3319,6 +3214,22 @@ public class JavacParser implements Parser {
return label; return label;
} }
private JCExpression parseGuard(JCCaseLabel label) {
JCExpression guard = null;
if (token.kind == IDENTIFIER && token.name() == names.when) {
int pos = token.pos;
nextToken();
guard = term(EXPR | NOLAMBDA);
if (!(label instanceof JCPatternCaseLabel)) {
guard = syntaxError(pos, List.of(guard), Errors.GuardNotAllowed);
}
}
return guard;
}
@SuppressWarnings("fallthrough") @SuppressWarnings("fallthrough")
PatternResult analyzePattern(int lookahead) { PatternResult analyzePattern(int lookahead) {
int typeDepth = 0; int typeDepth = 0;

View File

@ -528,9 +528,16 @@ compiler.err.duplicate.unconditional.pattern=\
compiler.err.unconditional.pattern.and.default=\ compiler.err.unconditional.pattern.and.default=\
switch has both an unconditional pattern and a default label switch has both an unconditional pattern and a default label
compiler.err.guard.not.allowed=\
guards are only allowed for case with a pattern
compiler.err.guard.has.constant.expression.false=\ compiler.err.guard.has.constant.expression.false=\
this case label has a guard that is a constant expression with value ''false'' this case label has a guard that is a constant expression with value ''false''
# 0: symbol
compiler.err.cannot.assign.not.declared.guard=\
cannot assign to {0}, as it was not declared inside the guard
# 0: type, 1: type # 0: type, 1: type
compiler.err.constant.label.not.compatible=\ compiler.err.constant.label.not.compatible=\
constant label of type {0} is not compatible with switch selector type {1} constant label of type {0} is not compatible with switch selector type {1}
@ -619,10 +626,6 @@ compiler.err.foreach.not.applicable.to.type=\
required: {1}\n\ required: {1}\n\
found: {0} found: {0}
# 0: type, 1: type
compiler.err.foreach.not.exhaustive.on.type=\
Pattern {0} is not exhaustive on {1}
compiler.err.fp.number.too.large=\ compiler.err.fp.number.too.large=\
floating-point number too large floating-point number too large
@ -1500,10 +1503,6 @@ compiler.misc.varargs.trustme.on.reifiable.varargs=\
compiler.err.instanceof.reifiable.not.safe=\ compiler.err.instanceof.reifiable.not.safe=\
{0} cannot be safely cast to {1} {0} cannot be safely cast to {1}
# 0: type, 1: type
compiler.err.instanceof.pattern.no.subtype=\
expression type {0} is a subtype of pattern type {1}
# 0: symbol # 0: symbol
compiler.misc.varargs.trustme.on.non.varargs.meth=\ compiler.misc.varargs.trustme.on.non.varargs.meth=\
Method {0} is not a varargs method. Method {0} is not a varargs method.
@ -3854,6 +3853,9 @@ compiler.err.instance.initializer.not.allowed.in.records=\
compiler.err.static.declaration.not.allowed.in.inner.classes=\ compiler.err.static.declaration.not.allowed.in.inner.classes=\
static declarations not allowed in inner classes static declarations not allowed in inner classes
compiler.err.record.patterns.annotations.not.allowed=\
annotations not allowed on record patterns
############################################ ############################################
# messages previously at javac.properties # messages previously at javac.properties

View File

@ -240,7 +240,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
/** Patterns. /** Patterns.
*/ */
BINDINGPATTERN, BINDINGPATTERN,
PARENTHESIZEDPATTERN,
RECORDPATTERN, RECORDPATTERN,
/* Case labels. /* Case labels.
@ -1222,13 +1221,11 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
* The enhanced for loop. * The enhanced for loop.
*/ */
public static class JCEnhancedForLoop extends JCStatement implements EnhancedForLoopTree { public static class JCEnhancedForLoop extends JCStatement implements EnhancedForLoopTree {
public JCTree varOrRecordPattern; public JCVariableDecl var;
public JCExpression expr; public JCExpression expr;
public JCStatement body; public JCStatement body;
public Type elementType; protected JCEnhancedForLoop(JCVariableDecl var, JCExpression expr, JCStatement body) {
this.var = var;
protected JCEnhancedForLoop(JCTree varOrRecordPattern, JCExpression expr, JCStatement body) {
this.varOrRecordPattern = varOrRecordPattern;
this.expr = expr; this.expr = expr;
this.body = body; this.body = body;
} }
@ -1238,11 +1235,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
public Kind getKind() { return Kind.ENHANCED_FOR_LOOP; } public Kind getKind() { return Kind.ENHANCED_FOR_LOOP; }
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
public JCVariableDecl getVariable() { public JCVariableDecl getVariable() { return var; }
return varOrRecordPattern instanceof JCVariableDecl var ? var : null;
}
@DefinedBy(Api.COMPILER_TREE)
public JCTree getVariableOrRecordPattern() { return varOrRecordPattern; }
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
public JCExpression getExpression() { return expr; } public JCExpression getExpression() { return expr; }
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
@ -1255,10 +1248,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public Tag getTag() { public Tag getTag() {
return FOREACHLOOP; return FOREACHLOOP;
} }
@Override @DefinedBy(Api.COMPILER_TREE)
public EnhancedForLoopTree.DeclarationKind getDeclarationKind() {
return varOrRecordPattern.hasTag(VARDEF) ? DeclarationKind.VARIABLE : DeclarationKind.PATTERN;
}
} }
/** /**
@ -1334,15 +1323,18 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public static final CaseKind RULE = CaseKind.RULE; public static final CaseKind RULE = CaseKind.RULE;
public final CaseKind caseKind; public final CaseKind caseKind;
public List<JCCaseLabel> labels; public List<JCCaseLabel> labels;
public JCExpression guard;
public List<JCStatement> stats; public List<JCStatement> stats;
public JCTree body; public JCTree body;
public boolean completesNormally; public boolean completesNormally;
protected JCCase(CaseKind caseKind, List<JCCaseLabel> labels, protected JCCase(CaseKind caseKind, List<JCCaseLabel> labels,
JCExpression guard,
List<JCStatement> stats, JCTree body) { List<JCStatement> stats, JCTree body) {
Assert.checkNonNull(labels); Assert.checkNonNull(labels);
Assert.check(labels.isEmpty() || labels.head != null); Assert.check(labels.isEmpty() || labels.head != null);
this.caseKind = caseKind; this.caseKind = caseKind;
this.labels = labels; this.labels = labels;
this.guard = guard;
this.stats = stats; this.stats = stats;
this.body = body; this.body = body;
} }
@ -1365,6 +1357,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
@Override @DefinedBy(Api.COMPILER_TREE) @Override @DefinedBy(Api.COMPILER_TREE)
public List<JCCaseLabel> getLabels() { return labels; } public List<JCCaseLabel> getLabels() { return labels; }
@Override @DefinedBy(Api.COMPILER_TREE) @Override @DefinedBy(Api.COMPILER_TREE)
public JCExpression getGuard() { return guard; }
@Override @DefinedBy(Api.COMPILER_TREE)
public List<JCStatement> getStatements() { public List<JCStatement> getStatements() {
return caseKind == CaseKind.STATEMENT ? stats : null; return caseKind == CaseKind.STATEMENT ? stats : null;
} }
@ -2252,10 +2246,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
public JCExpression getExpression() { return expr; } public JCExpression getExpression() { return expr; }
@DefinedBy(Api.COMPILER_TREE)
public TestKind getTestKind() {
return pattern instanceof JCPatternCaseLabel ? TestKind.PATTERN : TestKind.TYPE;
}
@Override @DefinedBy(Api.COMPILER_TREE) @Override @DefinedBy(Api.COMPILER_TREE)
public <R,D> R accept(TreeVisitor<R,D> v, D d) { public <R,D> R accept(TreeVisitor<R,D> v, D d) {
return v.visitInstanceOf(this, d); return v.visitInstanceOf(this, d);
@ -2378,11 +2368,9 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
implements PatternCaseLabelTree { implements PatternCaseLabelTree {
public JCPattern pat; public JCPattern pat;
public JCExpression guard;
protected JCPatternCaseLabel(JCPattern pat, JCExpression guard) { protected JCPatternCaseLabel(JCPattern pat) {
this.pat = pat; this.pat = pat;
this.guard = guard;
} }
@Override @DefinedBy(Api.COMPILER_TREE) @Override @DefinedBy(Api.COMPILER_TREE)
@ -2390,11 +2378,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
return pat; return pat;
} }
@Override @DefinedBy(Api.COMPILER_TREE)
public JCExpression getGuard() {
return guard;
}
@Override @Override
public void accept(Visitor v) { public void accept(Visitor v) {
v.visitPatternCaseLabel(this); v.visitPatternCaseLabel(this);
@ -2418,41 +2401,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
} }
public static class JCParenthesizedPattern extends JCPattern
implements ParenthesizedPatternTree {
public JCPattern pattern;
public JCParenthesizedPattern(JCPattern pattern) {
this.pattern = pattern;
}
@Override @DefinedBy(Api.COMPILER_TREE)
public PatternTree getPattern() {
return pattern;
}
@Override
public void accept(Visitor v) {
v.visitParenthesizedPattern(this);
}
@DefinedBy(Api.COMPILER_TREE)
public Kind getKind() {
return Kind.PARENTHESIZED_PATTERN;
}
@Override
@DefinedBy(Api.COMPILER_TREE)
public <R, D> R accept(TreeVisitor<R, D> v, D d) {
return v.visitParenthesizedPattern(this, d);
}
@Override
public Tag getTag() {
return PARENTHESIZEDPATTERN;
}
}
public static class JCRecordPattern extends JCPattern public static class JCRecordPattern extends JCPattern
implements DeconstructionPatternTree { implements DeconstructionPatternTree {
public JCExpression deconstructor; public JCExpression deconstructor;
@ -3487,11 +3435,11 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
JCExpression cond, JCExpression cond,
List<JCExpressionStatement> step, List<JCExpressionStatement> step,
JCStatement body); JCStatement body);
JCEnhancedForLoop ForeachLoop(JCTree var, JCExpression expr, JCStatement body); JCEnhancedForLoop ForeachLoop(JCVariableDecl var, JCExpression expr, JCStatement body);
JCLabeledStatement Labelled(Name label, JCStatement body); JCLabeledStatement Labelled(Name label, JCStatement body);
JCSwitch Switch(JCExpression selector, List<JCCase> cases); JCSwitch Switch(JCExpression selector, List<JCCase> cases);
JCSwitchExpression SwitchExpression(JCExpression selector, List<JCCase> cases); JCSwitchExpression SwitchExpression(JCExpression selector, List<JCCase> cases);
JCCase Case(CaseTree.CaseKind caseKind, List<JCCaseLabel> labels, JCCase Case(CaseTree.CaseKind caseKind, List<JCCaseLabel> labels, JCExpression guard,
List<JCStatement> stats, JCTree body); List<JCStatement> stats, JCTree body);
JCSynchronized Synchronized(JCExpression lock, JCBlock body); JCSynchronized Synchronized(JCExpression lock, JCBlock body);
JCTry Try(JCBlock body, List<JCCatch> catchers, JCBlock finalizer); JCTry Try(JCBlock body, List<JCCatch> catchers, JCBlock finalizer);
@ -3601,7 +3549,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public void visitDefaultCaseLabel(JCDefaultCaseLabel that) { visitTree(that); } public void visitDefaultCaseLabel(JCDefaultCaseLabel that) { visitTree(that); }
public void visitConstantCaseLabel(JCConstantCaseLabel that) { visitTree(that); } public void visitConstantCaseLabel(JCConstantCaseLabel that) { visitTree(that); }
public void visitPatternCaseLabel(JCPatternCaseLabel that) { visitTree(that); } public void visitPatternCaseLabel(JCPatternCaseLabel that) { visitTree(that); }
public void visitParenthesizedPattern(JCParenthesizedPattern that) { visitTree(that); }
public void visitRecordPattern(JCRecordPattern that) { visitTree(that); } public void visitRecordPattern(JCRecordPattern that) { visitTree(that); }
public void visitIndexed(JCArrayAccess that) { visitTree(that); } public void visitIndexed(JCArrayAccess that) { visitTree(that); }
public void visitSelect(JCFieldAccess that) { visitTree(that); } public void visitSelect(JCFieldAccess that) { visitTree(that); }

View File

@ -814,7 +814,7 @@ public class Pretty extends JCTree.Visitor {
public void visitForeachLoop(JCEnhancedForLoop tree) { public void visitForeachLoop(JCEnhancedForLoop tree) {
try { try {
print("for ("); print("for (");
printExpr(tree.varOrRecordPattern); printExpr(tree.var);
print(" : "); print(" : ");
printExpr(tree.expr); printExpr(tree.expr);
print(") "); print(") ");
@ -862,6 +862,10 @@ public class Pretty extends JCTree.Visitor {
print("case "); print("case ");
printExprs(tree.labels); printExprs(tree.labels);
} }
if (tree.guard != null) {
print(" when ");
print(tree.guard);
}
if (tree.caseKind == JCCase.STATEMENT) { if (tree.caseKind == JCCase.STATEMENT) {
print(':'); print(':');
println(); println();
@ -904,10 +908,6 @@ public class Pretty extends JCTree.Visitor {
public void visitPatternCaseLabel(JCPatternCaseLabel tree) { public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
try { try {
print(tree.pat); print(tree.pat);
if (tree.guard != null) {
print(" when ");
print(tree.guard);
}
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} }
@ -941,17 +941,6 @@ public class Pretty extends JCTree.Visitor {
} }
} }
@Override
public void visitParenthesizedPattern(JCParenthesizedPattern patt) {
try {
print('(');
printExpr(patt.pattern);
print(')');
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override @Override
public void visitRecordPattern(JCRecordPattern tree) { public void visitRecordPattern(JCRecordPattern tree) {
try { try {

View File

@ -153,6 +153,7 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
public JCTree visitCase(CaseTree node, P p) { public JCTree visitCase(CaseTree node, P p) {
JCCase t = (JCCase) node; JCCase t = (JCCase) node;
List<JCCaseLabel> labels = copy(t.labels, p); List<JCCaseLabel> labels = copy(t.labels, p);
JCExpression guard = copy(t.guard, p);
List<JCStatement> stats = copy(t.stats, p); List<JCStatement> stats = copy(t.stats, p);
JCTree body; JCTree body;
if (node.getCaseKind() == CaseTree.CaseKind.RULE) { if (node.getCaseKind() == CaseTree.CaseKind.RULE) {
@ -161,7 +162,7 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
} else { } else {
body = null; body = null;
} }
return M.at(t.pos).Case(t.caseKind, labels, stats, body); return M.at(t.pos).Case(t.caseKind, labels, guard, stats, body);
} }
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
@ -223,10 +224,10 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
public JCTree visitEnhancedForLoop(EnhancedForLoopTree node, P p) { public JCTree visitEnhancedForLoop(EnhancedForLoopTree node, P p) {
JCEnhancedForLoop t = (JCEnhancedForLoop) node; JCEnhancedForLoop t = (JCEnhancedForLoop) node;
JCTree varOrRecordPattern = copy(t.varOrRecordPattern, p); JCVariableDecl var = copy(t.var, p);
JCExpression expr = copy(t.expr, p); JCExpression expr = copy(t.expr, p);
JCStatement body = copy(t.body, p); JCStatement body = copy(t.body, p);
return M.at(t.pos).ForeachLoop(varOrRecordPattern, expr, body); return M.at(t.pos).ForeachLoop(var, expr, body);
} }
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
@ -505,13 +506,6 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
return M.at(t.pos).BindingPattern(var); return M.at(t.pos).BindingPattern(var);
} }
@DefinedBy(Api.COMPILER_TREE)
public JCTree visitParenthesizedPattern(ParenthesizedPatternTree node, P p) {
JCParenthesizedPattern t = (JCParenthesizedPattern) node;
JCPattern pattern = copy(t.pattern, p);
return M.at(t.pos).ParenthesizedPattern(pattern);
}
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
public JCTree visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) { public JCTree visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) {
JCDefaultCaseLabel t = (JCDefaultCaseLabel) node; JCDefaultCaseLabel t = (JCDefaultCaseLabel) node;
@ -529,8 +523,7 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
public JCTree visitPatternCaseLabel(PatternCaseLabelTree node, P p) { public JCTree visitPatternCaseLabel(PatternCaseLabelTree node, P p) {
JCPatternCaseLabel t = (JCPatternCaseLabel) node; JCPatternCaseLabel t = (JCPatternCaseLabel) node;
JCPattern pat = copy(t.pat, p); JCPattern pat = copy(t.pat, p);
JCExpression guard = copy(t.guard, p); return M.at(t.pos).PatternCaseLabel(pat);
return M.at(t.pos).PatternCaseLabel(pat, guard);
} }
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)

View File

@ -648,10 +648,6 @@ public class TreeInfo {
return getEndPos(((JCWhileLoop) tree).body, endPosTable); return getEndPos(((JCWhileLoop) tree).body, endPosTable);
case ANNOTATED_TYPE: case ANNOTATED_TYPE:
return getEndPos(((JCAnnotatedType) tree).underlyingType, endPosTable); return getEndPos(((JCAnnotatedType) tree).underlyingType, endPosTable);
case PARENTHESIZEDPATTERN: {
JCParenthesizedPattern node = (JCParenthesizedPattern) tree;
return getEndPos(node.pattern, endPosTable);
}
case ERRONEOUS: { case ERRONEOUS: {
JCErroneous node = (JCErroneous)tree; JCErroneous node = (JCErroneous)tree;
if (node.errs != null && node.errs.nonEmpty()) if (node.errs != null && node.errs.nonEmpty())
@ -855,15 +851,6 @@ public class TreeInfo {
return tree; return tree;
} }
/** Skip parens and return the enclosed expression
*/
public static JCPattern skipParens(JCPattern tree) {
while (tree.hasTag(PARENTHESIZEDPATTERN)) {
tree = ((JCParenthesizedPattern) tree).pattern;
}
return tree;
}
/** Return the types of a list of trees. /** Return the types of a list of trees.
*/ */
public static List<Type> types(List<? extends JCTree> trees) { public static List<Type> types(List<? extends JCTree> trees) {
@ -1358,7 +1345,6 @@ public class TreeInfo {
public static Type primaryPatternType(JCTree pat) { public static Type primaryPatternType(JCTree pat) {
return switch (pat.getTag()) { return switch (pat.getTag()) {
case BINDINGPATTERN -> pat.type; case BINDINGPATTERN -> pat.type;
case PARENTHESIZEDPATTERN -> primaryPatternType(((JCParenthesizedPattern) pat).pattern);
case RECORDPATTERN -> ((JCRecordPattern) pat).type; case RECORDPATTERN -> ((JCRecordPattern) pat).type;
default -> throw new AssertionError(); default -> throw new AssertionError();
}; };
@ -1367,7 +1353,6 @@ public class TreeInfo {
public static JCTree primaryPatternTypeTree(JCTree pat) { public static JCTree primaryPatternTypeTree(JCTree pat) {
return switch (pat.getTag()) { return switch (pat.getTag()) {
case BINDINGPATTERN -> ((JCBindingPattern) pat).var.vartype; case BINDINGPATTERN -> ((JCBindingPattern) pat).var.vartype;
case PARENTHESIZEDPATTERN -> primaryPatternTypeTree(((JCParenthesizedPattern) pat).pattern);
case RECORDPATTERN -> ((JCRecordPattern) pat).deconstructor; case RECORDPATTERN -> ((JCRecordPattern) pat).deconstructor;
default -> throw new AssertionError(); default -> throw new AssertionError();
}; };
@ -1380,11 +1365,8 @@ public class TreeInfo {
.anyMatch(l -> TreeInfo.isNullCaseLabel(l)); .anyMatch(l -> TreeInfo.isNullCaseLabel(l));
} }
public static boolean unguardedCaseLabel(JCCaseLabel cse) { public static boolean unguardedCase(JCCase cse) {
if (!cse.hasTag(PATTERNCASELABEL)) { JCExpression guard = cse.guard;
return true;
}
JCExpression guard = ((JCPatternCaseLabel) cse).guard;
if (guard == null) { if (guard == null) {
return true; return true;
} }

View File

@ -274,8 +274,8 @@ public class TreeMaker implements JCTree.Factory {
return tree; return tree;
} }
public JCEnhancedForLoop ForeachLoop(JCTree varOrRecordPattern, JCExpression expr, JCStatement body) { public JCEnhancedForLoop ForeachLoop(JCVariableDecl var, JCExpression expr, JCStatement body) {
JCEnhancedForLoop tree = new JCEnhancedForLoop(varOrRecordPattern, expr, body); JCEnhancedForLoop tree = new JCEnhancedForLoop(var, expr, body);
tree.pos = pos; tree.pos = pos;
return tree; return tree;
} }
@ -293,8 +293,8 @@ public class TreeMaker implements JCTree.Factory {
} }
public JCCase Case(CaseTree.CaseKind caseKind, List<JCCaseLabel> labels, public JCCase Case(CaseTree.CaseKind caseKind, List<JCCaseLabel> labels,
List<JCStatement> stats, JCTree body) { JCExpression guard, List<JCStatement> stats, JCTree body) {
JCCase tree = new JCCase(caseKind, labels, stats, body); JCCase tree = new JCCase(caseKind, labels, guard, stats, body);
tree.pos = pos; tree.pos = pos;
return tree; return tree;
} }
@ -501,14 +501,8 @@ public class TreeMaker implements JCTree.Factory {
return tree; return tree;
} }
public JCPatternCaseLabel PatternCaseLabel(JCPattern pat, JCExpression guard) { public JCPatternCaseLabel PatternCaseLabel(JCPattern pat) {
JCPatternCaseLabel tree = new JCPatternCaseLabel(pat, guard); JCPatternCaseLabel tree = new JCPatternCaseLabel(pat);
tree.pos = pos;
return tree;
}
public JCParenthesizedPattern ParenthesizedPattern(JCPattern pattern) {
JCParenthesizedPattern tree = new JCParenthesizedPattern(pattern);
tree.pos = pos; tree.pos = pos;
return tree; return tree;
} }

View File

@ -162,7 +162,7 @@ public class TreeScanner extends Visitor {
} }
public void visitForeachLoop(JCEnhancedForLoop tree) { public void visitForeachLoop(JCEnhancedForLoop tree) {
scan(tree.varOrRecordPattern); scan(tree.var);
scan(tree.expr); scan(tree.expr);
scan(tree.body); scan(tree.body);
} }
@ -178,6 +178,7 @@ public class TreeScanner extends Visitor {
public void visitCase(JCCase tree) { public void visitCase(JCCase tree) {
scan(tree.labels); scan(tree.labels);
scan(tree.guard);
scan(tree.stats); scan(tree.stats);
} }
@ -319,12 +320,6 @@ public class TreeScanner extends Visitor {
@Override @Override
public void visitPatternCaseLabel(JCPatternCaseLabel tree) { public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
scan(tree.pat); scan(tree.pat);
scan(tree.guard);
}
@Override
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
scan(tree.pattern);
} }
@Override @Override

View File

@ -189,7 +189,7 @@ public class TreeTranslator extends JCTree.Visitor {
} }
public void visitForeachLoop(JCEnhancedForLoop tree) { public void visitForeachLoop(JCEnhancedForLoop tree) {
tree.varOrRecordPattern = translate(tree.varOrRecordPattern); tree.var = translate(tree.var);
tree.expr = translate(tree.expr); tree.expr = translate(tree.expr);
tree.body = translate(tree.body); tree.body = translate(tree.body);
result = tree; result = tree;
@ -208,6 +208,7 @@ public class TreeTranslator extends JCTree.Visitor {
public void visitCase(JCCase tree) { public void visitCase(JCCase tree) {
tree.labels = translate(tree.labels); tree.labels = translate(tree.labels);
tree.guard = translate(tree.guard);
tree.stats = translate(tree.stats); tree.stats = translate(tree.stats);
result = tree; result = tree;
} }
@ -377,13 +378,6 @@ public class TreeTranslator extends JCTree.Visitor {
@Override @Override
public void visitPatternCaseLabel(JCPatternCaseLabel tree) { public void visitPatternCaseLabel(JCPatternCaseLabel tree) {
tree.pat = translate(tree.pat); tree.pat = translate(tree.pat);
tree.guard = translate(tree.guard);
result = tree;
}
@Override
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
tree.pattern = translate(tree.pattern);
result = tree; result = tree;
} }

View File

@ -92,9 +92,11 @@ public class Names {
public final Name hasNext; public final Name hasNext;
public final Name hashCode; public final Name hashCode;
public final Name init; public final Name init;
public final Name invoke;
public final Name iterator; public final Name iterator;
public final Name length; public final Name length;
public final Name next; public final Name next;
public final Name of;
public final Name ordinal; public final Name ordinal;
public final Name provider; public final Name provider;
public final Name serialVersionUID; public final Name serialVersionUID;
@ -221,6 +223,7 @@ public class Names {
// pattern switches // pattern switches
public final Name typeSwitch; public final Name typeSwitch;
public final Name enumSwitch; public final Name enumSwitch;
public final Name enumConstant;
// templated string // templated string
public final Name process; public final Name process;
@ -282,9 +285,11 @@ public class Names {
hasNext = fromString("hasNext"); hasNext = fromString("hasNext");
hashCode = fromString("hashCode"); hashCode = fromString("hashCode");
init = fromString("<init>"); init = fromString("<init>");
invoke = fromString("invoke");
iterator = fromString("iterator"); iterator = fromString("iterator");
length = fromString("length"); length = fromString("length");
next = fromString("next"); next = fromString("next");
of = fromString("of");
ordinal = fromString("ordinal"); ordinal = fromString("ordinal");
provider = fromString("provider"); provider = fromString("provider");
serialVersionUID = fromString("serialVersionUID"); serialVersionUID = fromString("serialVersionUID");
@ -415,6 +420,7 @@ public class Names {
// pattern switches // pattern switches
typeSwitch = fromString("typeSwitch"); typeSwitch = fromString("typeSwitch");
enumSwitch = fromString("enumSwitch"); enumSwitch = fromString("enumSwitch");
enumConstant = fromString("enumConstant");
} }
protected Name.Table createTable(Options options) { protected Name.Table createTable(Options options) {

View File

@ -24,15 +24,20 @@
*/ */
import java.io.Serializable; import java.io.Serializable;
import java.lang.Enum.EnumDesc;
import java.lang.constant.ClassDesc;
import java.lang.invoke.CallSite; import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.lang.runtime.SwitchBootstraps; import java.lang.runtime.SwitchBootstraps;
import java.util.concurrent.atomic.AtomicBoolean;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail; import static org.testng.Assert.fail;
/** /**
@ -158,6 +163,28 @@ public class SwitchBootstrapsTest {
} }
} }
public void testSwitchLabelTypes() throws Throwable {
enum E {A}
try {
testType(E.A, 0, -1, E.A);
fail("Didn't get the expected exception.");
} catch (IllegalArgumentException ex) {
//OK, expected
}
}
public void testSwitchQualifiedEnum() throws Throwable {
enum E {A, B, C}
Object[] labels = new Object[] {
EnumDesc.of(ClassDesc.of(E.class.getName()), "A"),
EnumDesc.of(ClassDesc.of(E.class.getName()), "B"),
EnumDesc.of(ClassDesc.of(E.class.getName()), "C")
};
testType(E.A, 0, 0, labels);
testType(E.B, 0, 1, labels);
testType(E.C, 0, 2, labels);
}
public void testNullLabels() throws Throwable { public void testNullLabels() throws Throwable {
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
try { try {
@ -188,4 +215,100 @@ public class SwitchBootstrapsTest {
//OK //OK
} }
} }
private static AtomicBoolean enumInitialized = new AtomicBoolean();
public void testEnumInitialization1() throws Throwable {
enumInitialized.set(false);
enum E {
A;
static {
enumInitialized.set(true);
}
}
MethodType enumSwitchType = MethodType.methodType(int.class, E.class, int.class);
CallSite invocation = (CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, new Object[] {"A"});
assertFalse(enumInitialized.get());
assertEquals(invocation.dynamicInvoker().invoke(null, 0), -1);
assertFalse(enumInitialized.get());
E e = E.A;
assertTrue(enumInitialized.get());
assertEquals(invocation.dynamicInvoker().invoke(e, 0), 0);
}
public void testEnumInitialization2() throws Throwable {
enumInitialized.set(false);
enum E {
A;
static {
enumInitialized.set(true);
}
}
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
Object[] labels = new Object[] {
EnumDesc.of(ClassDesc.of(E.class.getName()), "A"),
"test"
};
CallSite invocation = (CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels);
assertFalse(enumInitialized.get());
assertEquals(invocation.dynamicInvoker().invoke(null, 0), -1);
assertFalse(enumInitialized.get());
assertEquals(invocation.dynamicInvoker().invoke("test", 0), 1);
assertFalse(enumInitialized.get());
E e = E.A;
assertTrue(enumInitialized.get());
assertEquals(invocation.dynamicInvoker().invoke(e, 0), 0);
}
public void testIncorrectEnumLabels() throws Throwable {
try {
testEnum(E1.B, 0, -1, "B", 1);
fail("Didn't get the expected exception.");
} catch (IllegalArgumentException ex) {
//OK
}
try {
testEnum(E1.B, 0, -1, "B", null);
fail("Didn't get the expected exception.");
} catch (IllegalArgumentException ex) {
//OK
}
}
public void testIncorrectEnumStartIndex() throws Throwable {
try {
testEnum(E1.B, -1, -1, "B");
fail("Didn't get the expected exception.");
} catch (IndexOutOfBoundsException ex) {
//OK
}
try {
testEnum(E1.B, 2, -1, "B");
fail("Didn't get the expected exception.");
} catch (IndexOutOfBoundsException ex) {
//OK
}
}
public void testIncorrectTypeStartIndex() throws Throwable {
try {
testType("", -1, -1, "");
fail("Didn't get the expected exception.");
} catch (IndexOutOfBoundsException ex) {
//OK
}
try {
testType("", 2, -1, "");
fail("Didn't get the expected exception.");
} catch (IndexOutOfBoundsException ex) {
//OK
}
}
} }

View File

@ -99,10 +99,7 @@ public class ConditionalExpressionResolvePending extends ComboInstance<Condition
case "FALSE" -> False; case "FALSE" -> False;
case "SNIPPET" -> snippet; case "SNIPPET" -> snippet;
default -> throw new UnsupportedOperationException(pname); default -> throw new UnsupportedOperationException(pname);
}) });
.withOption("--enable-preview")
.withOption("-source")
.withOption(String.valueOf(Runtime.version().feature()));
task.generate(result -> { task.generate(result -> {
try { try {

View File

@ -25,7 +25,7 @@
* @test * @test
* @bug 8286797 * @bug 8286797
* @summary Guards of constant value false are not permitted * @summary Guards of constant value false are not permitted
* @compile/fail/ref=T8286797.out --enable-preview -source ${jdk.version} -XDrawDiagnostics -XDshould-stop.at=FLOW T8286797.java * @compile/fail/ref=T8286797.out -XDrawDiagnostics -XDshould-stop.at=FLOW T8286797.java
*/ */
public class T8286797 { public class T8286797 {

View File

@ -1,5 +1,3 @@
T8286797.java:35:32: compiler.err.guard.has.constant.expression.false T8286797.java:35:32: compiler.err.guard.has.constant.expression.false
T8286797.java:43:34: compiler.err.guard.has.constant.expression.false T8286797.java:43:34: compiler.err.guard.has.constant.expression.false
- compiler.note.preview.filename: T8286797.java, DEFAULT
- compiler.note.preview.recompile
2 errors 2 errors

View File

@ -25,7 +25,7 @@
* @bug 8295447 * @bug 8295447
* @summary NullPointerException with invalid pattern matching construct in constructor call * @summary NullPointerException with invalid pattern matching construct in constructor call
* @modules jdk.compiler * @modules jdk.compiler
* @compile/fail/ref=T8295447.out -XDrawDiagnostics --enable-preview -source ${jdk.version} T8295447.java * @compile/fail/ref=T8295447.out -XDrawDiagnostics T8295447.java
*/ */
public class T8295447 { public class T8295447 {
class Foo { class Foo {

View File

@ -1,6 +1,4 @@
T8295447.java:33:29: compiler.err.deconstruction.pattern.only.records: T8295447.Foo T8295447.java:33:29: compiler.err.deconstruction.pattern.only.records: T8295447.Foo
T8295447.java:37:29: compiler.err.deconstruction.pattern.only.records: T8295447.Foo T8295447.java:37:29: compiler.err.deconstruction.pattern.only.records: T8295447.Foo
T8295447.java:44:44: compiler.err.deconstruction.pattern.only.records: java.awt.Point T8295447.java:44:44: compiler.err.deconstruction.pattern.only.records: java.awt.Point
- compiler.note.preview.filename: T8295447.java, DEFAULT
- compiler.note.preview.recompile
3 errors 3 errors

View File

@ -28,7 +28,6 @@
* @modules java.compiler * @modules java.compiler
* jdk.jdeps/com.sun.tools.javap * jdk.jdeps/com.sun.tools.javap
* @build toolbox.JavapTask * @build toolbox.JavapTask
* @enablePreview
* @run main Patterns * @run main Patterns
*/ */

View File

@ -81,7 +81,7 @@ public class RuleSwitchBreaks extends LineNumberTestBase {
""", """,
List.of(1, 3, 4, 5, 6, 7, 9, 11), List.of(1, 3, 4, 5, 6, 7, 9, 11),
true, true,
List.of("--enable-preview", "-source", System.getProperty("java.specification.version")), List.of(),
"TestGuards") "TestGuards")
}; };

View File

@ -141,6 +141,7 @@ compiler.warn.invalid.path # this warning is genera
compiler.err.invalid.path # this error is generated only in Windows systems compiler.err.invalid.path # this error is generated only in Windows systems
compiler.note.multiple.elements # needs user code compiler.note.multiple.elements # needs user code
compiler.err.preview.feature.disabled.classfile # preview feature support: needs compilation against classfile compiler.err.preview.feature.disabled.classfile # preview feature support: needs compilation against classfile
compiler.warn.preview.feature.use # preview feature support: not generated currently
compiler.warn.preview.feature.use.classfile # preview feature support: needs compilation against classfile compiler.warn.preview.feature.use.classfile # preview feature support: needs compilation against classfile
compiler.note.preview.plural.additional # preview feature support: diag test causes intermittent failures (see JDK-8201498) compiler.note.preview.plural.additional # preview feature support: diag test causes intermittent failures (see JDK-8201498)
compiler.misc.bad.intersection.target.for.functional.expr # currently not generated, should be removed? compiler.misc.bad.intersection.target.for.functional.expr # currently not generated, should be removed?

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -21,32 +21,14 @@
* questions. * questions.
*/ */
/* // key: compiler.err.cannot.assign.not.declared.guard
* @test
* @bug 8297602
* @summary Compiler crash with type annotation and generic record during pattern matching
* @enablePreview
* @compile --enable-preview -source ${jdk.version} -XDrawDiagnostics T8297602.java
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
public class T8297602
{
void meth(Foo<Integer> p) {
switch(p) {
case Foo<@Annot(field = "") Integer>(): {}
};
if (p instanceof Foo<@Annot(field = "") Integer>()) {
class CannotAssignNotDeclaredGuard {
void test(Object i) {
final boolean b;
switch (i) {
case Object o when b = true -> {}
default -> {}
} }
} }
@Target({ElementType.TYPE_USE})
@interface Annot {
String field();
}
record Foo<T>() { }
} }

View File

@ -25,9 +25,6 @@
// key: compiler.misc.inner.cls // key: compiler.misc.inner.cls
// key: compiler.misc.lambda // key: compiler.misc.lambda
// key: compiler.misc.guard // key: compiler.misc.guard
// key: compiler.misc.feature.pattern.switch
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class CantRefNonEffectivelyFinalVar { class CantRefNonEffectivelyFinalVar {
void test() { void test() {

View File

@ -21,9 +21,9 @@
* questions. * questions.
*/ */
// key: compiler.err.feature.not.supported.in.source
// key: compiler.misc.feature.case.null // key: compiler.misc.feature.case.null
// key: compiler.warn.preview.feature.use // options: -source 20 -Xlint:-options
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class CaseNull { class CaseNull {
private void doSwitch(String s) { private void doSwitch(String s) {

View File

@ -22,9 +22,6 @@
*/ */
// key: compiler.err.constant.label.not.compatible // key: compiler.err.constant.label.not.compatible
// key: compiler.misc.feature.pattern.switch
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class ConstantLabelNotCompatible { class ConstantLabelNotCompatible {
private void doSwitch(Object o) { private void doSwitch(Object o) {

View File

@ -22,9 +22,6 @@
*/ */
// key: compiler.err.deconstruction.pattern.only.records // key: compiler.err.deconstruction.pattern.only.records
// key: compiler.warn.preview.feature.use.plural
// key: compiler.misc.feature.deconstruction.patterns
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class DeconstructionpatternOnlyRecords { class DeconstructionpatternOnlyRecords {
public boolean deconstruction(Object o) { public boolean deconstruction(Object o) {

View File

@ -22,9 +22,6 @@
*/ */
// key: compiler.err.default.label.not.allowed // key: compiler.err.default.label.not.allowed
// key: compiler.misc.feature.pattern.switch
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class DefaultLabelNotAllowed { class DefaultLabelNotAllowed {
private void doSwitch(Object o) { private void doSwitch(Object o) {

View File

@ -22,9 +22,6 @@
*/ */
// key: compiler.err.duplicate.unconditional.pattern // key: compiler.err.duplicate.unconditional.pattern
// key: compiler.misc.feature.pattern.switch
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class DuplicateUnconditionalPattern { class DuplicateUnconditionalPattern {
private void doSwitch(Object o) { private void doSwitch(Object o) {

View File

@ -22,6 +22,7 @@
*/ */
// key: compiler.err.enum.label.must.be.unqualified.enum // key: compiler.err.enum.label.must.be.unqualified.enum
// options: --release 20
class EnumLabelUnqualified { class EnumLabelUnqualified {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -21,20 +21,13 @@
* questions. * questions.
*/ */
// key: compiler.err.feature.not.supported.in.source.plural
// key: compiler.misc.feature.deconstruction.patterns // key: compiler.misc.feature.deconstruction.patterns
// key: compiler.misc.feature.pattern.switch // options: -source 20 -Xlint:-options
// key: compiler.warn.preview.feature.use.plural
// key: compiler.err.foreach.not.exhaustive.on.type
// options: --enable-preview -source ${jdk.version} -Xlint:preview
import java.util.List; class FeatureDeconstructionPatterns {
Object o;
boolean b = o instanceof R(String s);
class ForeachNotExhaustive { record R(Object o) {}
void m(List<Object> points) {
for (Point(var x, var y): points) {
System.out.println();
}
}
record Point(Integer x, Integer y) { }
} }

View File

@ -21,11 +21,9 @@
* questions. * questions.
*/ */
// key: compiler.err.feature.not.supported.in.source.plural
// key: compiler.misc.feature.unconditional.patterns.in.instanceof // key: compiler.misc.feature.unconditional.patterns.in.instanceof
// key: compiler.warn.preview.feature.use.plural // options: -source 20 -Xlint:-options
// options: --enable-preview -source ${jdk.version} -Xlint:-options,preview
import java.util.*;
class FeatureUnconditionalTypesInstanceof { class FeatureUnconditionalTypesInstanceof {
String s; String s;

View File

@ -22,9 +22,6 @@
*/ */
// key: compiler.err.flows.through.from.pattern // key: compiler.err.flows.through.from.pattern
// key: compiler.misc.feature.pattern.switch
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class FlowsThroughToPattern { class FlowsThroughToPattern {
private void doSwitch(Object o) { private void doSwitch(Object o) {

View File

@ -22,11 +22,6 @@
*/ */
// key: compiler.err.flows.through.to.pattern // key: compiler.err.flows.through.to.pattern
// key: compiler.misc.feature.pattern.switch
// key: compiler.warn.preview.feature.use.plural
// key: compiler.misc.feature.case.null
// key: compiler.warn.preview.feature.use
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class FlowsThroughToPattern { class FlowsThroughToPattern {
private void doSwitch(Object o) { private void doSwitch(Object o) {

View File

@ -22,9 +22,6 @@
*/ */
// key: compiler.err.guard.has.constant.expression.false // key: compiler.err.guard.has.constant.expression.false
// key: compiler.misc.feature.pattern.switch
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class GuardHasConstantFalse { class GuardHasConstantFalse {
private void doSwitch(Object o) { private void doSwitch(Object o) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -21,10 +21,13 @@
* questions. * questions.
*/ */
// key: compiler.err.instanceof.pattern.no.subtype // key: compiler.err.guard.not.allowed
class InstanceofPatternNoSubtype { class GuardNotAllowed {
boolean test(Object o) { private void doSwitch(int i, boolean b) {
return o instanceof Object obj; switch (i) {
case 0 when b -> {}
default -> {}
}
} }
} }

View File

@ -22,9 +22,6 @@
*/ */
// key: compiler.err.incorrect.number.of.nested.patterns // key: compiler.err.incorrect.number.of.nested.patterns
// key: compiler.misc.feature.deconstruction.patterns
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class IncorrectNumberOfNestedPatterns { class IncorrectNumberOfNestedPatterns {
private boolean t(Object o) { private boolean t(Object o) {

View File

@ -22,9 +22,6 @@
*/ */
// key: compiler.err.invalid.case.label.combination // key: compiler.err.invalid.case.label.combination
// key: compiler.misc.feature.case.null
// key: compiler.warn.preview.feature.use
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class InvalidCaseLabelCombination { class InvalidCaseLabelCombination {
private void doSwitch(Integer i) { private void doSwitch(Integer i) {

View File

@ -23,9 +23,6 @@
// key: compiler.misc.not.applicable.types // key: compiler.misc.not.applicable.types
// key: compiler.err.prob.found.req // key: compiler.err.prob.found.req
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview --source ${jdk.version}
class NotApplicableTypes { class NotApplicableTypes {
void t(int i) { void t(int i) {

View File

@ -22,9 +22,6 @@
*/ */
// key: compiler.err.not.exhaustive.statement // key: compiler.err.not.exhaustive.statement
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview --source ${jdk.version}
class NotExhaustive { class NotExhaustive {
void t(Object o) { void t(Object o) {

View File

@ -22,9 +22,6 @@
*/ */
// key: compiler.err.pattern.dominated // key: compiler.err.pattern.dominated
// key: compiler.misc.feature.pattern.switch
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class PatternDominated { class PatternDominated {
private void doSwitch(Object o) { private void doSwitch(Object o) {

View File

@ -22,9 +22,6 @@
*/ */
// key: compiler.err.pattern.expected // key: compiler.err.pattern.expected
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source ${jdk.version}
class PatternSwitch { class PatternSwitch {
private void doSwitch(Object o) { private void doSwitch(Object o) {

View File

@ -21,9 +21,9 @@
* questions. * questions.
*/ */
// key: compiler.err.feature.not.supported.in.source.plural
// key: compiler.misc.feature.pattern.switch // key: compiler.misc.feature.pattern.switch
// key: compiler.warn.preview.feature.use.plural // options: -source 20 -Xlint:-options
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class PatternSwitch { class PatternSwitch {
private void doSwitch(Object o) { private void doSwitch(Object o) {

View File

@ -22,9 +22,6 @@
*/ */
// key: compiler.err.pattern.type.cannot.infer // key: compiler.err.pattern.type.cannot.infer
// key: compiler.warn.preview.feature.use.plural
// key: compiler.misc.feature.deconstruction.patterns
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class PatternTypeCannotInfer { class PatternTypeCannotInfer {
interface A<T> {} interface A<T> {}

View File

@ -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) {}
}

View File

@ -22,9 +22,6 @@
*/ */
// key: compiler.err.unconditional.pattern.and.default // key: compiler.err.unconditional.pattern.and.default
// key: compiler.misc.feature.pattern.switch
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class UnconditionalPatternAndDefault { class UnconditionalPatternAndDefault {
private void doSwitch(Object o) { private void doSwitch(Object o) {

View File

@ -3,7 +3,7 @@
* @bug 4936393 8050021 * @bug 4936393 8050021
* @summary enum switch case labels required to be unqualified. * @summary enum switch case labels required to be unqualified.
* @author gafter * @author gafter
* @compile/fail/ref=EnumSwitch2.out -XDrawDiagnostics EnumSwitch2.java * @compile/fail/ref=EnumSwitch2.out -XDrawDiagnostics --release 20 EnumSwitch2.java
*/ */
enum E1 { a, b, c, d, e } enum E1 { a, b, c, d, e }

View File

@ -722,7 +722,7 @@ public class DPrinter {
@Override @Override
public void visitForeachLoop(JCEnhancedForLoop tree) { public void visitForeachLoop(JCEnhancedForLoop tree) {
printTree("var", tree.varOrRecordPattern); printTree("var", tree.var);
printTree("expr", tree.expr); printTree("expr", tree.expr);
printTree("body", tree.body); printTree("body", tree.body);
} }

View File

@ -2342,6 +2342,59 @@ public class JavacParserTest extends TestCase {
} }
} }
@Test
void testGuardRecovery() throws IOException {
String code = """
package t;
class Test {
private int t(Integer i, boolean b) {
switch (i) {
case 0 when b -> {}
case null when b -> {}
default when b -> {}
}
return switch (i) {
case 0 when b -> 0;
case null when b -> 0;
default when b -> 0;
};
}
}""";
DiagnosticCollector<JavaFileObject> coll =
new DiagnosticCollector<>();
JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll, null,
null, Arrays.asList(new MyFileObject(code)));
CompilationUnitTree cut = ct.parse().iterator().next();
new TreeScanner<Void, Void>() {
@Override
public Void visitCase(CaseTree node, Void p) {
assertNotNull(node.getGuard());
assertEquals("guard kind", Kind.ERRONEOUS, node.getGuard().getKind());
assertEquals("guard content",
List.of("b"),
((ErroneousTree) node.getGuard()).getErrorTrees()
.stream()
.map(t -> t.toString()).toList());
return super.visitCase(node, p);
}
}.scan(cut, null);
List<String> codes = new LinkedList<>();
for (Diagnostic<? extends JavaFileObject> d : coll.getDiagnostics()) {
codes.add(d.getLineNumber() + ":" + d.getColumnNumber() + ":" + d.getCode());
}
assertEquals("testUsupportedTextBlock: " + codes,
List.of("5:20:compiler.err.guard.not.allowed",
"6:23:compiler.err.guard.not.allowed",
"7:21:compiler.err.guard.not.allowed",
"10:20:compiler.err.guard.not.allowed",
"11:23:compiler.err.guard.not.allowed",
"12:21:compiler.err.guard.not.allowed"),
codes);
}
void run(String[] args) throws Exception { void run(String[] args) throws Exception {
int passed = 0, failed = 0; int passed = 0, failed = 0;
final Pattern p = (args != null && args.length > 0) final Pattern p = (args != null && args.length > 0)

View File

@ -0,0 +1,42 @@
/*
* @test /nodynamiccopyright/
* @bug 8300543
* @summary Verify error related to annotations and patterns
* @compile/fail/ref=AnnotationErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW AnnotationErrors.java
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
public class AnnotationErrors {
private void test(Object o, G<String> g) {
boolean b1 = o instanceof @DA R(var s);
boolean b2 = o instanceof @DTA R(var s);
boolean b3 = o instanceof @TA R(var s);
boolean b5 = g instanceof G<@DTA String>(var s);
boolean b6 = g instanceof G<@TA String>(var s);
switch (o) {
case @DA R(var s) when b1 -> {}
case @DTA R(var s) when b1 -> {}
case @TA R(var s) when b1 -> {}
default -> {}
}
switch (g) {
case G<@DTA String>(var s) when b1 -> {}
case G<@TA String>(var s) when b1 -> {}
default -> {}
}
}
record R(String s) {}
record G<T>(T t) {}
@Target(ElementType.LOCAL_VARIABLE)
@interface DA {}
@Target({ElementType.TYPE_USE, ElementType.LOCAL_VARIABLE})
@interface DTA {}
@Target(ElementType.TYPE_USE)
@interface TA {}
}

View File

@ -0,0 +1,11 @@
AnnotationErrors.java:14:35: compiler.err.record.patterns.annotations.not.allowed
AnnotationErrors.java:15:35: compiler.err.record.patterns.annotations.not.allowed
AnnotationErrors.java:16:35: compiler.err.record.patterns.annotations.not.allowed
AnnotationErrors.java:17:37: compiler.err.record.patterns.annotations.not.allowed
AnnotationErrors.java:18:37: compiler.err.record.patterns.annotations.not.allowed
AnnotationErrors.java:20:18: compiler.err.record.patterns.annotations.not.allowed
AnnotationErrors.java:21:18: compiler.err.record.patterns.annotations.not.allowed
AnnotationErrors.java:22:18: compiler.err.record.patterns.annotations.not.allowed
AnnotationErrors.java:26:20: compiler.err.record.patterns.annotations.not.allowed
AnnotationErrors.java:27:20: compiler.err.record.patterns.annotations.not.allowed
10 errors

View File

@ -85,16 +85,14 @@ public class CaseStructureTest extends ComboInstance<CaseStructureTest> {
protected void doWork() throws Throwable { protected void doWork() throws Throwable {
String labelSeparator = asCaseLabelElements ? ", " : ": case "; String labelSeparator = asCaseLabelElements ? ", " : ": case ";
String labels = Arrays.stream(caseLabels).filter(l -> l != CaseLabel.NONE).map(l -> l.code).collect(Collectors.joining(labelSeparator, "case ", ": break;")); String labels = Arrays.stream(caseLabels).filter(l -> l != CaseLabel.NONE).map(l -> l.code).collect(Collectors.joining(labelSeparator, "case ", ": break;"));
boolean hasDefault = Arrays.stream(caseLabels).anyMatch(l -> l == CaseLabel.DEFAULT || l == CaseLabel.TYPE_PATTERN || l == CaseLabel.PARENTHESIZED_PATTERN); boolean hasDefault = Arrays.stream(caseLabels).anyMatch(l -> l == CaseLabel.DEFAULT || l == CaseLabel.TYPE_PATTERN);
ComboTask task = newCompilationTask() ComboTask task = newCompilationTask()
.withSourceFromTemplate(MAIN_TEMPLATE.replace("#{CASES}", labels).replace("#{DEFAULT}", hasDefault ? "" : "default: break;")) .withSourceFromTemplate(MAIN_TEMPLATE.replace("#{CASES}", labels).replace("#{DEFAULT}", hasDefault ? "" : "default: break;"));
.withOption("--enable-preview")
.withOption("-source").withOption(JAVA_VERSION);
task.generate(result -> { task.generate(result -> {
boolean shouldPass = true; boolean shouldPass = true;
long patternCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.TYPE_PATTERN || l == CaseLabel.PARENTHESIZED_PATTERN).count(); long patternCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.TYPE_PATTERN).count();
long constantCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.CONSTANT).count(); long constantCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.CONSTANT).count();
long nullCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.NULL).count(); long nullCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.NULL).count();
long defaultCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.DEFAULT).count(); long defaultCases = Arrays.stream(caseLabels).filter(l -> l == CaseLabel.DEFAULT).count();
@ -136,7 +134,6 @@ public class CaseStructureTest extends ComboInstance<CaseStructureTest> {
public enum CaseLabel implements ComboParameter { public enum CaseLabel implements ComboParameter {
NONE(""), NONE(""),
TYPE_PATTERN("Integer i"), TYPE_PATTERN("Integer i"),
PARENTHESIZED_PATTERN("(Integer i)"),
CONSTANT("1"), CONSTANT("1"),
NULL("null"), NULL("null"),
DEFAULT("default"); DEFAULT("default");

View File

@ -25,8 +25,8 @@
* @test * @test
* @bug 8291769 8301858 8304694 8304883 * @bug 8291769 8301858 8304694 8304883
* @summary Verify more complex switches work properly * @summary Verify more complex switches work properly
* @compile --enable-preview -source ${jdk.version} DeconstructionDesugaring.java * @compile DeconstructionDesugaring.java
* @run main/othervm --enable-preview DeconstructionDesugaring * @run main DeconstructionDesugaring
*/ */
import java.util.Objects; import java.util.Objects;
@ -79,7 +79,7 @@ public class DeconstructionDesugaring {
private int runCheckStatement(Object o) { private int runCheckStatement(Object o) {
switch (o) { switch (o) {
case (((R1((((R2((((String s))))))))))) -> { return 1; } case R1(R2(String s)) -> { return 1; }
case R1(R2(Integer i)) -> { return 2; } case R1(R2(Integer i)) -> { return 2; }
case R1(R2(Double d)) -> { return 3; } case R1(R2(Double d)) -> { return 3; }
case R1(R2(CharSequence cs)) -> { return 4; } case R1(R2(CharSequence cs)) -> { return 4; }
@ -93,7 +93,7 @@ public class DeconstructionDesugaring {
private int runCheckExpression(Object o) { private int runCheckExpression(Object o) {
return switch (o) { return switch (o) {
case (((R1((((R2((((String s))))))))))) -> 1; case R1(R2(String s)) -> 1;
case R1(R2(Integer i)) -> 2; case R1(R2(Integer i)) -> 2;
case R1(R2(Double d)) -> 3; case R1(R2(Double d)) -> 3;
case R1(R2(CharSequence cs)) -> 4; case R1(R2(CharSequence cs)) -> 4;

View File

@ -1,9 +1,9 @@
/** /**
* @test /nodynamiccopyright/ * @test /nodynamiccopyright/
* @summary Verify error reports for erroneous deconstruction patterns are sensible * @summary Verify error reports for erroneous deconstruction patterns are sensible
* @enablePreview
* @compile/fail/ref=DeconstructionPatternErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW -XDdev DeconstructionPatternErrors.java * @compile/fail/ref=DeconstructionPatternErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW -XDdev DeconstructionPatternErrors.java
*/ */
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@ -26,7 +26,4 @@ DeconstructionPatternErrors.java:31:29: compiler.err.prob.found.req: (compiler.m
DeconstructionPatternErrors.java:32:29: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long) DeconstructionPatternErrors.java:32:29: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
DeconstructionPatternErrors.java:34:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, byte) DeconstructionPatternErrors.java:34:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, byte)
DeconstructionPatternErrors.java:35:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long) DeconstructionPatternErrors.java:35:21: compiler.err.prob.found.req: (compiler.misc.not.applicable.types: int, long)
DeconstructionPatternErrors.java:44:9: compiler.err.not.exhaustive.statement 28 errors
- compiler.note.preview.filename: DeconstructionPatternErrors.java, DEFAULT
- compiler.note.preview.recompile
29 errors

View File

@ -28,37 +28,22 @@
* jdk.compiler/com.sun.tools.javac.parser * jdk.compiler/com.sun.tools.javac.parser
* jdk.compiler/com.sun.tools.javac.tree * jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util * jdk.compiler/com.sun.tools.javac.util
* @enablePreview
*/ */
import com.sun.source.tree.CaseLabelTree; import com.sun.source.tree.CaseLabelTree;
import com.sun.source.tree.ClassTree; import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ConstantCaseLabelTree; import com.sun.source.tree.ConstantCaseLabelTree;
import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.MethodTree; import com.sun.source.tree.MethodTree;
import com.sun.source.tree.PatternCaseLabelTree; import com.sun.source.tree.PatternCaseLabelTree;
import com.sun.source.tree.PatternTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.SwitchTree; import com.sun.source.tree.SwitchTree;
import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.parser.JavacParser; import com.sun.tools.javac.parser.JavacParser;
import com.sun.tools.javac.parser.ParserFactory; import com.sun.tools.javac.parser.ParserFactory;
import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.main.Option; import com.sun.tools.javac.main.Option;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Options; import com.sun.tools.javac.util.Options;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
public class DisambiguatePatterns { public class DisambiguatePatterns {
@ -68,11 +53,11 @@ public class DisambiguatePatterns {
ExpressionType.PATTERN); ExpressionType.PATTERN);
test.disambiguationTest("String s when s.isEmpty()", test.disambiguationTest("String s when s.isEmpty()",
ExpressionType.PATTERN); ExpressionType.PATTERN);
test.disambiguationTest("(String s)", test.disambiguationTest("String s",
ExpressionType.PATTERN); ExpressionType.PATTERN);
test.disambiguationTest("(@Ann String s)", test.disambiguationTest("@Ann String s",
ExpressionType.PATTERN); ExpressionType.PATTERN);
test.disambiguationTest("((String s))", test.disambiguationTest("String s",
ExpressionType.PATTERN); ExpressionType.PATTERN);
test.disambiguationTest("(String) s", test.disambiguationTest("(String) s",
ExpressionType.EXPRESSION); ExpressionType.EXPRESSION);
@ -92,7 +77,7 @@ public class DisambiguatePatterns {
ExpressionType.EXPRESSION); ExpressionType.EXPRESSION);
test.disambiguationTest("(a << b || a < b | a >>> b)", test.disambiguationTest("(a << b || a < b | a >>> b)",
ExpressionType.EXPRESSION); ExpressionType.EXPRESSION);
test.disambiguationTest("(a < c.d > b)", test.disambiguationTest("a < c.d > b",
ExpressionType.PATTERN); ExpressionType.PATTERN);
test.disambiguationTest("a<? extends c.d> b", test.disambiguationTest("a<? extends c.d> b",
ExpressionType.PATTERN); ExpressionType.PATTERN);
@ -124,44 +109,14 @@ public class DisambiguatePatterns {
ExpressionType.EXPRESSION); ExpressionType.EXPRESSION);
test.disambiguationTest("a & b", test.disambiguationTest("a & b",
ExpressionType.EXPRESSION); ExpressionType.EXPRESSION);
test.forDisambiguationTest("T[] a", ForType.ENHANCED_FOR);
test.forDisambiguationTest("T[].class.getName()", ForType.TRADITIONAL_FOR);
test.forDisambiguationTest("T[].class", ForType.TRADITIONAL_FOR, "compiler.err.not.stmt");
test.forDisambiguationTest("R(T[] a)", ForType.ENHANCED_FOR_WITH_PATTERNS);
test.forDisambiguationTest("Point(Integer a, Integer b)", ForType.ENHANCED_FOR_WITH_PATTERNS);
test.forDisambiguationTest("ForEachPatterns.Point(Integer a, Integer b)", ForType.ENHANCED_FOR_WITH_PATTERNS);
test.forDisambiguationTest("GPoint<Integer>(Integer a, Integer b)", ForType.ENHANCED_FOR_WITH_PATTERNS);
test.forDisambiguationTest("@Annot(field = \"test\") Point p", ForType.ENHANCED_FOR);
test.forDisambiguationTest("GPoint<Point>(Point(Integer a, Integer b), Point c)", ForType.ENHANCED_FOR_WITH_PATTERNS);
test.forDisambiguationTest("GPoint<Point>(Point(var a, Integer b), Point c)", ForType.ENHANCED_FOR_WITH_PATTERNS);
test.forDisambiguationTest("GPoint<VoidPoint>(VoidPoint(), VoidPoint())", ForType.ENHANCED_FOR_WITH_PATTERNS);
test.forDisambiguationTest("RecordOfLists(List<Integer> lr)", ForType.ENHANCED_FOR_WITH_PATTERNS);
test.forDisambiguationTest("RecordOfLists2(List<List<Integer>> lr)", ForType.ENHANCED_FOR_WITH_PATTERNS);
test.forDisambiguationTest("GPoint<@Annot(field = \"\") ? extends Point>(var x, var y)", ForType.ENHANCED_FOR_WITH_PATTERNS);
test.forDisambiguationTest("method()", ForType.TRADITIONAL_FOR);
test.forDisambiguationTest("method(), method()", ForType.TRADITIONAL_FOR);
test.forDisambiguationTest("method2((Integer a) -> 42)", ForType.TRADITIONAL_FOR);
test.forDisambiguationTest("m(cond ? b() : i)", ForType.TRADITIONAL_FOR);
test.forDisambiguationTest("m((GPoint<?>)null, cond ? b() : i)", ForType.TRADITIONAL_FOR);
} }
private final ParserFactory factory; private final ParserFactory factory;
private final List<String> errors = new ArrayList<>();
public DisambiguatePatterns() throws URISyntaxException { public DisambiguatePatterns() {
Context context = new Context(); Context context = new Context();
context.put(DiagnosticListener.class, d -> {
if (d.getKind() == Diagnostic.Kind.ERROR) {
errors.add(d.getCode());
}
});
JavacFileManager jfm = new JavacFileManager(context, true, Charset.defaultCharset()); JavacFileManager jfm = new JavacFileManager(context, true, Charset.defaultCharset());
Options.instance(context).put(Option.PREVIEW, ""); Options.instance(context).put(Option.PREVIEW, "");
SimpleJavaFileObject source =
new SimpleJavaFileObject(new URI("mem://Test.java"), JavaFileObject.Kind.SOURCE) {};
Log.instance(context).useSource(source);
factory = ParserFactory.instance(context); factory = ParserFactory.instance(context);
} }
@ -192,66 +147,9 @@ public class DisambiguatePatterns {
} }
} }
void forDisambiguationTest(String snippet, ForType forType, String... expectedErrors) {
errors.clear();
String codeTemplate = switch (forType) {
case TRADITIONAL_FOR ->
"""
public class Test {
private void test() {
for (SNIPPET; ;) {
}
}
}
""";
case ENHANCED_FOR, ENHANCED_FOR_WITH_PATTERNS ->
"""
public class Test {
private void test() {
for (SNIPPET : collection) {
}
}
}
""";
};
String code = codeTemplate.replace("SNIPPET", snippet);
JavacParser parser = factory.newParser(code, false, false, false);
CompilationUnitTree result = parser.parseCompilationUnit();
if (!Arrays.asList(expectedErrors).equals(errors)) {
throw new AssertionError("Expected errors: " + Arrays.asList(expectedErrors) +
", actual: " + errors +
", for: " + code);
}
ClassTree clazz = (ClassTree) result.getTypeDecls().get(0);
MethodTree method = (MethodTree) clazz.getMembers().get(0);
StatementTree st = method.getBody().getStatements().get(0);
if (forType == ForType.TRADITIONAL_FOR) {
if (st.getKind() != Kind.FOR_LOOP) {
throw new AssertionError("Unpected statement: " + st);
}
} else {
EnhancedForLoopTree ef = (EnhancedForLoopTree) st;
ForType actualType = switch (ef.getVariableOrRecordPattern()) {
case PatternTree pattern -> ForType.ENHANCED_FOR_WITH_PATTERNS;
default -> ForType.ENHANCED_FOR;
};
if (forType != actualType) {
throw new AssertionError("Expected: " + forType + ", actual: " + actualType +
", for: " + code + ", parsed: " + result);
}
}
}
enum ExpressionType { enum ExpressionType {
PATTERN, PATTERN,
EXPRESSION; EXPRESSION;
} }
enum ForType {
TRADITIONAL_FOR,
ENHANCED_FOR,
ENHANCED_FOR_WITH_PATTERNS;
}
} }

View File

@ -25,9 +25,9 @@
* @test * @test
* @bug 8262891 8290709 * @bug 8262891 8290709
* @summary Check the pattern domination error are reported correctly. * @summary Check the pattern domination error are reported correctly.
* @enablePreview
* @compile/fail/ref=Domination.out -XDrawDiagnostics Domination.java * @compile/fail/ref=Domination.out -XDrawDiagnostics Domination.java
*/ */
public class Domination { public class Domination {
int testDominatesError1(Object o) { int testDominatesError1(Object o) {
switch (o) { switch (o) {
@ -180,8 +180,8 @@ public class Domination {
record R(int a) {} record R(int a) {}
Object o = null; Object o = null;
switch (o) { switch (o) {
case ((R r)): return 1; case R r: return 1;
case ((R(int a))): return -1; case R(int a): return -1;
} }
} }
@ -189,8 +189,8 @@ public class Domination {
record R(int a) {} record R(int a) {}
Object o = null; Object o = null;
switch (o) { switch (o) {
case ((R(int a))): return 1; case R(int a): return 1;
case ((R(int a))): return -1; case R(int a): return -1;
} }
} }

View File

@ -16,6 +16,4 @@ Domination.java:184:18: compiler.err.pattern.dominated
Domination.java:193:18: compiler.err.pattern.dominated Domination.java:193:18: compiler.err.pattern.dominated
Domination.java:202:18: compiler.err.pattern.dominated Domination.java:202:18: compiler.err.pattern.dominated
Domination.java:211:18: compiler.err.pattern.dominated Domination.java:211:18: compiler.err.pattern.dominated
- compiler.note.preview.filename: Domination.java, DEFAULT
- compiler.note.preview.recompile
18 errors 18 errors

View File

@ -23,7 +23,6 @@
/** /**
* @test * @test
* @enablePreview
* @compile EmptyRecordClass.java * @compile EmptyRecordClass.java
*/ */

View File

@ -25,7 +25,6 @@
* @test * @test
* @bug 8262891 * @bug 8262891
* @summary Verify pattern switches work properly when the set of enum constant changes. * @summary Verify pattern switches work properly when the set of enum constant changes.
* @enablePreview
* @compile EnumTypeChanges.java * @compile EnumTypeChanges.java
* @compile EnumTypeChanges2.java * @compile EnumTypeChanges2.java
* @run main EnumTypeChanges * @run main EnumTypeChanges

View File

@ -25,10 +25,9 @@
* @test * @test
* @bug 8297118 * @bug 8297118
* @summary Verify pattern switches work properly when the set of enum constant changes. * @summary Verify pattern switches work properly when the set of enum constant changes.
* @compile EnumTypeChangesNonPreview.java * @compile --release 20 EnumTypeChangesNonPreview.java
* @compile EnumTypeChanges2.java * @compile EnumTypeChanges2.java
* @run main EnumTypeChangesNonPreview * @run main EnumTypeChangesNonPreview
* @run main/othervm --enable-preview EnumTypeChangesNonPreview
*/ */
import java.util.function.Function; import java.util.function.Function;

File diff suppressed because it is too large Load Diff

View File

@ -1,254 +0,0 @@
/*
* @test /nodynamiccopyright/
* @summary
* @enablePreview
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
public class ForEachPatterns {
public static void main(String[] args) {
List<Point> in = List.of(new Point(1, 2), new Point(2, 3));
List<IPoint> in_iface = List.of(new Point(1, 2), new Point(2, 3));
List inRaw = List.of(new Point(1, 2), new Point(2, 3), new Frog(3, 4));
List<PointEx> inWithPointEx = List.of(new PointEx(1, 2));
byte[] inBytes = { (byte) 127, (byte) 127 };
List<Point> inWithNullComponent = List.of(new Point(1, null), new Point(2, 3));
Point[] inArray = in.toArray(Point[]::new);
List<WithPrimitives> inWithPrimitives = List.of(new WithPrimitives(1, 2), new WithPrimitives(2, 3));
IParent recs [] = { new Rec(1) };
List<Point> inWithNull = new ArrayList<>();
{
inWithNull.add(new Point(2, 3));
inWithNull.add(null);
}
assertEquals(8, iteratorEnhancedFor(in));
assertEquals(8, arrayEnhancedFor(inArray));
assertEquals(8, simpleDecostructionPatternWithAccesses(in));
assertEx(ForEachPatterns::simpleDecostructionPatternWithAccesses, null, NullPointerException.class);
assertMatchExceptionWithNested(ForEachPatterns::simpleDecostructionPatternWithAccesses, inWithNull, NullPointerException.class);
assertEx(ForEachPatterns::simpleDecostructionPatternWithAccesses, inWithNullComponent, NullPointerException.class);
assertMatchExceptionWithNested(ForEachPatterns::simpleDecostructionPatternException, inWithPointEx, TestPatternFailed.class);
assertEx(ForEachPatterns::simpleDecostructionPatternWithAccesses, (List<Point>) inRaw, ClassCastException.class);
assertEquals(2, simpleDecostructionPatternNoComponentAccess(in));
assertMatchExceptionWithNested(ForEachPatterns::simpleDecostructionPatternNoComponentAccess, inWithNull, NullPointerException.class);
assertEquals(2, simpleDecostructionPatternNoComponentAccess(inWithNullComponent));
assertEquals(8, varAndConcrete(in));
assertEquals(3, returnFromEnhancedFor(in));
assertEquals(0, breakFromEnhancedFor(in));
assertEquals(254, primitiveWidening(inBytes));
assertEquals(8, sealedRecordPassBaseType(in_iface));
assertEquals(8, withPrimitives(inWithPrimitives));
assertEquals(List.of(Color.RED), JEPExample());
assertEquals(1, arrayWithSealed(recs));
}
static int iteratorEnhancedFor(List<Point> points) {
int result = 0;
for (Point(Integer a, Integer b) : points) {
result += a + b;
}
return result;
}
static int arrayEnhancedFor(Point[] points) {
int result = 0;
for (Point(Integer a, Integer b) : points) {
result += a + b;
}
return result;
}
static int simpleDecostructionPatternWithAccesses(List<Point> points) {
int result = 0;
for (Point(var a, var b): points) {
result += a + b;
}
return result;
}
static int simpleDecostructionPatternException(List<PointEx> points) {
int result = 0;
for (PointEx(var a, var b): points) {
result += a + b;
}
return result;
}
static int simpleDecostructionPatternNoComponentAccess(List<Point> points) {
int result = 0;
for (Point(var a, var b): points) {
result += 1;
}
return result;
}
static int varAndConcrete(List<Point> points) {
int result = 0;
for (Point(Integer a, var b): points) {
result += a + b;
}
return result;
}
static int returnFromEnhancedFor(List<Point> points) {
for (Point(var a, var b): points) {
return a + b;
}
return -1;
}
static int breakFromEnhancedFor(List<Point> points) {
int i = 1;
int result = 0;
for (Point(var a, var b): points) {
if (i == 1) break;
else result += a + b;
}
return result;
}
static int sealedRecordPassBaseType(List<IPoint> points) {
int result = 0;
for(Point(var x, var y) : points) {
result += (x + y);
}
return result;
}
static int withPrimitives(List<WithPrimitives> points) {
int result = 0;
for (WithPrimitives(int a, double b): points) {
result += a + (int) b;
}
return result;
}
// Simpler pos tests with local variable declarations
// Should pass now and in the future if local variable
// declaration is subsumed by patterns (not just record patterns)
static int primitiveWidening(byte[] inBytes) {
int acc = 0;
for (int i: inBytes) {
acc += i;
}
return acc;
}
static int applicability1(List<Point> points) {
for (IPoint p: points) {
System.out.println(p);
}
return -1;
}
static int applicability2(List<Object> points) {
for (Object p: points) {
System.out.println(p);
}
return -1;
}
static List<Color> JEPExample() {
Rectangle rect = new Rectangle(
new ColoredPoint(new Point(1,2), Color.RED),
new ColoredPoint(new Point(3,4), Color.GREEN)
);
Rectangle[] rArr = {rect};
return printUpperLeftColors(rArr);
}
//where
static List<Color> printUpperLeftColors(Rectangle[] r) {
List<Color> ret = new ArrayList<>();
for (Rectangle(ColoredPoint(Point p, Color c), ColoredPoint lr): r) {
ret.add(c);
}
return ret;
}
static int arrayWithSealed(IParent[] recs){
for (Rec(int a) : recs) {
return a;
}
return -1;
}
enum Color { RED, GREEN, BLUE }
record ColoredPoint(Point p, Color c) {}
record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}
sealed interface IParent permits Rec {}
record Rec(int a) implements IParent {}
sealed interface IPoint permits Point {}
record Point(Integer x, Integer y) implements IPoint { }
record GPoint<T>(T x, T y) { }
record VoidPoint() { }
record RecordOfLists(List<Integer> o) {}
record RecordOfLists2(List<List<Integer>> o) {}
@Target({ElementType.TYPE_USE, ElementType.LOCAL_VARIABLE})
@interface Annot {
String field();
}
record Frog(Integer x, Integer y) { }
record PointEx(Integer x, Integer y) {
@Override
public Integer x() {
throw new TestPatternFailed(EXCEPTION_MESSAGE);
}
}
record WithPrimitives(int x, double y) { }
static final String EXCEPTION_MESSAGE = "exception-message";
public static class TestPatternFailed extends AssertionError {
public TestPatternFailed(String message) {
super(message);
}
}
// error handling
static void fail(String message) {
throw new AssertionError(message);
}
static void assertEquals(Object expected, Object actual) {
if (!Objects.equals(expected, actual)) {
throw new AssertionError("Expected: " + expected + "," +
"got: " + actual);
}
}
static <T> void assertMatchExceptionWithNested(Function<List<T>, Integer> f, List<T> points, Class<?> nestedExceptionClass) {
try {
f.apply(points);
fail("Expected an exception, but none happened!");
}
catch(Exception ex) {
assertEquals(MatchException.class, ex.getClass());
MatchException me = (MatchException) ex;
assertEquals(nestedExceptionClass, me.getCause().getClass());
}
}
static <T> void assertEx(Function<List<T>, Integer> f, List<T> points, Class<?> exceptionClass) {
try {
f.apply(points);
fail("Expected an exception, but none happened!");
}
catch(Exception ex) {
assertEquals(exceptionClass, ex.getClass());
}
}
}

View File

@ -1,46 +0,0 @@
/*
* @test /nodynamiccopyright/
* @summary
* @enablePreview
* @compile/fail/ref=ForEachPatternsErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW ForEachPatternsErrors.java
*/
import java.util.List;
public class ForEachPatternsErrors {
static void exhaustivity_error1(List<Object> points) {
for (Point(var x, var y): points) {
System.out.println();
}
}
static void exhaustivity_error2(List points) {
for (Point(var x, var y): points) {
System.out.println();
}
}
static void exhaustivity_error3(List<OPoint> opoints) {
for (OPoint(String s, String t) : opoints) {
System.out.println(s);
}
}
static void exhaustivity_error4(List<?> f) {
for (Rec(var x): f){
}
}
static void applicability_error(List<Object> points) {
for (Interface p: points) {
System.out.println(p);
}
}
record Rec(String x) { }
interface Interface {}
sealed interface IPoint permits Point {}
record Point(Integer x, Integer y) implements IPoint { }
record OPoint(Object x, Object y) { }
}

View File

@ -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

View File

@ -1,12 +0,0 @@
/*
* @test /nodynamiccopyright/
* @summary
* @enablePreview
* @compile -XDfind=all ForEachTestAllAnalyzers.java
*/
public class ForEachTestAllAnalyzers {
private void test(Iterable<? extends R> l) {
for (R(Object a) : l) { }
}
record R(Object a) {}
}

View File

@ -24,7 +24,6 @@
/** /**
* @test * @test
* @bug 8298184 * @bug 8298184
* @enablePreview
* @compile GenericRecordDeconstructionPattern.java * @compile GenericRecordDeconstructionPattern.java
* @run main GenericRecordDeconstructionPattern * @run main GenericRecordDeconstructionPattern
*/ */
@ -47,8 +46,6 @@ public class GenericRecordDeconstructionPattern {
runTest(this::runSwitchInference3); runTest(this::runSwitchInference3);
runTest(this::runSwitchInference4); runTest(this::runSwitchInference4);
testInference3(); testInference3();
assertEquals(0, forEachInference(List.of(new Box(""))));
assertEquals(1, forEachInference(List.of(new Box(null))));
assertEquals(1, runIfSuperBound(new Box<>(new StringBuilder()))); assertEquals(1, runIfSuperBound(new Box<>(new StringBuilder())));
assertEquals(1, runIfSuperBound(new Box<>(0))); assertEquals(1, runIfSuperBound(new Box<>(0)));
} }
@ -106,13 +103,6 @@ public class GenericRecordDeconstructionPattern {
: -1; : -1;
} }
int forEachInference(Iterable<I<String>> b) {
for (Box(var s) : b) {
return s == null ? 1 : s.length();
}
return -1;
}
void testInference3() { void testInference3() {
I<I<String>> b = new Box<>(new Box<>(null)); I<I<String>> b = new Box<>(new Box<>(null));
assertEquals(1, runSwitchInferenceNested(b)); assertEquals(1, runSwitchInferenceNested(b));

View File

@ -25,7 +25,6 @@
* @test * @test
* @bug 8262891 8268663 8289894 * @bug 8262891 8268663 8289894
* @summary Check guards implementation. * @summary Check guards implementation.
* @enablePreview
*/ */
import java.util.Objects; import java.util.Objects;
@ -134,24 +133,24 @@ public class Guards {
String typeGuardAfterParenthesizedTrueSwitchStatement(Object o) { String typeGuardAfterParenthesizedTrueSwitchStatement(Object o) {
switch (o) { switch (o) {
case (Integer i) when i == 0: o = String.valueOf(i); return "true"; case Integer i when i == 0: o = String.valueOf(i); return "true";
case (Integer i) when i == 2: o = String.valueOf(i); return "second"; case Integer i when i == 2: o = String.valueOf(i); return "second";
case Object x: return "any"; case Object x: return "any";
} }
} }
String typeGuardAfterParenthesizedTrueSwitchExpression(Object o) { String typeGuardAfterParenthesizedTrueSwitchExpression(Object o) {
return switch (o) { return switch (o) {
case (Integer i) when i == 0: o = String.valueOf(i); yield "true"; case Integer i when i == 0: o = String.valueOf(i); yield "true";
case (Integer i) when i == 2: o = String.valueOf(i); yield "second"; case Integer i when i == 2: o = String.valueOf(i); yield "second";
case Object x: yield "any"; case Object x: yield "any";
}; };
} }
String typeGuardAfterParenthesizedTrueIfStatement(Object o) { String typeGuardAfterParenthesizedTrueIfStatement(Object o) {
if (o != null && o instanceof (Integer i) && i == 0) { if (o != null && o instanceof Integer i && i == 0) {
return "true"; return "true";
} else if (o != null && o instanceof (Integer i) && i == 2 && (o = i) != null) { } else if (o != null && o instanceof Integer i && i == 2 && (o = i) != null) {
return "second"; return "second";
} else { } else {
return "any"; return "any";

View File

@ -25,7 +25,6 @@
* @test * @test
* @bug 8262891 * @bug 8262891
* @summary Check errors reported for guarded patterns. * @summary Check errors reported for guarded patterns.
* @enablePreview
* @compile/fail/ref=GuardsErrors.out -XDrawDiagnostics GuardsErrors.java * @compile/fail/ref=GuardsErrors.out -XDrawDiagnostics GuardsErrors.java
*/ */
@ -41,4 +40,35 @@ public class GuardsErrors {
} }
void variablesInGuards(Object o) {
final int i1;
int i2 = 0;
switch (o) {
case Integer v when (i1 = 0) == 0 -> {}
case Integer v when i2++ == 0 -> {}
case Integer v when ++i2 == 0 -> {}
case Integer v when new Predicate() {
public boolean test() {
final int i;
i = 2;
return i == 2;
}
}.test() -> {}
case Number v1 when v1 instanceof Integer v2 && (v2 = 0) == 0 -> {}
default -> {}
}
}
GuardsErrors(Object o) {
switch (o) {
case Integer v when (f = 0) == 0 -> {}
default -> throw new RuntimeException();
}
}
final int f;
interface Predicate {
public boolean test();
}
} }

Some files were not shown because too many files have changed in this diff Show More