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