8262891: Compiler implementation for Pattern Matching for switch (Preview)

Co-authored-by: Brian Goetz <briangoetz@openjdk.org>
Co-authored-by: Mandy Chung <mchung@openjdk.org>
Co-authored-by: Jan Lahoda <jlahoda@openjdk.org>
Reviewed-by: mcimadamore, forax, godin, psandoz, mchung
This commit is contained in:
Jan Lahoda 2021-06-07 07:01:30 +00:00
parent 3e48244084
commit 908aca29ca
78 changed files with 4795 additions and 292 deletions

View File

@ -49,7 +49,8 @@ TARGETS += $(patsubst %, $(BUILDTOOLS_OUTPUTDIR)/gensrc/%/module-info.java, \
$(INTERIM_LANGTOOLS_MODULES))
$(eval $(call SetupCopyFiles, COPY_PREVIEW_FEATURES, \
FILES := $(TOPDIR)/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java, \
FILES := $(TOPDIR)/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java \
$(TOPDIR)/src/java.base/share/classes/jdk/internal/javac/NoPreview.java, \
DEST := $(BUILDTOOLS_OUTPUTDIR)/gensrc/java.base.interim/jdk/internal/javac/, \
))

View File

@ -0,0 +1,170 @@
/*
* Copyright (c) 2017, 2021, 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 java.lang.runtime;
import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Stream;
import jdk.internal.javac.PreviewFeature;
import static java.util.Objects.requireNonNull;
/**
* Bootstrap methods for linking {@code invokedynamic} call sites that implement
* the selection functionality of the {@code switch} statement. The bootstraps
* take additional static arguments corresponding to the {@code case} labels
* of the {@code switch}, implicitly numbered sequentially from {@code [0..N)}.
*
* @since 17
*/
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING)
public class SwitchBootstraps {
private SwitchBootstraps() {}
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static final MethodHandle DO_SWITCH;
static {
try {
DO_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "doSwitch",
MethodType.methodType(int.class, Object.class, int.class, Object[].class));
}
catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
/**
* 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}.
* <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
* will be an {@code Object} instance ({@code target}) and the second
* will be {@code int} ({@code restart}).
* <p>
* If the {@code target} is {@code null}, then the method of the call site
* returns {@literal -1}.
* <p>
* If the {@code target} is not {@code null}, then the method of the call site
* returns the index of the first element in the {@code labels} array starting from
* the {@code restart} index matching one of the following conditions:
* <ul>
* <li>the element is of type {@code Class} that is assignable
* from the target's class; or</li>
* <li>the element is of type {@code String} or {@code Integer} and
* 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.
*
* @param lookup Represents a lookup context with the accessibility
* privileges of the caller. When used with {@code invokedynamic},
* this is stacked automatically by the VM.
* @param invocationName unused
* @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
* @return a {@code CallSite} returning the first matching element as described above
*
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalArgumentException if any element in the labels array is null, if the
* 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}.
* @throws Throwable if there is any error linking the call site
* @jvms 4.4.6 The CONSTANT_NameAndType_info Structure
* @jvms 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures
*/
public static CallSite typeSwitch(MethodHandles.Lookup lookup,
String invocationName,
MethodType invocationType,
Object... labels) throws Throwable {
if (invocationType.parameterCount() != 2
|| (!invocationType.returnType().equals(int.class))
|| invocationType.parameterType(0).isPrimitive()
|| !invocationType.parameterType(1).equals(int.class))
throw new IllegalArgumentException("Illegal invocation type " + invocationType);
requireNonNull(labels);
labels = labels.clone();
Stream.of(labels).forEach(SwitchBootstraps::verifyLabel);
MethodHandle target = MethodHandles.insertArguments(DO_SWITCH, 2, (Object) labels);
return new ConstantCallSite(target);
}
private static void verifyLabel(Object label) {
if (label == null) {
throw new IllegalArgumentException("null label found");
}
Class<?> labelClass = label.getClass();
if (labelClass != Class.class &&
labelClass != String.class &&
labelClass != Integer.class) {
throw new IllegalArgumentException("label with illegal type found: " + label.getClass());
}
}
private static int doSwitch(Object target, int startIndex, Object[] labels) {
if (target == null)
return -1;
// Dumbest possible strategy
Class<?> targetClass = target.getClass();
for (int i = startIndex; i < labels.length; i++) {
Object label = labels[i];
if (label instanceof Class<?> c) {
if (c.isAssignableFrom(targetClass))
return i;
} else if (label instanceof Integer constant) {
if (target instanceof Number input && constant.intValue() == input.intValue()) {
return i;
} else if (target instanceof Character input && constant.intValue() == input.charValue()) {
return i;
}
} else if (label.equals(target)) {
return i;
}
}
return labels.length;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2021, 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 jdk.internal.javac;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The element annotated with this annotation should not be marked as a preview element.
*/
@Target({ElementType.METHOD,
ElementType.CONSTRUCTOR,
ElementType.FIELD,
ElementType.PACKAGE,
ElementType.MODULE,
ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface NoPreview {
}

View File

@ -58,6 +58,7 @@ public @interface PreviewFeature {
* This one can only be removed after JDK 17
*/
SEALED_CLASSES,
SWITCH_PATTERN_MATCHING,
/**
* A key for testing.
*/

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021, 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 marker interface for {@code Tree}s that may be used as {@link CaseTree} labels.
*
* @since 17
*/
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public interface CaseLabelTree extends Tree {}

View File

@ -27,6 +27,8 @@ 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.
*
@ -65,6 +67,16 @@ public interface CaseTree extends Tree {
*/
List<? extends ExpressionTree> getExpressions();
/**
* Returns the labels for this case.
* For {@code default} case return a list with a single element, {@link DefaultCaseLabelTree}.
*
* @return labels for this case
* @since 17
*/
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
List<? extends CaseLabelTree> getLabels();
/**
* For case with kind {@linkplain CaseKind#STATEMENT},
* returns the statements labeled by the case.

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021, 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 case label that marks {@code default} in {@code case null, default}.
*
* @since 17
*/
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public interface DefaultCaseLabelTree extends CaseLabelTree {}

View File

@ -25,6 +25,8 @@
package com.sun.source.tree;
import jdk.internal.javac.NoPreview;
/**
* A tree node used as the base class for the different types of
* expressions.
@ -35,4 +37,5 @@ package com.sun.source.tree;
* @author Jonathan Gibbons
* @since 1.6
*/
public interface ExpressionTree extends Tree {}
@NoPreview
public interface ExpressionTree extends Tree, CaseLabelTree {}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2017, 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 guard pattern tree.
*
* @since 17
*/
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
public interface GuardedPatternTree extends PatternTree {
/**
* The guarded pattern expression.
* @return the guarded pattern
*/
public PatternTree getPattern();
/**
* The guard expression.
* @return the guard expression
*/
public ExpressionTree getExpression();
}

View File

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

View File

@ -31,4 +31,4 @@ package com.sun.source.tree;
*
* @since 16
*/
public interface PatternTree extends Tree {}
public interface PatternTree extends Tree, CaseLabelTree {}

View File

@ -25,6 +25,8 @@
package com.sun.source.tree;
import jdk.internal.javac.PreviewFeature;
/**
* Common interface for all nodes in an abstract syntax tree.
*
@ -226,6 +228,30 @@ public interface Tree {
*/
BINDING_PATTERN(BindingPatternTree.class),
/**
* Used for instances of {@link GuardedPatternTree}.
*
* @since 17
*/
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
GUARDED_PATTERN(GuardedPatternTree.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
*/
@PreviewFeature(feature=PreviewFeature.Feature.SWITCH_PATTERN_MATCHING, reflective=true)
DEFAULT_CASE_LABEL(DefaultCaseLabelTree.class),
/**
* Used for instances of {@link PrimitiveTypeTree}.
*/

View File

@ -25,6 +25,8 @@
package com.sun.source.tree;
import jdk.internal.javac.PreviewFeature;
/**
* A visitor of trees, in the style of the visitor design pattern.
* Classes implementing this interface are used to operate
@ -266,6 +268,16 @@ public interface TreeVisitor<R,P> {
*/
R visitBindingPattern(BindingPatternTree node, P p);
/**
* Visits a DefaultCaseLabelTree 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 visitDefaultCaseLabel(DefaultCaseLabelTree node, P p);
/**
* Visits a MethodTree node.
* @param node the node being visited
@ -290,6 +302,26 @@ public interface TreeVisitor<R,P> {
*/
R visitNewArray(NewArrayTree node, P p);
/**
* Visits a GuardPatternTree 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 visitGuardedPattern(GuardedPatternTree node, P p);
/**
* Visits a 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 NewClassTree node.
* @param node the node being visited

View File

@ -26,6 +26,7 @@
package com.sun.source.util;
import com.sun.source.tree.*;
import jdk.internal.javac.PreviewFeature;
/**
* A simple visitor for tree nodes.
@ -564,6 +565,20 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
return defaultAction(node, p);
}
/**
* {@inheritDoc} 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 visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) {
return defaultAction(node, p);
}
/**
* {@inheritDoc} This implementation calls {@code defaultAction}.
*
@ -588,6 +603,34 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
return defaultAction(node, p);
}
/**
* {@inheritDoc} 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} 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 visitGuardedPattern(GuardedPatternTree node, P p) {
return defaultAction(node, p);
}
/**
* {@inheritDoc} This implementation calls {@code defaultAction}.
*

View File

@ -26,6 +26,7 @@
package com.sun.source.util;
import com.sun.source.tree.*;
import jdk.internal.javac.PreviewFeature;
/**
* A TreeVisitor that visits all the child tree nodes.
@ -695,6 +696,20 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
return scan(node.getVariable(), p);
}
/**
* {@inheritDoc} This implementation returns {@code null}.
*
* @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 visitDefaultCaseLabel(DefaultCaseLabelTree node, P p) {
return null;
}
/**
* {@inheritDoc} This implementation scans the children in left to right order.
*
@ -721,6 +736,35 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
return scan(node.getExpression(), p);
}
/**
* {@inheritDoc} 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} 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 visitGuardedPattern(GuardedPatternTree node, P p) {
R r = scan(node.getPattern(), p);
return scanAndReduce(node.getExpression(), p, r);
}
/**
* {@inheritDoc} This implementation scans the children in left to right order.
*

View File

@ -50,6 +50,7 @@ import static com.sun.tools.javac.code.Flags.RECORD;
import static com.sun.tools.javac.code.Flags.SEALED;
import static com.sun.tools.javac.code.Flags.NON_SEALED;
import static com.sun.tools.javac.main.Option.PREVIEW;
import com.sun.tools.javac.util.JCDiagnostic;
/**
* Helper class to handle preview language features. This class maps certain language features
@ -79,6 +80,7 @@ public class Preview {
private final Lint lint;
private final Log log;
private final Source source;
private static final Context.Key<Preview> previewKey = new Context.Key<>();
@ -96,7 +98,7 @@ public class Preview {
enabled = options.isSet(PREVIEW);
log = Log.instance(context);
lint = Lint.instance(context);
Source source = Source.instance(context);
source = Source.instance(context);
this.previewHandler =
new MandatoryWarningHandler(log, source, lint.isEnabled(LintCategory.PREVIEW), true, "preview", LintCategory.PREVIEW);
forcePreview = options.isSet("forcePreview");
@ -183,6 +185,9 @@ public class Preview {
*/
public boolean isPreview(Feature feature) {
return switch (feature) {
case CASE_NULL -> true;
case PATTERN_SWITCH -> 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'
//for those selected features, and 'false' for all the others.
@ -236,4 +241,19 @@ public class Preview {
previewHandler.clear();
}
public void checkSourceLevel(DiagnosticPosition pos, Feature feature) {
if (isPreview(feature) && !isEnabled()) {
//preview feature without --preview flag, error
log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos, disabledError(feature));
} else {
if (!feature.allowedInSource(source)) {
log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos,
feature.error(source.name));
}
if (isEnabled() && isPreview(feature)) {
warnPreview(pos, feature);
}
}
}
}

View File

@ -224,6 +224,8 @@ 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),
REDUNDANT_STRICTFP(JDK17),
;

View File

@ -220,6 +220,7 @@ public class Symtab {
public final Type previewFeatureInternalType;
public final Type typeDescriptorType;
public final Type recordType;
public final Type switchBootstrapsType;
public final Type valueBasedType;
/** The symbol representing the length field of an array.
@ -585,6 +586,7 @@ public class Symtab {
previewFeatureInternalType = enterSyntheticAnnotation("jdk.internal.PreviewFeature+Annotation");
typeDescriptorType = enterClass("java.lang.invoke.TypeDescriptor");
recordType = enterClass("java.lang.Record");
switchBootstrapsType = enterClass("java.lang.runtime.SwitchBootstraps");
valueBasedType = enterClass("jdk.internal.ValueBased");
synthesizeEmptyInterfaceIfMissing(autoCloseableType);

View File

@ -1657,15 +1657,28 @@ public class Attr extends JCTree.Visitor {
boolean enumSwitch = (seltype.tsym.flags() & Flags.ENUM) != 0;
boolean stringSwitch = types.isSameType(seltype, syms.stringType);
boolean errorEnumSwitch = TreeInfo.isErrorEnumSwitch(selector, cases);
if (!enumSwitch && !stringSwitch)
seltype = chk.checkType(selector.pos(), seltype, syms.intType);
boolean patternSwitch;
if (!enumSwitch && !stringSwitch && !errorEnumSwitch &&
!types.isAssignable(seltype, syms.intType)) {
preview.checkSourceLevel(selector.pos(), Feature.PATTERN_SWITCH);
patternSwitch = true;
} else {
patternSwitch = cases.stream()
.flatMap(c -> c.labels.stream())
.anyMatch(l -> l.isPattern());
}
// Attribute all cases and
// check that there are no duplicate case labels or default clauses.
Set<Object> labels = new HashSet<>(); // The set of case labels.
boolean hasDefault = false; // Is there a default label?
List<Type> coveredTypes = List.nil();
boolean hasDefault = false; // Is there a default label?
boolean hasTotalPattern = false; // Is there a total pattern?
boolean hasNullPattern = false; // Is there a null pattern?
CaseTree.CaseKind caseKind = null;
boolean wasError = false;
MatchBindings prevBindings = null;
boolean prevCompletedNormally = false;
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
JCCase c = l.head;
if (caseKind == null) {
@ -1675,17 +1688,29 @@ public class Attr extends JCTree.Visitor {
Errors.SwitchMixingCaseTypes);
wasError = true;
}
if (c.getExpressions().nonEmpty()) {
for (JCExpression pat : c.getExpressions()) {
if (TreeInfo.isNull(pat)) {
log.error(pat.pos(),
Errors.SwitchNullNotAllowed);
MatchBindings currentBindings = prevBindings;
boolean wasTotalPattern = hasTotalPattern;
for (JCCaseLabel pat : c.labels) {
if (pat.isExpression()) {
JCExpression expr = (JCExpression) pat;
if (TreeInfo.isNull(expr)) {
preview.checkSourceLevel(expr.pos(), Feature.CASE_NULL);
if (hasNullPattern) {
log.error(c.pos(), Errors.DuplicateCaseLabel);
} else if (wasTotalPattern) {
log.error(c.pos(), Errors.PatternDominated);
}
hasNullPattern = true;
attribExpr(expr, switchEnv, seltype);
matchBindings = new MatchBindings(matchBindings.bindingsWhenTrue, matchBindings.bindingsWhenFalse, true);
} else if (enumSwitch) {
Symbol sym = enumConstant(pat, seltype);
Symbol sym = enumConstant(expr, seltype);
if (sym == null) {
log.error(pat.pos(), Errors.EnumLabelMustBeUnqualifiedEnum);
log.error(expr.pos(), Errors.EnumLabelMustBeUnqualifiedEnum);
} else if (!labels.add(sym)) {
log.error(c.pos(), Errors.DuplicateCaseLabel);
} else {
checkCaseLabelDominated(pat.pos(), coveredTypes, sym.type);
}
} else if (errorEnumSwitch) {
//error recovery: the selector is erroneous, and all the case labels
@ -1699,30 +1724,88 @@ public class Attr extends JCTree.Visitor {
rs.basicLogResolveHelper = prevResolveHelper;
}
} else {
Type pattype = attribExpr(pat, switchEnv, seltype);
Type pattype = attribExpr(expr, switchEnv, seltype);
if (!pattype.hasTag(ERROR)) {
if (!stringSwitch && !types.isAssignable(seltype, syms.intType)) {
log.error(pat.pos(), Errors.ConstantLabelNotCompatible(pattype, seltype));
}
if (pattype.constValue() == null) {
log.error(pat.pos(),
log.error(expr.pos(),
(stringSwitch ? Errors.StringConstReq : Errors.ConstExprReq));
} else if (!labels.add(pattype.constValue())) {
log.error(c.pos(), Errors.DuplicateCaseLabel);
} else {
checkCaseLabelDominated(pat.pos(), coveredTypes, types.boxedTypeOrType(pattype));
}
}
}
} else if (pat.hasTag(DEFAULTCASELABEL)) {
if (hasDefault) {
log.error(pat.pos(), Errors.DuplicateDefaultLabel);
} else if (hasTotalPattern) {
log.error(pat.pos(), Errors.TotalPatternAndDefault);
} else if (matchBindings.bindingsWhenTrue.nonEmpty()) {
//there was a pattern, and the execution flows into a default:
log.error(pat.pos(), Errors.FlowsThroughFromPattern);
}
hasDefault = true;
matchBindings = MatchBindingsComputer.EMPTY;
} else {
if (prevCompletedNormally) {
log.error(pat.pos(), Errors.FlowsThroughToPattern);
}
//binding pattern
attribExpr(pat, switchEnv);
var primary = TreeInfo.primaryPatternType((JCPattern) pat);
Type primaryType = primary.type();
if (!primaryType.hasTag(TYPEVAR)) {
primaryType = chk.checkClassOrArrayType(pat.pos(), primaryType);
}
checkCastablePattern(pat.pos(), seltype, primaryType);
Type patternType = types.erasure(primaryType);
boolean isTotal = primary.unconditional() &&
!patternType.isErroneous() &&
types.isSubtype(types.erasure(seltype), patternType);
if (isTotal) {
if (hasTotalPattern) {
log.error(pat.pos(), Errors.DuplicateTotalPattern);
} else if (hasDefault) {
log.error(pat.pos(), Errors.TotalPatternAndDefault);
}
hasTotalPattern = true;
}
checkCaseLabelDominated(pat.pos(), coveredTypes, patternType);
if (primary.unconditional() && !patternType.isErroneous()) {
coveredTypes = coveredTypes.prepend(patternType);
}
}
} else if (hasDefault) {
log.error(c.pos(), Errors.DuplicateDefaultLabel);
} else {
hasDefault = true;
currentBindings = matchBindingsComputer.switchCase(pat, currentBindings, matchBindings);
prevCompletedNormally = !TreeInfo.isNull(pat);
}
Env<AttrContext> caseEnv =
switchEnv.dup(c, env.info.dup(switchEnv.info.scope.dup()));
bindingEnv(switchEnv, c, currentBindings.bindingsWhenTrue);
try {
attribCase.accept(c, caseEnv);
} finally {
caseEnv.info.scope.leave();
}
addVars(c.stats, switchEnv.info.scope);
boolean completesNormally = c.caseKind == CaseTree.CaseKind.STATEMENT ? flow.aliveAfter(caseEnv, c, make) : false;
prevBindings = completesNormally ? currentBindings : null;
prevCompletedNormally =
completesNormally &&
!(c.labels.size() == 1 &&
TreeInfo.isNull(c.labels.head) && c.stats.isEmpty());
}
if (switchTree.hasTag(SWITCH)) {
((JCSwitch) switchTree).hasTotalPattern = hasDefault || hasTotalPattern;
((JCSwitch) switchTree).patternSwitch = patternSwitch;
} else if (switchTree.hasTag(SWITCH_EXPRESSION)) {
((JCSwitchExpression) switchTree).hasTotalPattern = hasDefault || hasTotalPattern;
((JCSwitchExpression) switchTree).patternSwitch = patternSwitch;
} else {
Assert.error(switchTree.getTag().name());
}
} finally {
switchEnv.info.scope.leave();
@ -1737,6 +1820,14 @@ public class Attr extends JCTree.Visitor {
switchScope.enter(((JCVariableDecl) stat).sym);
}
}
private void checkCaseLabelDominated(DiagnosticPosition pos,
List<Type> coveredTypes, Type patternType) {
for (Type existing : coveredTypes) {
if (types.isSubtype(patternType, existing)) {
log.error(pos, Errors.PatternDominated);
}
}
}
// where
/** Return the selected enumeration constant symbol, or null. */
private Symbol enumConstant(JCTree tree, Type enumType) {
@ -2080,7 +2171,11 @@ public class Attr extends JCTree.Visitor {
};
Env<AttrContext> bindingEnv(Env<AttrContext> env, List<BindingSymbol> bindings) {
Env<AttrContext> env1 = env.dup(env.tree, env.info.dup(env.info.scope.dup()));
return bindingEnv(env, env.tree, bindings);
}
Env<AttrContext> bindingEnv(Env<AttrContext> env, JCTree newTree, List<BindingSymbol> bindings) {
Env<AttrContext> env1 = env.dup(newTree, env.info.dup(env.info.scope.dup()));
bindings.forEach(env1.info.scope::enter);
return env1;
}
@ -3994,16 +4089,7 @@ public class Attr extends JCTree.Visitor {
if (!clazztype.isErroneous() && !types.isReifiable(clazztype)) {
boolean valid = false;
if (allowReifiableTypesInInstanceof) {
Warner warner = new Warner();
if (!types.isCastable(exprtype, clazztype, warner)) {
chk.basicHandler.report(tree.expr.pos(),
diags.fragment(Fragments.InconvertibleTypes(exprtype, clazztype)));
} else if (warner.hasLint(LintCategory.UNCHECKED)) {
log.error(tree.expr.pos(),
Errors.InstanceofReifiableNotSafe(exprtype, clazztype));
} else {
valid = true;
}
valid = checkCastablePattern(tree.expr.pos(), exprtype, clazztype);
} else {
log.error(DiagnosticFlag.SOURCE_LEVEL, tree.pos(),
Feature.REIFIABLE_TYPES_INSTANCEOF.error(this.sourceName));
@ -4018,6 +4104,23 @@ public class Attr extends JCTree.Visitor {
result = check(tree, syms.booleanType, KindSelector.VAL, resultInfo);
}
private boolean checkCastablePattern(DiagnosticPosition pos,
Type exprType,
Type pattType) {
Warner warner = new Warner();
if (!types.isCastable(exprType, pattType, warner)) {
chk.basicHandler.report(pos,
diags.fragment(Fragments.InconvertibleTypes(exprType, pattType)));
return false;
} else if (warner.hasLint(LintCategory.UNCHECKED)) {
log.error(pos,
Errors.InstanceofReifiableNotSafe(exprType, pattType));
return false;
} else {
return true;
}
}
public void visitBindingPattern(JCBindingPattern tree) {
ResultInfo varInfo = new ResultInfo(KindSelector.TYP, resultInfo.pt, resultInfo.checkContext);
tree.type = tree.var.type = attribTree(tree.var.vartype, env, varInfo);
@ -4034,6 +4137,25 @@ public class Attr extends JCTree.Visitor {
matchBindings = new MatchBindings(List.of(v), List.nil());
}
@Override
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
attribExpr(tree.pattern, env);
}
@Override
public void visitGuardPattern(JCGuardPattern tree) {
attribExpr(tree.patt, env);
MatchBindings afterPattern = matchBindings;
Env<AttrContext> bodyEnv = bindingEnv(env, matchBindings.bindingsWhenTrue);
try {
attribExpr(tree.expr, bodyEnv, syms.booleanType);
} finally {
bodyEnv.info.scope.leave();
}
result = tree.type = tree.patt.type;
matchBindings = matchBindingsComputer.guardedPattern(tree, afterPattern, matchBindings);
}
public void visitIndexed(JCArrayAccess tree) {
Type owntype = types.createErrorType(tree.type);
Type atype = attribExpr(tree.indexed, env);

View File

@ -39,6 +39,7 @@ 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.*;
import com.sun.tools.javac.tree.TreeInfo.PatternPrimaryType;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.JCDiagnostic.Error;
@ -53,7 +54,10 @@ import static com.sun.tools.javac.code.Flags.BLOCK;
import static com.sun.tools.javac.code.Kinds.Kind.*;
import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
import static com.sun.tools.javac.code.TypeTag.VOID;
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
import com.sun.tools.javac.tree.JCTree.JCParenthesizedPattern;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
import com.sun.tools.javac.util.JCDiagnostic.Fragment;
/** This pass implements dataflow analysis for Java programs though
* different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that
@ -661,16 +665,13 @@ public class Flow {
ListBuffer<PendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>();
scan(tree.selector);
boolean hasDefault = false;
Set<Object> constants = tree.patternSwitch ? allSwitchConstants(tree.selector) : null;
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
alive = Liveness.ALIVE;
JCCase c = l.head;
if (c.pats.isEmpty())
hasDefault = true;
else {
for (JCExpression pat : c.pats) {
scan(pat);
}
for (JCCaseLabel pat : c.labels) {
scan(pat);
handleConstantCaseLabel(constants, pat);
}
scanStats(c.stats);
c.completesNormally = alive != Liveness.DEAD;
@ -686,7 +687,11 @@ public class Flow {
l.tail.head.pos(),
Warnings.PossibleFallThroughIntoCase);
}
if (!hasDefault) {
if ((constants == null || !constants.isEmpty()) && !tree.hasTotalPattern &&
tree.patternSwitch && !TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases)) {
log.error(tree, Errors.NotExhaustiveStatement);
}
if (!tree.hasTotalPattern) {
alive = Liveness.ALIVE;
}
alive = alive.or(resolveBreaks(tree, prevPendingExits));
@ -697,33 +702,14 @@ public class Flow {
ListBuffer<PendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>();
scan(tree.selector);
Set<Object> constants = null;
TypeSymbol selectorSym = tree.selector.type.tsym;
if ((selectorSym.flags() & ENUM) != 0) {
constants = new HashSet<>();
Predicate<Symbol> enumConstantFilter =
s -> (s.flags() & ENUM) != 0 && s.kind == Kind.VAR;
for (Symbol s : selectorSym.members().getSymbols(enumConstantFilter)) {
constants.add(s.name);
}
}
boolean hasDefault = false;
Set<Object> constants = allSwitchConstants(tree.selector);
Liveness prevAlive = alive;
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
alive = Liveness.ALIVE;
JCCase c = l.head;
if (c.pats.isEmpty())
hasDefault = true;
else {
for (JCExpression pat : c.pats) {
scan(pat);
if (constants != null) {
if (pat.hasTag(IDENT))
constants.remove(((JCIdent) pat).name);
if (pat.type != null)
constants.remove(pat.type.constValue());
}
}
for (JCCaseLabel pat : c.labels) {
scan(pat);
handleConstantCaseLabel(constants, pat);
}
scanStats(c.stats);
if (alive == Liveness.ALIVE) {
@ -737,7 +723,7 @@ public class Flow {
}
c.completesNormally = alive != Liveness.DEAD;
}
if ((constants == null || !constants.isEmpty()) && !hasDefault &&
if ((constants == null || !constants.isEmpty()) && !tree.hasTotalPattern &&
!TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases)) {
log.error(tree, Errors.NotExhaustive);
}
@ -745,6 +731,39 @@ public class Flow {
alive = alive.or(resolveYields(tree, prevPendingExits));
}
private Set<Object> allSwitchConstants(JCExpression selector) {
Set<Object> constants = null;
TypeSymbol selectorSym = selector.type.tsym;
if ((selectorSym.flags() & ENUM) != 0) {
constants = new HashSet<>();
Predicate<Symbol> enumConstantFilter =
s -> (s.flags() & ENUM) != 0 && s.kind == Kind.VAR;
for (Symbol s : selectorSym.members().getSymbols(enumConstantFilter)) {
constants.add(s.name);
}
} else if (selectorSym.isAbstract() && selectorSym.isSealed() && selectorSym.kind == Kind.TYP) {
constants = new HashSet<>();
constants.addAll(((ClassSymbol) selectorSym).permitted);
}
return constants;
}
private void handleConstantCaseLabel(Set<Object> constants, JCCaseLabel pat) {
if (constants != null) {
if (pat.isExpression()) {
JCExpression expr = (JCExpression) pat;
if (expr.hasTag(IDENT))
constants.remove(((JCIdent) expr).name);
} else if (pat.isPattern()) {
PatternPrimaryType patternType = TreeInfo.primaryPatternType((JCPattern) pat);
if (patternType.unconditional()) {
constants.remove(patternType.type().tsym);
}
}
}
}
public void visitTry(JCTry tree) {
ListBuffer<PendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>();
@ -1194,7 +1213,7 @@ public class Flow {
scan(selector);
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
JCCase c = l.head;
scan(c.pats);
scan(c.labels);
scan(c.stats);
}
if (tree.hasTag(SWITCH_EXPRESSION)) {
@ -2352,52 +2371,54 @@ public class Flow {
}
public void visitSwitch(JCSwitch tree) {
handleSwitch(tree, tree.selector, tree.cases);
handleSwitch(tree, tree.selector, tree.cases, tree.hasTotalPattern);
}
public void visitSwitchExpression(JCSwitchExpression tree) {
handleSwitch(tree, tree.selector, tree.cases);
handleSwitch(tree, tree.selector, tree.cases, tree.hasTotalPattern);
}
private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
private void handleSwitch(JCTree tree, JCExpression selector,
List<JCCase> cases, boolean hasTotalPattern) {
ListBuffer<PendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>();
int nextadrPrev = nextadr;
scanExpr(selector);
final Bits initsSwitch = new Bits(inits);
final Bits uninitsSwitch = new Bits(uninits);
boolean hasDefault = false;
for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
inits.assign(initsSwitch);
uninits.assign(uninits.andSet(uninitsSwitch));
JCCase c = l.head;
if (c.pats.isEmpty()) {
hasDefault = true;
} else {
for (JCExpression pat : c.pats) {
scanExpr(pat);
}
for (JCCaseLabel pat : c.labels) {
scan(pat);
}
if (hasDefault) {
inits.assign(initsSwitch);
uninits.assign(uninits.andSet(uninitsSwitch));
if (l.head.stats.isEmpty() &&
l.tail.nonEmpty() &&
l.tail.head.labels.size() == 1 &&
l.tail.head.labels.head.isExpression() &&
TreeInfo.isNull(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.stats);
if (c.completesNormally && c.caseKind == JCCase.RULE) {
scanSyntheticBreak(make, tree);
}
addVars(c.stats, initsSwitch, uninitsSwitch);
if (!hasDefault) {
inits.assign(initsSwitch);
uninits.assign(uninits.andSet(uninitsSwitch));
}
// Warn about fall-through if lint switch fallthrough enabled.
}
if (!hasDefault) {
if (!hasTotalPattern) {
if (tree.hasTag(SWITCH_EXPRESSION)) {
markDead();
} else {
inits.andSet(initsSwitch);
inits.assign(initsSwitch);
uninits.assign(uninits.andSet(uninitsSwitch));
}
}
if (tree.hasTag(SWITCH_EXPRESSION)) {
@ -2852,6 +2873,7 @@ public class Flow {
}
break;
}
case GUARDPATTERN:
case LAMBDA:
if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) {
reportEffectivelyFinalError(pos, sym);
@ -2875,6 +2897,7 @@ public class Flow {
reportInnerClsNeedsFinalError(tree, sym);
break;
}
case GUARDPATTERN:
case LAMBDA:
reportEffectivelyFinalError(tree, sym);
}
@ -2883,8 +2906,12 @@ public class Flow {
}
void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) {
String subKey = currentTree.hasTag(LAMBDA) ?
"lambda" : "inner.cls";
Fragment subKey = switch (currentTree.getTag()) {
case LAMBDA -> Fragments.Lambda;
case GUARDPATTERN -> Fragments.Guard;
case CLASSDEF -> Fragments.InnerCls;
default -> throw new AssertionError("Unexpected tree kind: " + currentTree.getTag());
};
log.error(pos, Errors.CantRefNonEffectivelyFinalVar(sym, diags.fragment(subKey)));
}
@ -2920,6 +2947,18 @@ public class Flow {
}
}
@Override
public void visitGuardPattern(JCGuardPattern tree) {
scan(tree.patt);
JCTree prevTree = currentTree;
try {
currentTree = tree;
scan(tree.expr);
} finally {
currentTree = prevTree;
}
}
@Override
public void visitIdent(JCIdent tree) {
if (tree.sym.kind == VAR) {

View File

@ -3583,18 +3583,25 @@ public class Lower extends TreeTranslator {
}
public void visitSwitch(JCSwitch tree) {
handleSwitch(tree, tree.selector, tree.cases);
List<JCCase> cases = tree.patternSwitch ? addDefaultIfNeeded(tree.cases) : tree.cases;
handleSwitch(tree, tree.selector, cases);
}
@Override
public void visitSwitchExpression(JCSwitchExpression tree) {
if (tree.cases.stream().noneMatch(c -> c.pats.isEmpty())) {
List<JCCase> cases = addDefaultIfNeeded(tree.cases);
handleSwitch(tree, tree.selector, cases);
}
private List<JCCase> addDefaultIfNeeded(List<JCCase> cases) {
if (cases.stream().flatMap(c -> c.labels.stream()).noneMatch(p -> p.hasTag(Tag.DEFAULTCASELABEL))) {
JCThrow thr = make.Throw(makeNewClass(syms.incompatibleClassChangeErrorType,
List.nil()));
JCCase c = make.Case(JCCase.STATEMENT, List.nil(), List.of(thr), null);
tree.cases = tree.cases.append(c);
JCCase c = make.Case(JCCase.STATEMENT, List.of(make.DefaultCaseLabel()), List.of(thr), null);
cases = cases.append(c);
}
handleSwitch(tree, tree.selector, tree.cases);
return cases;
}
private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
@ -3602,7 +3609,7 @@ public class Lower extends TreeTranslator {
ListBuffer<JCCase> convertedCases = new ListBuffer<>();
for (JCCase c : cases) {
switch (c.pats.size()) {
switch (c.labels.size()) {
case 0: //default
case 1: //single label
convertedCases.append(c);
@ -3613,7 +3620,7 @@ public class Lower extends TreeTranslator {
//case C1:
//case C2:
//case C3: ...
List<JCExpression> patterns = c.pats;
List<JCCaseLabel> patterns = c.labels;
while (patterns.tail.nonEmpty()) {
convertedCases.append(make_at(c.pos()).Case(JCCase.STATEMENT,
List.of(patterns.head),
@ -3621,7 +3628,7 @@ public class Lower extends TreeTranslator {
null));
patterns = patterns.tail;
}
c.pats = patterns;
c.labels = patterns;
convertedCases.append(c);
break;
}
@ -3629,7 +3636,7 @@ public class Lower extends TreeTranslator {
for (JCCase c : convertedCases) {
if (c.caseKind == JCCase.RULE && c.completesNormally) {
JCBreak b = make_at(c.pos()).Break(null);
JCBreak b = make.at(TreeInfo.endPos(c.stats.last())).Break(null);
b.target = tree;
c.stats = c.stats.append(b);
}
@ -3642,9 +3649,8 @@ public class Lower extends TreeTranslator {
(selector.type.tsym.flags() & ENUM) != 0;
boolean stringSwitch = selsuper != null &&
types.isSameType(selector.type, syms.stringType);
Type target = enumSwitch ? selector.type :
(stringSwitch? syms.stringType : syms.intType);
selector = translate(selector, target);
boolean boxedSwitch = !enumSwitch && !stringSwitch && !selector.type.isPrimitive();
selector = translate(selector, selector.type);
cases = translateCases(cases);
if (tree.hasTag(SWITCH)) {
((JCSwitch) tree).selector = selector;
@ -3659,6 +3665,10 @@ public class Lower extends TreeTranslator {
result = visitEnumSwitch(tree, selector, cases);
} else if (stringSwitch) {
result = visitStringSwitch(tree, selector, cases);
} else if (boxedSwitch) {
//An switch over boxed primitive. Pattern matching switches are already translated
//by TransPatterns, so all non-primitive types are only boxed primitives:
result = visitBoxedPrimitiveSwitch(tree, selector, cases);
} else {
result = tree;
}
@ -3672,14 +3682,42 @@ public class Lower extends TreeTranslator {
names.ordinal,
selector.type,
List.nil());
JCArrayAccess newSelector = make.Indexed(map.mapVar,
make.App(make.Select(selector,
ordinalMethod)));
JCExpression newSelector;
if (cases.stream().anyMatch(c -> TreeInfo.isNull(c.labels.head))) {
//for enum switches with case null, do:
//switch ($selector != null ? $mapVar[$selector.ordinal()] : -1) {...}
//replacing case null with case -1:
VarSymbol dollar_s = new VarSymbol(FINAL|SYNTHETIC,
names.fromString("s" + tree.pos + this.target.syntheticNameChar()),
selector.type,
currentMethodSym);
JCStatement var = make.at(tree.pos()).VarDef(dollar_s, selector).setType(dollar_s.type);
newSelector = make.Indexed(map.mapVar,
make.App(make.Select(make.Ident(dollar_s),
ordinalMethod)));
newSelector =
make.LetExpr(List.of(var),
make.Conditional(makeBinary(NE, make.Ident(dollar_s), makeNull()),
newSelector,
makeLit(syms.intType, -1))
.setType(newSelector.type))
.setType(newSelector.type);
} else {
newSelector = make.Indexed(map.mapVar,
make.App(make.Select(selector,
ordinalMethod)));
}
ListBuffer<JCCase> newCases = new ListBuffer<>();
for (JCCase c : cases) {
if (c.pats.nonEmpty()) {
VarSymbol label = (VarSymbol)TreeInfo.symbol(c.pats.head);
JCLiteral pat = map.forConstant(label);
if (c.labels.head.isExpression()) {
JCExpression pat;
if (TreeInfo.isNull(c.labels.head)) {
pat = makeLit(syms.intType, -1);
} else {
VarSymbol label = (VarSymbol)TreeInfo.symbol((JCExpression) c.labels.head);
pat = map.forConstant(label);
}
newCases.append(make.Case(JCCase.STATEMENT, List.of(pat), c.stats, null));
} else {
newCases.append(c);
@ -3756,23 +3794,30 @@ public class Lower extends TreeTranslator {
Map<Integer, Set<String>> hashToString = new LinkedHashMap<>(alternatives + 1, 1.0f);
int casePosition = 0;
JCCase nullCase = null;
int nullCaseLabel = -1;
for(JCCase oneCase : caseList) {
if (oneCase.pats.nonEmpty()) { // pats is empty for a "default" case
JCExpression expression = oneCase.pats.head;
String labelExpr = (String) expression.type.constValue();
Integer mapping = caseLabelToPosition.put(labelExpr, casePosition);
Assert.checkNull(mapping);
int hashCode = labelExpr.hashCode();
Set<String> stringSet = hashToString.get(hashCode);
if (stringSet == null) {
stringSet = new LinkedHashSet<>(1, 1.0f);
stringSet.add(labelExpr);
hashToString.put(hashCode, stringSet);
if (oneCase.labels.head.isExpression()) {
if (TreeInfo.isNull(oneCase.labels.head)) {
nullCase = oneCase;
nullCaseLabel = casePosition;
} else {
boolean added = stringSet.add(labelExpr);
Assert.check(added);
JCExpression expression = (JCExpression) oneCase.labels.head;
String labelExpr = (String) expression.type.constValue();
Integer mapping = caseLabelToPosition.put(labelExpr, casePosition);
Assert.checkNull(mapping);
int hashCode = labelExpr.hashCode();
Set<String> stringSet = hashToString.get(hashCode);
if (stringSet == null) {
stringSet = new LinkedHashSet<>(1, 1.0f);
stringSet.add(labelExpr);
hashToString.put(hashCode, stringSet);
} else {
boolean added = stringSet.add(labelExpr);
Assert.check(added);
}
}
}
casePosition++;
@ -3847,7 +3892,14 @@ public class Lower extends TreeTranslator {
}
switch1.cases = caseBuffer.toList();
stmtList.append(switch1);
if (nullCase != null) {
stmtList.append(make.If(makeBinary(NE, make.Ident(dollar_s), makeNull()), switch1, make.Exec(make.Assign(make.Ident(dollar_tmp),
make.Literal(nullCaseLabel)).
setType(dollar_tmp.type))).setType(syms.intType));
} else {
stmtList.append(switch1);
}
// Make isomorphic switch tree replacing string labels
// with corresponding integer ones from the label to
@ -3855,16 +3907,18 @@ public class Lower extends TreeTranslator {
ListBuffer<JCCase> lb = new ListBuffer<>();
for(JCCase oneCase : caseList ) {
boolean isDefault = (oneCase.pats.isEmpty());
JCExpression caseExpr;
boolean isDefault = !oneCase.labels.head.isExpression();
JCCaseLabel caseExpr;
if (isDefault)
caseExpr = null;
else {
caseExpr = make.Literal(caseLabelToPosition.get((String)TreeInfo.skipParens(oneCase.pats.head).
else if (oneCase == nullCase) {
caseExpr = make.Literal(nullCaseLabel);
} else {
caseExpr = make.Literal(caseLabelToPosition.get((String)TreeInfo.skipParens((JCExpression) oneCase.labels.head).
type.constValue()));
}
lb.append(make.Case(JCCase.STATEMENT, caseExpr == null ? List.nil() : List.of(caseExpr),
lb.append(make.Case(JCCase.STATEMENT, caseExpr == null ? List.of(make.DefaultCaseLabel()) : List.of(caseExpr),
oneCase.stats, null));
}
@ -3898,6 +3952,70 @@ public class Lower extends TreeTranslator {
}
}
private JCTree visitBoxedPrimitiveSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
JCExpression newSelector;
if (cases.stream().anyMatch(c -> TreeInfo.isNull(c.labels.head))) {
//a switch over a boxed primitive, with a null case. Pick two constants that are
//not used by any branch in the case (c1 and c2), close to other constants that are
//used in the switch. Then do:
//switch ($selector != null ? $selector != c1 ? $selector : c2 : c1) {...}
//replacing case null with case c1
Set<Integer> constants = new LinkedHashSet<>();
JCCase nullCase = null;
for (JCCase c : cases) {
if (TreeInfo.isNull(c.labels.head)) {
nullCase = c;
} else if (!c.labels.head.hasTag(DEFAULTCASELABEL)) {
constants.add((int) c.labels.head.type.constValue());
}
}
Assert.checkNonNull(nullCase);
int nullValue = constants.isEmpty() ? 0 : constants.iterator().next();
while (constants.contains(nullValue)) nullValue++;
constants.add(nullValue);
nullCase.labels.head = makeLit(syms.intType, nullValue);
int replacementValue = nullValue;
while (constants.contains(replacementValue)) replacementValue++;
VarSymbol dollar_s = new VarSymbol(FINAL|SYNTHETIC,
names.fromString("s" + tree.pos + this.target.syntheticNameChar()),
selector.type,
currentMethodSym);
JCStatement var = make.at(tree.pos()).VarDef(dollar_s, selector).setType(dollar_s.type);
JCExpression nullValueReplacement =
make.Conditional(makeBinary(NE,
unbox(make.Ident(dollar_s), syms.intType),
makeLit(syms.intType, nullValue)),
unbox(make.Ident(dollar_s), syms.intType),
makeLit(syms.intType, replacementValue))
.setType(syms.intType);
JCExpression nullCheck =
make.Conditional(makeBinary(NE, make.Ident(dollar_s), makeNull()),
nullValueReplacement,
makeLit(syms.intType, nullValue))
.setType(syms.intType);
newSelector = make.LetExpr(List.of(var), nullCheck).setType(syms.intType);
} else {
newSelector = unbox(selector, syms.intType);
}
if (tree.hasTag(SWITCH)) {
((JCSwitch) tree).selector = newSelector;
} else {
((JCSwitchExpression) tree).selector = newSelector;
}
return tree;
}
@Override
public void visitBreak(JCBreak tree) {
result = tree;

View File

@ -29,6 +29,7 @@ import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.BindingSymbol;
import com.sun.tools.javac.resources.CompilerProperties.Errors;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCGuardPattern;
import com.sun.tools.javac.tree.JCTree.Tag;
import com.sun.tools.javac.tree.TreeScanner;
import com.sun.tools.javac.util.Context;
@ -96,13 +97,7 @@ public class MatchBindingsComputer extends TreeScanner {
public MatchBindings binary(JCTree tree, MatchBindings lhsBindings, MatchBindings rhsBindings) {
switch (tree.getTag()) {
case AND: {
// e.T = union(x.T, y.T)
// e.F = intersection(x.F, y.F) (error recovery)
List<BindingSymbol> bindingsWhenTrue =
union(tree.pos(), lhsBindings.bindingsWhenTrue, rhsBindings.bindingsWhenTrue);
List<BindingSymbol> bindingsWhenFalse = //error recovery
intersection(tree.pos(), lhsBindings.bindingsWhenFalse, rhsBindings.bindingsWhenFalse);
return new MatchBindings(bindingsWhenTrue, bindingsWhenFalse);
return andOperation(tree.pos(), lhsBindings, rhsBindings);
}
case OR: {
// e.T = intersection(x.T, y.T) (error recovery)
@ -117,9 +112,40 @@ public class MatchBindingsComputer extends TreeScanner {
return EMPTY;
}
public MatchBindings guardedPattern(JCGuardPattern tree, MatchBindings patternBindings, MatchBindings guardBindings) {
return andOperation(tree.pos(), patternBindings, guardBindings);
}
public MatchBindings andOperation(DiagnosticPosition pos, MatchBindings lhsBindings, MatchBindings rhsBindings) {
// e.T = union(x.T, y.T)
// e.F = intersection(x.F, y.F) (error recovery)
List<BindingSymbol> bindingsWhenTrue =
union(pos, lhsBindings.bindingsWhenTrue, rhsBindings.bindingsWhenTrue);
List<BindingSymbol> bindingsWhenFalse = //error recovery
intersection(pos, lhsBindings.bindingsWhenFalse, rhsBindings.bindingsWhenFalse);
return new MatchBindings(bindingsWhenTrue, bindingsWhenFalse);
}
public MatchBindings switchCase(JCTree tree, MatchBindings prevBindings, MatchBindings currentBindings) {
if (prevBindings == null)
return currentBindings;
if (!prevBindings.bindingsWhenTrue.isEmpty() && !currentBindings.bindingsWhenTrue.isEmpty()) {
log.error(tree.pos(), Errors.FlowsThroughToPattern);
}
if (prevBindings.nullPattern) {
return currentBindings;
}
if (currentBindings.nullPattern) {
return prevBindings;
}
return new MatchBindings(intersection(tree.pos(), prevBindings.bindingsWhenTrue, currentBindings.bindingsWhenTrue),
intersection(tree.pos(), prevBindings.bindingsWhenFalse, currentBindings.bindingsWhenFalse));
}
public MatchBindings finishBindings(JCTree tree, MatchBindings matchBindings) {
switch (tree.getTag()) {
case NOT: case AND: case OR: case BINDINGPATTERN:
case PARENTHESIZEDPATTERN: case GUARDPATTERN:
case PARENS: case TYPETEST:
case CONDEXPR: //error recovery:
return matchBindings;
@ -132,10 +158,16 @@ public class MatchBindingsComputer extends TreeScanner {
public final List<BindingSymbol> bindingsWhenTrue;
public final List<BindingSymbol> bindingsWhenFalse;
public final boolean nullPattern;
public MatchBindings(List<BindingSymbol> bindingsWhenTrue, List<BindingSymbol> bindingsWhenFalse) {
this(bindingsWhenTrue, bindingsWhenFalse, false);
}
public MatchBindings(List<BindingSymbol> bindingsWhenTrue, List<BindingSymbol> bindingsWhenFalse, boolean nullPattern) {
this.bindingsWhenTrue = bindingsWhenTrue;
this.bindingsWhenFalse = bindingsWhenFalse;
this.nullPattern = nullPattern;
}
}

View File

@ -25,11 +25,16 @@
package com.sun.tools.javac.comp;
import com.sun.source.tree.CaseTree;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Kinds;
import com.sun.tools.javac.code.Kinds.Kind;
import com.sun.tools.javac.code.Preview;
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.VarSymbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
@ -44,6 +49,7 @@ import com.sun.tools.javac.tree.JCTree.JCIf;
import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
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;
@ -52,24 +58,40 @@ import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Names;
import com.sun.tools.javac.util.Options;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Type.ClassType;
import com.sun.tools.javac.code.Type.MethodType;
import com.sun.tools.javac.code.Type.WildcardType;
import com.sun.tools.javac.code.TypeTag;
import static com.sun.tools.javac.code.TypeTag.BOT;
import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant;
import com.sun.tools.javac.jvm.Target;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCBreak;
import com.sun.tools.javac.tree.JCTree.JCCase;
import com.sun.tools.javac.tree.JCTree.JCCaseLabel;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCContinue;
import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCGuardPattern;
import com.sun.tools.javac.tree.JCTree.JCLambda;
import com.sun.tools.javac.tree.JCTree.JCParenthesizedPattern;
import com.sun.tools.javac.tree.JCTree.JCPattern;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCSwitchExpression;
import com.sun.tools.javac.tree.JCTree.LetExpr;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.List;
/**
@ -87,13 +109,15 @@ public class TransPatterns extends TreeTranslator {
}
private final Symtab syms;
private final Attr attr;
private final Resolve rs;
private final Types types;
private final Operators operators;
private final Log log;
private final ConstFold constFold;
private final Names names;
private final Target target;
private final Preview preview;
private TreeMaker make;
private Env<AttrContext> env;
BindingContext bindingContext = new BindingContext() {
@Override
@ -106,6 +130,11 @@ public class TransPatterns extends TreeTranslator {
return null;
}
@Override
List<JCStatement> bindingVars(int diagPos) {
return List.nil();
}
@Override
JCStatement decorateStatement(JCStatement stat) {
return stat;
@ -132,68 +161,399 @@ public class TransPatterns extends TreeTranslator {
boolean debugTransPatterns;
private ClassSymbol currentClass = null;
private MethodSymbol currentMethodSym = null;
private VarSymbol currentValue = null;
protected TransPatterns(Context context) {
context.put(transPatternsKey, this);
syms = Symtab.instance(context);
attr = Attr.instance(context);
rs = Resolve.instance(context);
make = TreeMaker.instance(context);
types = Types.instance(context);
operators = Operators.instance(context);
log = Log.instance(context);
constFold = ConstFold.instance(context);
names = Names.instance(context);
target = Target.instance(context);
preview = Preview.instance(context);
debugTransPatterns = Options.instance(context).isSet("debug.patterns");
}
@Override
public void visitTypeTest(JCInstanceOf tree) {
if (tree.pattern.hasTag(Tag.BINDINGPATTERN)) {
//E instanceof T N
if (tree.pattern instanceof JCPattern) {
//E instanceof $pattern
//=>
//(let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))
//(let T' N$temp = E; N$temp instanceof typeof($pattern) && <desugared $pattern>)
//note the pattern desugaring performs binding variable assignments
Symbol exprSym = TreeInfo.symbol(tree.expr);
JCBindingPattern patt = (JCBindingPattern)tree.pattern;
VarSymbol pattSym = patt.var.sym;
Type tempType = tree.expr.type.hasTag(BOT) ?
syms.objectType
: tree.expr.type;
VarSymbol temp;
if (exprSym != null &&
exprSym.kind == Kind.VAR &&
exprSym.owner.kind.matches(Kinds.KindSelector.VAL_MTH)) {
temp = (VarSymbol) exprSym;
} else {
temp = new VarSymbol(pattSym.flags() | Flags.SYNTHETIC,
names.fromString(pattSym.name.toString() + target.syntheticNameChar() + "temp"),
tempType,
patt.var.sym.owner);
}
JCExpression translatedExpr = translate(tree.expr);
Type castTargetType = types.boxedTypeOrType(pattSym.erasure(types));
VarSymbol prevCurrentValue = currentValue;
try {
if (exprSym != null &&
exprSym.kind == Kind.VAR &&
exprSym.owner.kind.matches(Kinds.KindSelector.VAL_MTH)) {
currentValue = (VarSymbol) exprSym;
} else {
currentValue = new VarSymbol(Flags.FINAL | Flags.SYNTHETIC,
names.fromString("patt" + tree.pos + target.syntheticNameChar() + "temp"),
tempType,
currentMethodSym);
}
result = makeTypeTest(make.Ident(temp), make.Type(castTargetType));
VarSymbol bindingVar = bindingContext.bindingDeclared((BindingSymbol) patt.var.sym);
if (bindingVar != null) { //TODO: cannot be null here?
JCAssign fakeInit = (JCAssign)make.at(tree.pos).Assign(
make.Ident(bindingVar), convert(make.Ident(temp), castTargetType)).setType(bindingVar.erasure(types));
LetExpr nestedLE = make.LetExpr(List.of(make.Exec(fakeInit)),
make.Literal(true));
nestedLE.needsCond = true;
nestedLE.setType(syms.booleanType);
result = makeBinary(Tag.AND, (JCExpression)result, nestedLE);
}
if (temp != exprSym) {
result = make.at(tree.pos).LetExpr(make.VarDef(temp, translatedExpr), (JCExpression)result).setType(syms.booleanType);
((LetExpr) result).needsCond = true;
JCExpression translatedExpr = translate(tree.expr);
Type principalType = principalType((JCPattern) tree.pattern);
result = makeBinary(Tag.AND,
makeTypeTest(make.Ident(currentValue), make.Type(principalType)),
(JCExpression) this.<JCTree>translate(tree.pattern));
if (currentValue != exprSym) {
result = make.at(tree.pos).LetExpr(make.VarDef(currentValue, translatedExpr),
(JCExpression)result).setType(syms.booleanType);
((LetExpr) result).needsCond = true;
}
} finally {
currentValue = prevCurrentValue;
}
} else {
super.visitTypeTest(tree);
}
}
@Override
public void visitBindingPattern(JCBindingPattern tree) {
//it is assumed the primary type has already been checked:
BindingSymbol binding = (BindingSymbol) tree.var.sym;
Type castTargetType = principalType(tree);
VarSymbol bindingVar = bindingContext.bindingDeclared(binding);
if (bindingVar != null) {
JCAssign fakeInit = (JCAssign)make.at(TreeInfo.getStartPos(tree)).Assign(
make.Ident(bindingVar), convert(make.Ident(currentValue), castTargetType)).setType(bindingVar.erasure(types));
LetExpr nestedLE = make.LetExpr(List.of(make.Exec(fakeInit)),
make.Literal(true));
nestedLE.needsCond = true;
nestedLE.setType(syms.booleanType);
result = nestedLE;
} else {
result = make.Literal(true);
}
}
@Override
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
result = translate(tree.pattern);
}
@Override
public void visitGuardPattern(JCGuardPattern tree) {
JCExpression pattern = (JCExpression) this.<JCTree>translate(tree.patt);
JCExpression guard = translate(tree.expr);
result = makeBinary(Tag.AND, pattern, guard);
}
@Override
public void visitSwitch(JCSwitch tree) {
handleSwitch(tree, tree.selector, tree.cases, tree.hasTotalPattern, tree.patternSwitch);
}
@Override
public void visitSwitchExpression(JCSwitchExpression tree) {
handleSwitch(tree, tree.selector, tree.cases, tree.hasTotalPattern, tree.patternSwitch);
}
private void handleSwitch(JCTree tree,
JCExpression selector,
List<JCCase> cases,
boolean hasTotalPattern,
boolean patternSwitch) {
Type seltype = selector.type;
boolean enumSwitch = (seltype.tsym.flags() & Flags.ENUM) != 0;
if (patternSwitch) {
Assert.check(preview.isEnabled());
Assert.check(preview.usesPreview(env.toplevel.sourcefile));
//rewrite pattern matching switches:
//switch ($obj) {
// case $constant: $stats$
// case $pattern1: $stats$
// case $pattern2, null: $stats$
// case $pattern3: $stats$
//}
//=>
//int $idx = 0;
//$RESTART: switch (invokeDynamic typeSwitch($constant, typeof($pattern1), typeof($pattern2), typeof($pattern3))($obj, $idx)) {
// case 0:
// if (!(<desugared $pattern1>)) { $idx = 1; continue $RESTART; }
// $stats$
// case 1:
// if (!(<desugared $pattern1>)) { $idx = 2; continue $RESTART; }
// $stats$
// case 2, -1:
// if (!(<desugared $pattern1>)) { $idx = 3; continue $RESTART; }
// $stats$
// case 3:
// if (!(<desugared $pattern1>)) { $idx = 4; continue $RESTART; }
// $stats$
//}
//notes:
//-pattern desugaring performs assignment to the binding variables
//-the selector is evaluated only once and stored in a temporary variable
//-typeSwitch bootstrap method can restart matching at specified index. The bootstrap will
// categorize the input, and return the case index whose type or constant matches the input.
// The bootstrap does not evaluate guards, which are injected at the beginning of the case's
// statement list, and if the guard fails, the switch is "continued" and matching is
// restarted from the next index.
//-case null is always desugared to case -1, as the typeSwitch bootstrap method will
// return -1 when the input is null
//
//a special case for switches over enums with pattern case
//with only a single unguarded (type) pattern case, which is equivalent
//to a default with additional binding variable assignment:
//switch ($enum) {
// case $constant1: $stats$
// case $constant2: $stats$
// case typeof($enum) e: $stats$
//}
//=>
//switch ($enum) {
// case $constant1: $stats$
// case $constant2: $stats$
// default: typeof($enum) e = $enum; $stats$
//}
//constant labels in switches over enums with one or more pattern cases
//with guards are desugared into guards:
//case $constant1: $stats$
//=>
//case typeof($enum) e && e == $constant1: $stats$
//and handled as a normal pattern matching switch
//
//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) {
if (c.head.stats.isEmpty() && c.tail.nonEmpty()) {
c.tail.head.labels = c.tail.head.labels.prependList(c.head.labels);
} else {
newCases.add(c.head);
}
}
if (enumSwitch && hasGuards(newCases)) {
for (JCCase c : newCases) {
for (List<JCCaseLabel> l = c.labels; l.nonEmpty(); l = l.tail) {
if (l.head.isExpression() && !TreeInfo.isNull(l.head)) {
BindingSymbol temp = new BindingSymbol(Flags.SYNTHETIC,
names.fromString("enumGuard" + c.pos +
target.syntheticNameChar() + "temp"),
seltype,
currentMethodSym);
JCBindingPattern binding =
make.at(l.head.pos()).BindingPattern(make.VarDef(temp, null));
binding.setType(seltype);
l.head = make.GuardPattern(binding,
makeBinary(Tag.EQ,
make.Ident(temp),
(JCExpression) l.head));
}
}
}
enumSwitch = false;
}
cases = newCases.toList();
ListBuffer<JCStatement> statements = new ListBuffer<>();
VarSymbol temp = new VarSymbol(Flags.SYNTHETIC,
names.fromString("selector" + tree.pos + target.syntheticNameChar() + "temp"),
seltype,
currentMethodSym);
boolean hasNullCase = cases.stream()
.flatMap(c -> c.labels.stream())
.anyMatch(p -> p.isExpression() &&
TreeInfo.isNull((JCExpression) p));
JCCase lastCase = cases.last();
if (hasTotalPattern && !hasNullCase) {
JCCase last = lastCase;
if (last.labels.stream().noneMatch(l -> l.hasTag(Tag.DEFAULTCASELABEL))) {
last.labels = last.labels.prepend(makeLit(syms.botType, null));
hasNullCase = true;
}
}
statements.append(make.at(tree.pos).VarDef(temp, !hasNullCase ? attr.makeNullCheck(selector)
: selector));
VarSymbol index = new VarSymbol(Flags.SYNTHETIC,
names.fromString(tree.pos + target.syntheticNameChar() + "index"),
syms.intType,
currentMethodSym);
statements.append(make.at(tree.pos).VarDef(index, makeLit(syms.intType, 0)));
if (enumSwitch) {
selector = make.Ident(temp);
} else {
List<Type> staticArgTypes = List.of(syms.methodHandleLookupType,
syms.stringType,
syms.methodTypeType,
types.makeArrayType(new ClassType(syms.classType.getEnclosingType(),
List.of(new WildcardType(syms.objectType, BoundKind.UNBOUND,
syms.boundClass)),
syms.classType.tsym)));
LoadableConstant[] staticArgValues =
cases.stream()
.flatMap(c -> c.labels.stream())
.map(l -> toLoadableConstant(l))
.filter(c -> c != null)
.toArray(s -> new LoadableConstant[s]);
Symbol bsm = rs.resolveInternalMethod(tree.pos(), env, syms.switchBootstrapsType,
names.fromString("typeSwitch"), staticArgTypes, List.nil());
MethodType indyType = new MethodType(
List.of(syms.objectType, syms.intType),
syms.intType,
List.nil(),
syms.methodClass
);
DynamicMethodSymbol dynSym = new DynamicMethodSymbol(names.fromString("typeSwitch"),
syms.noSymbol,
((MethodSymbol)bsm).asHandle(),
indyType,
staticArgValues);
JCFieldAccess qualifier = make.Select(make.QualIdent(bsm.owner), dynSym.name);
qualifier.sym = dynSym;
qualifier.type = syms.intType;
selector = make.Apply(List.nil(),
qualifier,
List.of(make.Ident(temp), make.Ident(index)))
.setType(syms.intType);
}
int i = 0;
boolean previousCompletesNormally = false;
for (var c : cases) {
List<JCCaseLabel> clearedPatterns = c.labels;
boolean hasJoinedNull =
c.labels.size() > 1 && c.labels.stream().anyMatch(l -> l.isNullPattern());
if (hasJoinedNull) {
clearedPatterns = c.labels.stream()
.filter(l -> !l.isNullPattern())
.collect(List.collector());
}
if (clearedPatterns.size() == 1 && clearedPatterns.head.isPattern() && !previousCompletesNormally) {
JCCaseLabel p = clearedPatterns.head;
bindingContext = new BasicBindingContext();
VarSymbol prevCurrentValue = currentValue;
try {
currentValue = temp;
JCExpression test = (JCExpression) this.<JCTree>translate(p);
c.stats = translate(c.stats);
JCContinue continueSwitch = make.at(clearedPatterns.head.pos()).Continue(null);
continueSwitch.target = tree;
c.stats = c.stats.prepend(make.If(makeUnary(Tag.NOT, test).setType(syms.booleanType),
make.Block(0, List.of(make.Exec(make.Assign(make.Ident(index),
makeLit(syms.intType, i + 1))
.setType(syms.intType)),
continueSwitch)),
null));
c.stats = c.stats.prependList(bindingContext.bindingVars(c.pos));
} finally {
currentValue = prevCurrentValue;
bindingContext.pop();
}
}
if (enumSwitch) {
var labels = c.labels;
while (labels.nonEmpty()) {
if (labels.head.isPattern()) {
labels.head = make.DefaultCaseLabel();
}
labels = labels.tail;
}
} else {
ListBuffer<JCCaseLabel> translatedLabels = new ListBuffer<>();
for (var p : c.labels) {
if (p.hasTag(Tag.DEFAULTCASELABEL)) {
translatedLabels.add(p);
} else if (hasTotalPattern && c == lastCase && p.isPattern()) {
//If the switch has total pattern, the last case will contain it.
//Convert the total pattern to default:
translatedLabels.add(make.DefaultCaseLabel());
} else {
int value;
if (p.isNullPattern()) {
value = -1;
} else {
value = i++;
}
translatedLabels.add(make.Literal(value));
}
}
c.labels = translatedLabels.toList();
}
if (c.caseKind == CaseTree.CaseKind.STATEMENT) {
previousCompletesNormally = c.completesNormally;
} else {
previousCompletesNormally = false;
JCBreak brk = make.at(TreeInfo.endPos(c.stats.last())).Break(null);
brk.target = tree;
c.stats = c.stats.append(brk);
}
}
if (tree.hasTag(Tag.SWITCH)) {
((JCSwitch) tree).selector = selector;
((JCSwitch) tree).cases = cases;
statements.append((JCSwitch) tree);
result = make.Block(0, statements.toList());
} else {
((JCSwitchExpression) tree).selector = selector;
((JCSwitchExpression) tree).cases = cases;
LetExpr r = (LetExpr) make.LetExpr(statements.toList(), (JCSwitchExpression) tree)
.setType(tree.type);
r.needsCond = true;
result = r;
}
return ;
}
if (tree.hasTag(Tag.SWITCH)) {
super.visitSwitch((JCSwitch) tree);
} else {
super.visitSwitchExpression((JCSwitchExpression) tree);
}
}
private boolean hasGuards(Collection<JCCase> cases) {
return cases.stream()
.flatMap(c -> c.labels.stream())
.filter(JCCaseLabel::isPattern)
.anyMatch(l -> !TreeInfo.primaryPatternType((JCPattern) l).unconditional());
}
private Type principalType(JCPattern p) {
return types.boxedTypeOrType(types.erasure(TreeInfo.primaryPatternType(p).type()));
}
private LoadableConstant toLoadableConstant(JCCaseLabel l) {
if (l.isPattern()) {
return (LoadableConstant) principalType((JCPattern) l);
} else if (l.isExpression() && !TreeInfo.isNull((JCExpression) l)) {
Assert.checkNonNull(l.type.constValue());
return switch (l.type.getTag()) {
case BYTE, CHAR,
SHORT, INT -> LoadableConstant.Int((Integer) l.type.constValue());
case CLASS -> LoadableConstant.String((String) l.type.constValue());
default -> throw new AssertionError();
};
} else {
return null;
}
}
@Override
public void visitBinary(JCBinary tree) {
bindingContext = new BasicBindingContext();
@ -308,7 +668,15 @@ public class TransPatterns extends TreeTranslator {
return true;
}
};
MethodSymbol oldMethodSym = currentMethodSym;
try {
if (currentMethodSym == null) {
// Block is a static or instance initializer.
currentMethodSym =
new MethodSymbol(tree.flags | Flags.BLOCK,
names.empty, null,
currentClass);
}
for (List<JCStatement> l = tree.stats; l.nonEmpty(); l = l.tail) {
statements.append(translate(l.head));
}
@ -316,6 +684,7 @@ public class TransPatterns extends TreeTranslator {
tree.stats = statements.toList();
result = tree;
} finally {
currentMethodSym = oldMethodSym;
bindingContext.pop();
}
}
@ -331,13 +700,45 @@ public class TransPatterns extends TreeTranslator {
}
}
@Override
public void visitClassDef(JCClassDecl tree) {
ClassSymbol prevCurrentClass = currentClass;
try {
currentClass = tree.sym;
super.visitClassDef(tree);
} finally {
currentClass = prevCurrentClass;
}
}
public void visitVarDef(JCVariableDecl tree) {
MethodSymbol prevMethodSym = currentMethodSym;
try {
tree.mods = translate(tree.mods);
tree.vartype = translate(tree.vartype);
if (currentMethodSym == null) {
// A class or instance field initializer.
currentMethodSym =
new MethodSymbol((tree.mods.flags&Flags.STATIC) | Flags.BLOCK,
names.empty, null,
currentClass);
}
if (tree.init != null) tree.init = translate(tree.init);
result = tree;
} finally {
currentMethodSym = prevMethodSym;
}
}
public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
try {
this.make = make;
this.env = env;
translate(cdef);
} finally {
// note that recursive invocations of this method fail hard
this.make = null;
this.env = null;
}
return cdef;
@ -366,6 +767,17 @@ public class TransPatterns extends TreeTranslator {
return tree;
}
/** Make an attributed unary expression.
* @param optag The operators tree tag.
* @param arg The operator's argument.
*/
JCTree.JCUnary makeUnary(JCTree.Tag optag, JCExpression arg) {
JCTree.JCUnary tree = make.Unary(optag, arg);
tree.operator = operators.resolveUnary(tree, optag, arg.type);
tree.type = tree.operator.type.getReturnType();
return tree;
}
JCExpression convert(JCExpression expr, Type target) {
JCExpression result = make.at(expr.pos()).TypeCast(make.Type(target), expr);
result.type = target;
@ -375,6 +787,7 @@ public class TransPatterns extends TreeTranslator {
abstract class BindingContext {
abstract VarSymbol bindingDeclared(BindingSymbol varSymbol);
abstract VarSymbol getBindingFor(BindingSymbol varSymbol);
abstract List<JCStatement> bindingVars(int diagPos);
abstract JCStatement decorateStatement(JCStatement stat);
abstract JCExpression decorateExpression(JCExpression expr);
abstract BindingContext pop();
@ -413,9 +826,22 @@ public class TransPatterns extends TreeTranslator {
.map(e -> e.getValue()).orElse(null);
}
@Override
List<JCStatement> bindingVars(int diagPos) {
if (hoistedVarMap.isEmpty()) return List.nil();
ListBuffer<JCStatement> stats = new ListBuffer<>();
for (Entry<BindingSymbol, VarSymbol> e : hoistedVarMap.entrySet()) {
JCVariableDecl decl = makeHoistedVarDecl(diagPos, e.getValue());
if (!e.getKey().isPreserved() ||
!parent.tryPrepend(e.getKey(), decl)) {
stats.add(decl);
}
}
return stats.toList();
}
@Override
JCStatement decorateStatement(JCStatement stat) {
if (hoistedVarMap.isEmpty()) return stat;
//if (E instanceof T N) {
// //use N
//}
@ -426,17 +852,9 @@ public class TransPatterns extends TreeTranslator {
// //use N
// }
//}
ListBuffer<JCStatement> stats = new ListBuffer<>();
for (Entry<BindingSymbol, VarSymbol> e : hoistedVarMap.entrySet()) {
JCVariableDecl decl = makeHoistedVarDecl(stat.pos, e.getValue());
if (!e.getKey().isPreserved() ||
!parent.tryPrepend(e.getKey(), decl)) {
stats.add(decl);
}
}
List<JCStatement> stats = bindingVars(stat.pos);
if (stats.nonEmpty()) {
stats.add(stat);
stat = make.at(stat.pos).Block(0, stats.toList());
stat = make.at(stat.pos).Block(0, stats.append(stat));
}
return stat;
}
@ -475,4 +893,14 @@ public class TransPatterns extends TreeTranslator {
}
}
/** Make an attributed tree representing a literal. This will be an
* Ident node in the case of boolean literals, a Literal node in all
* other cases.
* @param type The literal's type.
* @param value The literal's value.
*/
JCExpression makeLit(Type type, Object value) {
return make.Literal(type.getTag(), value).setType(type.constType(value));
}
}

View File

@ -25,15 +25,11 @@
package com.sun.tools.javac.comp;
import java.util.*;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Attribute.TypeCompound;
import com.sun.tools.javac.code.Source.Feature;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.Type.IntersectionClassType;
import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError;
import com.sun.tools.javac.resources.CompilerProperties.Errors;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
@ -562,7 +558,7 @@ public class TransTypes extends TreeTranslator {
}
public void visitCase(JCCase tree) {
tree.pats = translate(tree.pats, null);
tree.labels = translate(tree.labels, null);
tree.stats = translate(tree.stats);
result = tree;
}
@ -583,6 +579,19 @@ 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;
}
@Override
public void visitGuardPattern(JCGuardPattern tree) {
tree.patt = translate(tree.patt, null);
tree.expr = translate(tree.expr, syms.booleanType);
result = tree;
}
public void visitSynchronized(JCSynchronized tree) {
tree.lock = translate(tree.lock, erasure(tree.lock.type));
tree.body = translate(tree.body);

View File

@ -45,6 +45,7 @@ import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCConditional;
import com.sun.tools.javac.tree.JCTree.JCContinue;
import com.sun.tools.javac.tree.JCTree.JCDefaultCaseLabel;
import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
import com.sun.tools.javac.tree.JCTree.JCErroneous;
@ -284,7 +285,12 @@ public class TreeDiffer extends TreeScanner {
@Override
public void visitCase(JCCase tree) {
JCCase that = (JCCase) parameter;
result = scan(tree.pats, that.pats) && scan(tree.stats, that.stats);
result = scan(tree.labels, that.labels) && scan(tree.stats, that.stats);
}
@Override
public void visitDefaultCaseLabel(JCDefaultCaseLabel tree) {
result = true;
}
@Override

View File

@ -322,11 +322,16 @@ implements CRTFlags {
public void visitCase(JCCase tree) {
SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
sr.mergeWith(csp(tree.pats));
sr.mergeWith(csp(tree.labels));
sr.mergeWith(csp(tree.stats));
result = sr;
}
@Override
public void visitDefaultCaseLabel(JCTree.JCDefaultCaseLabel that) {
result = null;
}
public void visitSynchronized(JCSynchronized tree) {
SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
sr.mergeWith(csp(tree.lock));

View File

@ -1190,7 +1190,7 @@ public class Gen extends JCTree.Visitor {
}
public void visitSwitch(JCSwitch tree) {
handleSwitch(tree, tree.selector, tree.cases);
handleSwitch(tree, tree.selector, tree.cases, tree.patternSwitch);
}
@Override
@ -1235,7 +1235,7 @@ public class Gen extends JCTree.Visitor {
}
int prevLetExprStart = code.setLetExprStackPos(code.state.stacksize);
try {
handleSwitch(tree, tree.selector, tree.cases);
handleSwitch(tree, tree.selector, tree.cases, tree.patternSwitch);
} finally {
code.setLetExprStackPos(prevLetExprStart);
}
@ -1265,9 +1265,11 @@ public class Gen extends JCTree.Visitor {
return hasTry[0];
}
private void handleSwitch(JCTree swtch, JCExpression selector, List<JCCase> cases) {
private void handleSwitch(JCTree swtch, JCExpression selector, List<JCCase> cases,
boolean patternSwitch) {
int limit = code.nextreg;
Assert.check(!selector.type.hasTag(CLASS));
int switchStart = patternSwitch ? code.entryPoint() : -1;
int startpcCrt = genCrt ? code.curCP() : 0;
Assert.check(code.isStatementStart());
Item sel = genExpr(selector, syms.intType);
@ -1297,9 +1299,9 @@ public class Gen extends JCTree.Visitor {
List<JCCase> l = cases;
for (int i = 0; i < labels.length; i++) {
if (l.head.pats.nonEmpty()) {
Assert.check(l.head.pats.size() == 1);
int val = ((Number)l.head.pats.head.type.constValue()).intValue();
if (l.head.labels.head.isExpression()) {
Assert.check(l.head.labels.size() == 1);
int val = ((Number)((JCExpression) l.head.labels.head).type.constValue()).intValue();
labels[i] = val;
if (val < lo) lo = val;
if (hi < val) hi = val;
@ -1371,6 +1373,12 @@ public class Gen extends JCTree.Visitor {
genStats(c.stats, switchEnv, CRT_FLOW_TARGET);
}
if (switchEnv.info.cont != null) {
Assert.check(patternSwitch);
code.resolve(switchEnv.info.cont);
code.resolve(code.branch(goto_), switchStart);
}
// Resolve all breaks.
Chain exit = switchEnv.info.exit;
if (exit != null) {

View File

@ -61,6 +61,7 @@ import static com.sun.tools.javac.tree.JCTree.Tag.*;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.ImplicitAndExplicitNotAllowed;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.VarAndExplicitNotAllowed;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.VarAndImplicitNotAllowed;
import java.util.function.BiFunction;
/**
* The parser maps a token sequence into an abstract syntax tree.
@ -758,6 +759,33 @@ public class JavacParser implements Parser {
return term(EXPR);
}
/** parses patterns.
*/
public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType, boolean inInstanceOf) {
if (token.kind == LPAREN && parsedType == null) {
int startPos = token.pos;
accept(LPAREN);
JCPattern p = parsePattern(token.pos, null, null, false);
accept(RPAREN);
return toP(F.at(startPos).ParenthesizedPattern(p));
} else {
JCPattern pattern;
JCExpression e = parsedType == null ? term(EXPR | TYPE | NOLAMBDA) : parsedType;
mods = mods != null ? mods : F.at(token.pos).Modifiers(0);
JCVariableDecl var = toP(F.at(token.pos).VarDef(mods, ident(), e, null));
pattern = toP(F.at(pos).BindingPattern(var));
if (!inInstanceOf && token.kind == AMPAMP) {
checkSourceLevel(Feature.PATTERN_SWITCH);
nextToken();
JCExpression guard = term(EXPR | NOLAMBDA);
pattern = F.at(pos).GuardPattern(pattern, guard);
}
return pattern;
}
}
/**
* parses (optional) type annotations followed by a type. If the
* annotations are present before the type and are not consumed during array
@ -936,31 +964,35 @@ public class JavacParser implements Parser {
if (token.kind == INSTANCEOF) {
int pos = token.pos;
nextToken();
int patternPos = token.pos;
JCModifiers mods = optFinal(0);
int typePos = token.pos;
JCExpression type = unannotatedType(false);
JCTree pattern;
if (token.kind == IDENTIFIER) {
checkSourceLevel(token.pos, Feature.PATTERN_MATCHING_IN_INSTANCEOF);
JCVariableDecl var = toP(F.at(token.pos).VarDef(mods, ident(), type, null));
pattern = toP(F.at(patternPos).BindingPattern(var));
if (token.kind == LPAREN) {
checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
pattern = parsePattern(token.pos, null, null, true);
} else {
checkNoMods(typePos, mods.flags & ~Flags.DEPRECATED);
if (mods.annotations.nonEmpty()) {
checkSourceLevel(mods.annotations.head.pos, Feature.TYPE_ANNOTATIONS);
List<JCAnnotation> typeAnnos =
mods.annotations
.map(decl -> {
JCAnnotation typeAnno = F.at(decl.pos)
.TypeAnnotation(decl.annotationType,
decl.args);
endPosTable.replaceTree(decl, typeAnno);
return typeAnno;
});
type = insertAnnotationsToMostInner(type, typeAnnos, false);
int patternPos = token.pos;
JCModifiers mods = optFinal(0);
int typePos = token.pos;
JCExpression type = unannotatedType(false);
if (token.kind == IDENTIFIER) {
checkSourceLevel(token.pos, Feature.PATTERN_MATCHING_IN_INSTANCEOF);
pattern = parsePattern(patternPos, mods, type, true);
} else {
checkNoMods(typePos, mods.flags & ~Flags.DEPRECATED);
if (mods.annotations.nonEmpty()) {
checkSourceLevel(mods.annotations.head.pos, Feature.TYPE_ANNOTATIONS);
List<JCAnnotation> typeAnnos =
mods.annotations
.map(decl -> {
JCAnnotation typeAnno = F.at(decl.pos)
.TypeAnnotation(decl.annotationType,
decl.args);
endPosTable.replaceTree(decl, typeAnno);
return typeAnno;
});
type = insertAnnotationsToMostInner(type, typeAnnos, false);
}
pattern = type;
}
pattern = type;
}
odStack[top] = F.at(pos).TypeTest(odStack[top], pattern);
} else {
@ -1461,14 +1493,16 @@ public class JavacParser implements Parser {
private List<JCCase> switchExpressionStatementGroup() {
ListBuffer<JCCase> caseExprs = new ListBuffer<>();
int casePos = token.pos;
ListBuffer<JCExpression> pats = new ListBuffer<>();
ListBuffer<JCCaseLabel> pats = new ListBuffer<>();
if (token.kind == DEFAULT) {
nextToken();
pats.append(toP(F.at(casePos).DefaultCaseLabel()));
} else {
accept(CASE);
while (true) {
pats.append(term(EXPR | NOLAMBDA));
JCCaseLabel label = parseCaseLabel();
pats.append(label);
if (token.kind != COMMA) break;
checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS);
nextToken();
@ -2958,9 +2992,9 @@ public class JavacParser implements Parser {
switch (token.kind) {
case CASE: {
nextToken();
ListBuffer<JCExpression> pats = new ListBuffer<>();
ListBuffer<JCCaseLabel> pats = new ListBuffer<>();
while (true) {
pats.append(term(EXPR | NOLAMBDA));
pats.append(parseCaseLabel());
if (token.kind != COMMA) break;
nextToken();
checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS);
@ -2991,6 +3025,7 @@ public class JavacParser implements Parser {
nextToken();
CaseTree.CaseKind caseKind;
JCTree body = null;
int patternPos = token.pos;
if (token.kind == ARROW) {
checkSourceLevel(Feature.SWITCH_RULE);
accept(ARROW);
@ -3006,7 +3041,8 @@ public class JavacParser implements Parser {
caseKind = JCCase.STATEMENT;
stats = blockStatements();
}
c = F.at(pos).Case(caseKind, List.nil(), stats, body);
JCCaseLabel defaultPattern = toP(F.at(patternPos).DefaultCaseLabel());
c = F.at(pos).Case(caseKind, List.of(defaultPattern), stats, body);
if (stats.isEmpty())
storeEnd(c, S.prevToken().endPos);
return cases.append(c).toList();
@ -3015,6 +3051,46 @@ public class JavacParser implements Parser {
throw new AssertionError("should not reach here");
}
private JCCaseLabel parseCaseLabel() {
int patternPos = token.pos;
JCCaseLabel label;
if (token.kind == DEFAULT) {
checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
nextToken();
label = toP(F.at(patternPos).DefaultCaseLabel());
} else {
if (token.kind == LPAREN) {
int lookahead = 0;
Token ahead;
while ((ahead = S.token(lookahead)).kind != EOF && ahead.kind != RPAREN && ahead.kind != AMPAMP) {
lookahead++;
}
Token twoBack;
boolean pattern = S.token(lookahead - 1).kind == IDENTIFIER &&
((twoBack = S.token(lookahead - 2)).kind == IDENTIFIER ||
twoBack.kind == GT || twoBack.kind == GTGT || twoBack.kind == GTGTGT);
if (pattern) {
return parsePattern(token.pos, null, null, false);
} else {
return term(EXPR | TYPE | NOLAMBDA);
}
} else {
JCModifiers mods = optFinal(0);
JCExpression e = term(EXPR | TYPE | NOLAMBDA);
if (token.kind == IDENTIFIER || mods.flags != 0 || mods.annotations.nonEmpty()) {
checkSourceLevel(token.pos, Feature.PATTERN_SWITCH);
return parsePattern(patternPos, null, e, false);
} else {
return e;
}
}
}
return label;
}
/** MoreStatementExpressions = { COMMA StatementExpression }
*/
<T extends ListBuffer<? super JCExpressionStatement>> T moreStatementExpressions(int pos,

View File

@ -351,6 +351,9 @@ compiler.misc.lambda=\
compiler.misc.inner.cls=\
an inner class
compiler.misc.guard=\
a guard
# 0: type
compiler.err.cant.deref=\
{0} cannot be dereferenced
@ -494,9 +497,28 @@ compiler.err.same.binary.name=\
compiler.err.duplicate.case.label=\
duplicate case label
compiler.err.pattern.dominated=\
this case label is dominated by a preceding case label
compiler.err.duplicate.default.label=\
duplicate default label
compiler.err.duplicate.total.pattern=\
duplicate total pattern
compiler.err.total.pattern.and.default=\
switch has both a total pattern and a default label
# 0: type, 1: type
compiler.err.constant.label.not.compatible=\
constant label of type {0} is not compatible with switch selector type {1}
compiler.err.flows.through.to.pattern=\
illegal fall-through to a pattern
compiler.err.flows.through.from.pattern=\
illegal fall-through from a pattern
compiler.err.else.without.if=\
''else'' without ''if''
@ -1357,6 +1379,9 @@ compiler.err.unreachable.stmt=\
compiler.err.not.exhaustive=\
the switch expression does not cover all possible input values
compiler.err.not.exhaustive.statement=\
the switch statement does not cover all possible input values
compiler.err.initializer.must.be.able.to.complete.normally=\
initializer must be able to complete normally
@ -2958,6 +2983,12 @@ compiler.misc.feature.records=\
compiler.misc.feature.sealed.classes=\
sealed classes
compiler.misc.feature.case.null=\
null in switch cases
compiler.misc.feature.pattern.switch=\
patterns in switch statements
compiler.warn.underscore.as.identifier=\
as of release 9, ''_'' is a keyword, and may not be used as an identifier
@ -3479,9 +3510,6 @@ compiler.err.illegal.argument.for.option=\
compiler.err.match.binding.exists=\
illegal attempt to redefine an existing match binding
compiler.err.switch.null.not.allowed=\
null label in case is not allowed
compiler.err.switch.case.unexpected.statement=\
unexpected statement in case, expected is an expression, a block or a throw statement

View File

@ -240,6 +240,9 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
/** Patterns.
*/
BINDINGPATTERN,
DEFAULTCASELABEL,
GUARDPATTERN,
PARENTHESIZEDPATTERN,
/** Indexed array expressions, of type Indexed.
*/
@ -699,7 +702,15 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
}
}
public static abstract class JCExpression extends JCTree implements ExpressionTree {
public static abstract class JCCaseLabel extends JCTree implements CaseLabelTree {
public abstract boolean isExpression();
public boolean isNullPattern() {
return isExpression() && TreeInfo.isNull((JCExpression) this);
}
public abstract boolean isPattern();
}
public static abstract class JCExpression extends JCCaseLabel implements ExpressionTree {
@Override
public JCExpression setType(Type type) {
super.setType(type);
@ -713,6 +724,16 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public boolean isPoly() { return false; }
public boolean isStandalone() { return true; }
@Override
public boolean isExpression() {
return true;
}
@Override
public boolean isPattern() {
return false;
}
}
/**
@ -1264,6 +1285,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public List<JCCase> cases;
/** Position of closing brace, optional. */
public int endpos = Position.NOPOS;
public boolean hasTotalPattern;
public boolean patternSwitch;
protected JCSwitch(JCExpression selector, List<JCCase> cases) {
this.selector = selector;
this.cases = cases;
@ -1296,16 +1319,16 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public static final CaseKind STATEMENT = CaseKind.STATEMENT;
public static final CaseKind RULE = CaseKind.RULE;
public final CaseKind caseKind;
public List<JCExpression> pats;
public List<JCCaseLabel> labels;
public List<JCStatement> stats;
public JCTree body;
public boolean completesNormally;
protected JCCase(CaseKind caseKind, List<JCExpression> pats,
protected JCCase(CaseKind caseKind, List<JCCaseLabel> labels,
List<JCStatement> stats, JCTree body) {
Assert.checkNonNull(pats);
Assert.check(pats.isEmpty() || pats.head != null);
Assert.checkNonNull(labels);
Assert.check(labels.isEmpty() || labels.head != null);
this.caseKind = caseKind;
this.pats = pats;
this.labels = labels;
this.stats = stats;
this.body = body;
}
@ -1315,9 +1338,11 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
@Override @DefinedBy(Api.COMPILER_TREE)
public Kind getKind() { return Kind.CASE; }
@Override @Deprecated @DefinedBy(Api.COMPILER_TREE)
public JCExpression getExpression() { return pats.head; }
public JCExpression getExpression() { return getExpressions().head; }
@Override @DefinedBy(Api.COMPILER_TREE)
public List<JCExpression> getExpressions() { return pats; }
public List<JCExpression> getExpressions() { return labels.stream().filter(p -> p instanceof JCExpression).map(p -> (JCExpression) p).collect(List.collector()); }
@Override @DefinedBy(Api.COMPILER_TREE)
public List<JCCaseLabel> getLabels() { return labels; }
@Override @DefinedBy(Api.COMPILER_TREE)
public List<JCStatement> getStatements() {
return caseKind == CaseKind.STATEMENT ? stats : null;
@ -1346,6 +1371,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public List<JCCase> cases;
/** Position of closing brace, optional. */
public int endpos = Position.NOPOS;
public boolean hasTotalPattern;
public boolean patternSwitch;
protected JCSwitchExpression(JCExpression selector, List<JCCase> cases) {
this.selector = selector;
this.cases = cases;
@ -2212,8 +2239,18 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
/**
* Pattern matching forms.
*/
public static abstract class JCPattern extends JCTree
public static abstract class JCPattern extends JCCaseLabel
implements PatternTree {
@Override
public boolean isExpression() {
return false;
}
@Override
public boolean isPattern() {
return true;
}
}
public static class JCBindingPattern extends JCPattern
@ -2251,6 +2288,121 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
}
}
public static class JCDefaultCaseLabel extends JCCaseLabel
implements DefaultCaseLabelTree {
protected JCDefaultCaseLabel() {
}
@Override
public void accept(Visitor v) {
v.visitDefaultCaseLabel(this);
}
@DefinedBy(Api.COMPILER_TREE)
public Kind getKind() {
return Kind.DEFAULT_CASE_LABEL;
}
@Override
@DefinedBy(Api.COMPILER_TREE)
public <R, D> R accept(TreeVisitor<R, D> v, D d) {
return v.visitDefaultCaseLabel(this, d);
}
@Override
public Tag getTag() {
return DEFAULTCASELABEL;
}
@Override
public boolean isExpression() {
return false;
}
@Override
public boolean isPattern() {
return false;
}
}
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 JCGuardPattern extends JCPattern
implements GuardedPatternTree {
public JCPattern patt;
public JCExpression expr;
public JCGuardPattern(JCPattern patt, JCExpression expr) {
this.patt = patt;
this.expr = expr;
}
@Override @DefinedBy(Api.COMPILER_TREE)
public PatternTree getPattern() {
return patt;
}
@Override @DefinedBy(Api.COMPILER_TREE)
public ExpressionTree getExpression() {
return expr;
}
@Override
public void accept(Visitor v) {
v.visitGuardPattern(this);
}
@DefinedBy(Api.COMPILER_TREE)
public Kind getKind() {
return Kind.GUARDED_PATTERN;
}
@Override
@DefinedBy(Api.COMPILER_TREE)
public <R, D> R accept(TreeVisitor<R, D> v, D d) {
return v.visitGuardedPattern(this, d);
}
@Override
public Tag getTag() {
return Tag.GUARDPATTERN;
}
}
/**
* An array selection
*/
@ -3187,7 +3339,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
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<JCExpression> pat,
JCCase Case(CaseTree.CaseKind caseKind, List<JCCaseLabel> labels,
List<JCStatement> stats, JCTree body);
JCSynchronized Synchronized(JCExpression lock, JCBlock body);
JCTry Try(JCBlock body, List<JCCatch> catchers, JCBlock finalizer);
@ -3291,6 +3443,9 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public void visitTypeCast(JCTypeCast that) { visitTree(that); }
public void visitTypeTest(JCInstanceOf that) { visitTree(that); }
public void visitBindingPattern(JCBindingPattern that) { visitTree(that); }
public void visitDefaultCaseLabel(JCDefaultCaseLabel that) { visitTree(that); }
public void visitParenthesizedPattern(JCParenthesizedPattern that) { visitTree(that); }
public void visitGuardPattern(JCGuardPattern that) { visitTree(that); }
public void visitIndexed(JCArrayAccess that) { visitTree(that); }
public void visitSelect(JCFieldAccess that) { visitTree(that); }
public void visitReference(JCMemberReference that) { visitTree(that); }

View File

@ -852,11 +852,11 @@ public class Pretty extends JCTree.Visitor {
public void visitCase(JCCase tree) {
try {
if (tree.pats.isEmpty()) {
if (tree.labels.size() == 1 && tree.labels.get(0).hasTag(DEFAULTCASELABEL)) {
print("default");
} else {
print("case ");
printExprs(tree.pats);
printExprs(tree.labels);
}
if (tree.caseKind == JCCase.STATEMENT) {
print(":");
@ -867,13 +867,26 @@ public class Pretty extends JCTree.Visitor {
align();
} else {
print(" -> ");
printStat(tree.stats.head);
if (tree.stats.size() == 1) {
printStat(tree.stats.head);
} else {
printBlock(tree.stats);
}
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public void visitDefaultCaseLabel(JCTree.JCDefaultCaseLabel that) {
try {
print("default");
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void visitSwitchExpression(JCSwitchExpression tree) {
try {
print("switch ");
@ -902,6 +915,28 @@ 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 visitGuardPattern(JCGuardPattern patt) {
try {
printExpr(patt.patt);
print(" && ");
printExpr(patt.expr);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void visitSynchronized(JCSynchronized tree) {
try {
print("synchronized ");

View File

@ -152,7 +152,7 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
@DefinedBy(Api.COMPILER_TREE)
public JCTree visitCase(CaseTree node, P p) {
JCCase t = (JCCase) node;
List<JCExpression> pats = copy(t.pats, p);
List<JCCaseLabel> labels = copy(t.labels, p);
List<JCStatement> stats = copy(t.stats, p);
JCTree body;
if (node.getCaseKind() == CaseTree.CaseKind.RULE) {
@ -161,7 +161,7 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
} else {
body = null;
}
return M.at(t.pos).Case(t.caseKind, pats, stats, body);
return M.at(t.pos).Case(t.caseKind, labels, stats, body);
}
@DefinedBy(Api.COMPILER_TREE)
@ -497,6 +497,27 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
return M.at(t.pos).BindingPattern(var);
}
@DefinedBy(Api.COMPILER_TREE)
public JCTree visitGuardedPattern(GuardedPatternTree node, P p) {
JCGuardPattern t = (JCGuardPattern) node;
JCPattern patt = copy(t.patt, p);
JCExpression expr = copy(t.expr, p);
return M.at(t.pos).GuardPattern(patt, expr);
}
@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;
return M.at(t.pos).DefaultCaseLabel();
}
@DefinedBy(Api.COMPILER_TREE)
public JCTree visitUnary(UnaryTree node, P p) {
JCUnary t = (JCUnary) node;

View File

@ -40,6 +40,7 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Kinds.Kind.*;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
import static com.sun.tools.javac.code.TypeTag.BOT;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
import static com.sun.tools.javac.tree.JCTree.Tag.BLOCK;
@ -541,6 +542,10 @@ public class TreeInfo {
JCBindingPattern node = (JCBindingPattern)tree;
return getStartPos(node.var);
}
case GUARDPATTERN: {
JCGuardPattern node = (JCGuardPattern) tree;
return getStartPos(node.patt);
}
case ERRONEOUS: {
JCErroneous node = (JCErroneous)tree;
if (node.errs != null && node.errs.nonEmpty())
@ -630,6 +635,14 @@ 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 GUARDPATTERN: {
JCGuardPattern node = (JCGuardPattern) tree;
return getEndPos(node.expr, endPosTable);
}
case ERRONEOUS: {
JCErroneous node = (JCErroneous)tree;
if (node.errs != null && node.errs.nonEmpty())
@ -1329,7 +1342,31 @@ public class TreeInfo {
public static boolean isErrorEnumSwitch(JCExpression selector, List<JCCase> cases) {
return selector.type.tsym.kind == Kinds.Kind.ERR &&
cases.stream().flatMap(c -> c.pats.stream())
cases.stream().flatMap(c -> c.labels.stream())
.allMatch(p -> p.hasTag(IDENT));
}
public static PatternPrimaryType primaryPatternType(JCPattern pat) {
return switch (pat.getTag()) {
case BINDINGPATTERN -> new PatternPrimaryType(((JCBindingPattern) pat).type, true);
case GUARDPATTERN -> {
JCGuardPattern guarded = (JCGuardPattern) pat;
PatternPrimaryType nested = primaryPatternType(guarded.patt);
boolean unconditional = nested.unconditional();
if (guarded.expr.type.hasTag(BOOLEAN) && unconditional) {
unconditional = false;
var constValue = guarded.expr.type.constValue();
if (constValue != null && ((int) constValue) == 1) {
unconditional = true;
}
}
yield new PatternPrimaryType(nested.type(), unconditional);
}
case PARENTHESIZEDPATTERN -> primaryPatternType(((JCParenthesizedPattern) pat).pattern);
default -> throw new AssertionError();
};
}
public record PatternPrimaryType(Type type, boolean unconditional) {}
}

View File

@ -291,9 +291,9 @@ public class TreeMaker implements JCTree.Factory {
return tree;
}
public JCCase Case(CaseTree.CaseKind caseKind, List<JCExpression> pats,
public JCCase Case(CaseTree.CaseKind caseKind, List<JCCaseLabel> labels,
List<JCStatement> stats, JCTree body) {
JCCase tree = new JCCase(caseKind, pats, stats, body);
JCCase tree = new JCCase(caseKind, labels, stats, body);
tree.pos = pos;
return tree;
}
@ -488,6 +488,24 @@ public class TreeMaker implements JCTree.Factory {
return tree;
}
public JCDefaultCaseLabel DefaultCaseLabel() {
JCDefaultCaseLabel tree = new JCDefaultCaseLabel();
tree.pos = pos;
return tree;
}
public JCParenthesizedPattern ParenthesizedPattern(JCPattern pattern) {
JCParenthesizedPattern tree = new JCParenthesizedPattern(pattern);
tree.pos = pos;
return tree;
}
public JCGuardPattern GuardPattern(JCPattern guardedPattern, JCExpression expr) {
JCGuardPattern tree = new JCGuardPattern(guardedPattern, expr);
tree.pos = pos;
return tree;
}
public JCArrayAccess Indexed(JCExpression indexed, JCExpression index) {
JCArrayAccess tree = new JCArrayAccess(indexed, index);
tree.pos = pos;

View File

@ -177,7 +177,7 @@ public class TreeScanner extends Visitor {
}
public void visitCase(JCCase tree) {
scan(tree.pats);
scan(tree.labels);
scan(tree.stats);
}
@ -307,6 +307,21 @@ public class TreeScanner extends Visitor {
scan(tree.var);
}
@Override
public void visitDefaultCaseLabel(JCDefaultCaseLabel tree) {
}
@Override
public void visitParenthesizedPattern(JCParenthesizedPattern that) {
scan(that.pattern);
}
@Override
public void visitGuardPattern(JCGuardPattern that) {
scan(that.patt);
scan(that.expr);
}
public void visitIndexed(JCArrayAccess tree) {
scan(tree.indexed);
scan(tree.index);

View File

@ -207,7 +207,7 @@ public class TreeTranslator extends JCTree.Visitor {
}
public void visitCase(JCCase tree) {
tree.pats = translate(tree.pats);
tree.labels = translate(tree.labels);
tree.stats = translate(tree.stats);
result = tree;
}
@ -363,6 +363,24 @@ public class TreeTranslator extends JCTree.Visitor {
result = tree;
}
@Override
public void visitDefaultCaseLabel(JCDefaultCaseLabel tree) {
result = tree;
}
@Override
public void visitParenthesizedPattern(JCParenthesizedPattern tree) {
tree.pattern = translate(tree.pattern);
result = tree;
}
@Override
public void visitGuardPattern(JCGuardPattern tree) {
tree.patt = translate(tree.patt);
tree.expr = translate(tree.expr);
result = tree;
}
public void visitIndexed(JCArrayAccess tree) {
tree.indexed = translate(tree.indexed);
tree.index = translate(tree.index);

View File

@ -3196,11 +3196,12 @@ public class Utils {
flags.add(ElementFlag.DEPRECATED);
}
if (!previewLanguageFeaturesUsed(el).isEmpty() ||
configuration.workArounds.isPreviewAPI(el) ||
!previewAPIs.previewAPI.isEmpty() ||
!previewAPIs.reflectivePreviewAPI.isEmpty() ||
!previewAPIs.declaredUsingPreviewFeature.isEmpty()) {
if ((!previewLanguageFeaturesUsed(el).isEmpty() ||
configuration.workArounds.isPreviewAPI(el) ||
!previewAPIs.previewAPI.isEmpty() ||
!previewAPIs.reflectivePreviewAPI.isEmpty() ||
!previewAPIs.declaredUsingPreviewFeature.isEmpty()) &&
!hasNoProviewAnnotation(el)) {
flags.add(ElementFlag.PREVIEW);
}
@ -3216,4 +3217,9 @@ public class Utils {
PREVIEW
}
private boolean hasNoProviewAnnotation(Element el) {
return el.getAnnotationMirrors()
.stream()
.anyMatch(am -> "jdk.internal.javac.NoPreview".equals(getQualifiedTypeName(am.getAnnotationType())));
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) 2021, 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.
*/
import java.io.Serializable;
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 org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
/**
* @test
* @compile --enable-preview -source ${jdk.version} SwitchBootstrapsTest.java
* @run testng/othervm --enable-preview SwitchBootstrapsTest
*/
@Test
public class SwitchBootstrapsTest {
public static final MethodHandle BSM_TYPE_SWITCH;
static {
try {
BSM_TYPE_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "typeSwitch",
MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class));
}
catch (ReflectiveOperationException e) {
throw new AssertionError("Should not happen", e);
}
}
private void testType(Object target, int start, int result, Object... labels) throws Throwable {
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker();
assertEquals((int) indy.invoke(target, start), result);
assertEquals(-1, (int) indy.invoke(null, start));
}
public enum E1 {
A;
}
public enum E2 {
C;
}
public void testTypes() throws Throwable {
testType("", 0, 0, String.class, Object.class);
testType("", 0, 0, Object.class);
testType("", 0, 1, Integer.class);
testType("", 0, 1, Integer.class, Serializable.class);
testType(E1.A, 0, 0, E1.class, Object.class);
testType(E2.C, 0, 1, E1.class, Object.class);
testType(new Serializable() { }, 0, 1, Comparable.class, Serializable.class);
testType("", 0, 0, "", String.class);
testType("", 1, 1, "", String.class);
testType("a", 0, 1, "", String.class);
testType(1, 0, 0, 1, Integer.class);
testType(2, 0, 1, 1, Integer.class);
testType(Byte.valueOf((byte) 1), 0, 0, 1, Integer.class);
testType(Short.valueOf((short) 1), 0, 0, 1, Integer.class);
testType(Character.valueOf((char) 1), 0, 0, 1, Integer.class);
testType(Integer.valueOf((int) 1), 0, 0, 1, Integer.class);
try {
testType(1, 0, 1, 1.0, Integer.class);
fail("Didn't get the expected exception.");
} catch (IllegalArgumentException ex) {
//OK
}
testType("", 0, 0, String.class, String.class, String.class);
testType("", 1, 1, String.class, String.class, String.class);
testType("", 2, 2, String.class, String.class, String.class);
}
public void testWrongSwitchTypes() throws Throwable {
MethodType[] switchTypes = new MethodType[] {
MethodType.methodType(int.class, Object.class),
MethodType.methodType(int.class, double.class, int.class),
MethodType.methodType(int.class, Object.class, Integer.class)
};
for (MethodType switchType : switchTypes) {
try {
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType);
fail("Didn't get the expected exception.");
} catch (IllegalArgumentException ex) {
//OK, expected
}
}
}
public void testNullLabels() throws Throwable {
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
try {
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, (Object[]) null);
fail("Didn't get the expected exception.");
} catch (NullPointerException ex) {
//OK
}
try {
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType,
new Object[] {1, null, String.class});
fail("Didn't get the expected exception.");
} catch (IllegalArgumentException ex) {
//OK
}
}
}

View File

@ -65,7 +65,7 @@ public class LineNumberTestBase extends TestBase {
try {
writeToFileIfEnabled(Paths.get(testCase.getName() + ".java"), testCase.src);
Set<Integer> coveredLines = new HashSet<>();
for (JavaFileObject file : compile(testCase.src).getClasses().values()) {
for (JavaFileObject file : compile(testCase.extraCompilerOptions, testCase.src).getClasses().values()) {
ClassFile classFile = ClassFile.read(file.openInputStream());
for (Method m : classFile.methods) {
Code_attribute code_attribute = (Code_attribute) m.attributes.get(Code);
@ -84,10 +84,17 @@ public class LineNumberTestBase extends TestBase {
.collect(toList()));
}
}
assertTrue(coveredLines.containsAll(testCase.expectedLines),
format("All significant lines are not covered.%n" +
"Covered: %s%n" +
"Expected: %s%n", coveredLines, testCase.expectedLines));
if (testCase.exactLines) {
assertTrue(coveredLines.equals(testCase.expectedLines),
format("Incorrect covered lines.%n" +
"Covered: %s%n" +
"Expected: %s%n", coveredLines, testCase.expectedLines));
} else {
assertTrue(coveredLines.containsAll(testCase.expectedLines),
format("All significant lines are not covered.%n" +
"Covered: %s%n" +
"Expected: %s%n", coveredLines, testCase.expectedLines));
}
} catch (AssertionFailedException | CompilationException ex) {
System.err.printf("# %-20s#%n", testCase.getName());
int l = 0;

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8262891
* @summary Verify correct LineNumberTable for rule switches.
* @library /tools/lib /tools/javac/lib ../lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.util
* jdk.jdeps/com.sun.tools.classfile
* @build toolbox.ToolBox InMemoryFileManager TestBase
* @build LineNumberTestBase TestCase
* @run main RuleSwitchBreaks
*/
import java.util.List;
public class RuleSwitchBreaks extends LineNumberTestBase {
public static void main(String[] args) throws Exception {
new RuleSwitchBreaks().test();
}
public void test() throws Exception {
test(List.of(TEST_CASE));
}
private static final TestCase[] TEST_CASE = new TestCase[] {
new TestCase("""
public class Test { // 1
private void test(int i) { // 2
switch (i) { // 3
case 0 -> // 4
System.out.println("a"); // 5
case 1 -> // 6
System.out.println("a"); // 7
default -> // 8
System.out.println("default"); // 9
} //10
} //11
} //12
""",
List.of(1, 3, 5, 7, 9, 11),
true,
List.of(),
"Test"),
new TestCase("""
public class TestGuards { // 1
private void test(Object o) { // 2
switch (o) { // 3
case String s && s.isEmpty() -> // 4
System.out.println("a"); // 5
case String s -> // 6
System.out.println("a"); // 7
default -> // 8
System.out.println("default"); // 9
} //10
} //11
} //12
""",
List.of(1, 3, 4, 5, 6, 7, 9, 11),
true,
List.of("--enable-preview", "-source", System.getProperty("java.specification.version")),
"TestGuards")
};
}

View File

@ -23,6 +23,7 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
@ -32,6 +33,8 @@ import java.util.Set;
public class TestCase {
public final String src;
public final Set<Integer> expectedLines;
public final boolean exactLines;
public final List<String> extraCompilerOptions;
private final String name;
@ -41,8 +44,16 @@ public class TestCase {
}
public TestCase(String src, Collection<Integer> expectedLines, String name) {
this(src, expectedLines, false, List.of(), name);
}
public TestCase(String src, Collection<Integer> expectedLines,
boolean exactLines, List<String> extraCompilerOptions,
String name) {
this.src = src;
this.expectedLines = new HashSet<>(expectedLines);
this.exactLines = exactLines;
this.extraCompilerOptions = extraCompilerOptions;
this.name = name;
}
}

View File

@ -24,6 +24,10 @@
// key: compiler.err.cant.ref.non.effectively.final.var
// 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() {
@ -41,4 +45,11 @@ class CantRefNonEffectivelyFinalVar {
SAM s = ()-> { int j = i; };
i++;
}
void test3(Object o, int i) {
switch (o) {
case String s && s.length() == i++: break;
default: break;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 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,14 +21,15 @@
* questions.
*/
// key: compiler.err.switch.null.not.allowed
// key: compiler.misc.feature.case.null
// key: compiler.warn.preview.feature.use
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class SwitchNullNotAllowed {
void test(Integer i) {
switch (i) {
class CaseNull {
private void doSwitch(String s) {
switch (s) {
case null: break;
case 0: break;
default: break;
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021, 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.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) {
switch (o) {
case 0:
}
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021, 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.duplicate.total.pattern
// key: compiler.misc.feature.pattern.switch
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class DuplicateTotalPattern {
private void doSwitch(Object o) {
switch (o) {
case Object obj: break;
case Object obj: break;
}
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021, 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.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) {
switch (o) {
case String str:
default: break;
}
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021, 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.flows.through.to.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) {
switch (o) {
case String str:
case Object obj: break;
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021, 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.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) {
switch (o) {
case String s -> System.err.println("String of length: " + s.length());
};
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021, 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.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) {
switch (o) {
case CharSequence cs: break;
case String str: break;
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021, 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.misc.feature.pattern.switch
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class PatternSwitch {
private void doSwitch(Object o) {
switch (o) {
case String str: break;
default: break;
}
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021, 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.total.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 TotalPatternAndDefault {
private void doSwitch(Object o) {
switch (o) {
case Object obj: break;
default: break;
}
}
}

View File

@ -738,7 +738,7 @@ public class DPrinter {
@Override
public void visitCase(JCCase tree) {
printList("pat", tree.pats);
printList("labels", tree.labels);
printList("stats", tree.stats);
}

View File

@ -0,0 +1,37 @@
/*
* @test /nodynamiccopyright/
* @bug 8262891
* @summary Check null handling for non-pattern switches.
* @compile/fail/ref=CaseDefault.out -source 16 -Xlint:-options -XDrawDiagnostics CaseDefault.java
* @compile --enable-preview -source ${jdk.version} CaseDefault.java
* @run main/othervm --enable-preview CaseDefault
*/
public class CaseDefault {
public static void main(String[] args) {
new CaseDefault().run();
}
void run() {
String str = "other";
switch (str) {
case "a": throw new AssertionError("Wrong branch.");
case default: break; //OK
}
switch (str) {
case "a" -> throw new AssertionError("Wrong branch.");
case default -> {} //OK
}
int i;
i = switch (str) {
case "a": throw new AssertionError("Wrong branch.");
case default: yield 0; //OK
};
i = switch (str) {
case "a" -> throw new AssertionError("Wrong branch.");
case default -> 0; //OK
};
}
}

View File

@ -0,0 +1,2 @@
CaseDefault.java:20:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)
1 error

View File

@ -0,0 +1,88 @@
/**
* @test
* @modules jdk.compiler/com.sun.tools.javac.file
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.parser
* jdk.compiler/com.sun.tools.javac.tree
* jdk.compiler/com.sun.tools.javac.util
* @compile --enable-preview -source ${jdk.version} DisambiguateParenthesizedPattern.java
* @run main/othervm --enable-preview DisambiguateParenthesizedPattern
*/
import com.sun.source.tree.CaseLabelTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.PatternTree;
import com.sun.source.tree.SwitchTree;
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.Options;
import java.nio.charset.Charset;
public class DisambiguateParenthesizedPattern {
public static void main(String... args) throws Throwable {
DisambiguateParenthesizedPattern test = new DisambiguateParenthesizedPattern();
test.disambiguationTest("String s",
ExpressionType.PATTERN);
test.disambiguationTest("String s && s.isEmpty()",
ExpressionType.PATTERN);
test.disambiguationTest("(String s)",
ExpressionType.PATTERN);
test.disambiguationTest("((String s))",
ExpressionType.PATTERN);
test.disambiguationTest("(String) s",
ExpressionType.EXPRESSION);
test.disambiguationTest("((String) s)",
ExpressionType.EXPRESSION);
test.disambiguationTest("((0x1))",
ExpressionType.EXPRESSION);
}
private final ParserFactory factory;
public DisambiguateParenthesizedPattern() {
Context context = new Context();
JavacFileManager jfm = new JavacFileManager(context, true, Charset.defaultCharset());
Options.instance(context).put(Option.PREVIEW, "");
factory = ParserFactory.instance(context);
}
void disambiguationTest(String snippet, ExpressionType expectedType) {
String code = """
public class Test {
private void test() {
switch (null) {
case SNIPPET -> {}
}
}
}
""".replace("SNIPPET", snippet);
JavacParser parser = factory.newParser(code, false, false, false);
CompilationUnitTree result = parser.parseCompilationUnit();
ClassTree clazz = (ClassTree) result.getTypeDecls().get(0);
MethodTree method = (MethodTree) clazz.getMembers().get(0);
SwitchTree st = (SwitchTree) method.getBody().getStatements().get(0);
CaseLabelTree label = st.getCases().get(0).getLabels().get(0);
ExpressionType actualType = switch (label) {
case ExpressionTree et -> ExpressionType.EXPRESSION;
case PatternTree pt -> ExpressionType.PATTERN;
default -> throw new AssertionError("Unexpected result: " + result);
};
if (expectedType != actualType) {
throw new AssertionError("Expected: " + expectedType + ", actual: " + actualType +
", for: " + code + ", parsed: " + result);
}
}
enum ExpressionType {
PATTERN,
EXPRESSION;
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8262891
* @summary Check the pattern domination error are reported correctly.
* @compile/fail/ref=Domination.out -XDrawDiagnostics --enable-preview -source ${jdk.version} Domination.java
*/
public class Domination {
int testDominatesError1(Object o) {
switch (o) {
case CharSequence cs: return 0;
case String s: return 1;
case Object x: return -1;
}
}
int testDominatesError2(Object o) {
switch (o) {
case CharSequence cs: return 0;
case String s && s.isEmpty(): return 1;
case Object x: return -1;
}
}
int testDominatesError3(Object o) {
switch (o) {
case CharSequence cs && true: return 0;
case String s && s.isEmpty(): return 1;
case Object x: return -1;
}
}
int testNotDominates1(Object o) {
switch (o) {
case CharSequence cs && cs.length() == 0: return 0;
case String s: return 1;
case Object x: return -1;
}
}
int testDominatesStringConstant(String str) {
switch (str) {
case String s: return 1;
case "": return -1;
}
}
int testDominatesIntegerConstant(Integer i) {
switch (i) {
case Integer j: return 1;
case 0: return -1;
}
}
int testDominatesEnumConstant() {
enum E {
A, B;
}
E e = E.A;
switch (e) {
case E d: return 1;
case A: return -1;
}
}
}

View File

@ -0,0 +1,9 @@
Domination.java:35:18: compiler.err.pattern.dominated
Domination.java:43:18: compiler.err.pattern.dominated
Domination.java:51:18: compiler.err.pattern.dominated
Domination.java:67:18: compiler.err.pattern.dominated
Domination.java:74:18: compiler.err.pattern.dominated
Domination.java:85:18: compiler.err.pattern.dominated
- compiler.note.preview.filename: Domination.java, DEFAULT
- compiler.note.preview.recompile
6 errors

View File

@ -0,0 +1,643 @@
/*
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8262891
* @summary Check exhaustiveness of switches over sealed types.
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.util
* @build toolbox.ToolBox toolbox.JavacTask
* @run main Exhaustiveness
*/
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import toolbox.TestRunner;
import toolbox.JavacTask;
import toolbox.Task;
import toolbox.ToolBox;
public class Exhaustiveness extends TestRunner {
private static final String JAVA_VERSION = System.getProperty("java.specification.version");
ToolBox tb;
public static void main(String... args) throws Exception {
new Exhaustiveness().runTests();
}
Exhaustiveness() {
super(System.err);
tb = new ToolBox();
}
public void runTests() throws Exception {
runTests(m -> new Object[] { Paths.get(m.getName()) });
}
@Test
public void testExhaustiveSealedClasses(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public sealed interface S permits A, B {}
""",
"""
package lib;
public final class A implements S {}
""",
"""
package lib;
public final class B implements S {}
"""},
"""
package test;
import lib.*;
public class Test {
private int test(S obj) {
return switch (obj) {
case A a -> 0;
case B b -> 1;
};
}
}
""");
}
@Test
public void testNonExhaustiveSealedClasses(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public sealed interface S permits A, B {}
""",
"""
package lib;
public final class A implements S {}
""",
"""
package lib;
public final class B implements S {}
"""},
"""
package test;
import lib.*;
public class Test {
private int test(S obj) {
return switch (obj) {
case A a -> 0;
};
}
}
""",
"Test.java:5:16: compiler.err.not.exhaustive",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"1 error");
}
@Test
public void testAbstractSealedClasses(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public sealed abstract class S permits A, B {}
""",
"""
package lib;
public final class A extends S {}
""",
"""
package lib;
public final class B extends S {}
"""},
"""
package test;
import lib.*;
public class Test {
private int test(S obj) {
return switch (obj) {
case A a -> 0;
case B b -> 1;
};
}
}
""");
}
@Test
public void testConcreteSealedClasses(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public sealed class S permits A, B {}
""",
"""
package lib;
public final class A extends S {}
""",
"""
package lib;
public final class B extends S {}
"""},
"""
package test;
import lib.*;
public class Test {
private int test(S obj) {
return switch (obj) {
case A a -> 0;
case B b -> 1;
};
}
}
""",
"Test.java:5:16: compiler.err.not.exhaustive",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"1 error");
}
@Test
public void testGuards1(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public sealed interface S permits A, B {}
""",
"""
package lib;
public final class A implements S {}
""",
"""
package lib;
public final class B implements S {}
"""},
"""
package test;
import lib.*;
public class Test {
private int test(S obj) {
return switch (obj) {
case A a && a.toString().isEmpty() -> 0;
case B b -> 1;
};
}
}
""",
"Test.java:5:16: compiler.err.not.exhaustive",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"1 error");
}
@Test
public void testGuards2(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public sealed interface S permits A, B {}
""",
"""
package lib;
public final class A implements S {}
""",
"""
package lib;
public final class B implements S {}
"""},
"""
package test;
import lib.*;
public class Test {
private static final boolean TEST = true;
private int test(S obj) {
return switch (obj) {
case A a && !(!(TEST)) -> 0;
case B b -> 1;
};
}
}
""");
}
@Test
public void testGuards3(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public sealed interface S permits A, B {}
""",
"""
package lib;
public final class A implements S {}
""",
"""
package lib;
public final class B implements S {}
"""},
"""
package test;
import lib.*;
public class Test {
private int test(S obj) {
return switch (obj) {
case A a && false -> 0;
case B b -> 1;
};
}
}
""",
"Test.java:5:16: compiler.err.not.exhaustive",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"1 error");
}
@Test
public void testCoversType1(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public sealed interface S permits A, B {}
""",
"""
package lib;
public final class A implements S {}
""",
"""
package lib;
public final class B implements S {}
"""},
"""
package test;
import lib.*;
public class Test {
private int test(S obj) {
return switch (obj) {
case A a -> 0;
case S s -> 1;
};
}
}
""");
}
@Test
public void testCoversType2(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public interface S {}
""",
"""
package lib;
public final class A implements S {}
""",
"""
package lib;
public final class B implements S {}
"""},
"""
package test;
import lib.*;
public class Test {
private int test(S obj) {
return switch (obj) {
case A a -> 0;
case S s -> 1;
};
}
}
""");
}
@Test
public void testCoversType3(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public interface S<T> {}
""",
"""
package lib;
public final class A implements S<A> {}
""",
"""
package lib;
public final class B implements S<B> {}
"""},
"""
package test;
import lib.*;
public class Test {
private int test(S obj) {
return switch (obj) {
case A a -> 0;
case S<?> s -> 1;
};
}
}
""");
}
private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException {
Path current = base.resolve(".");
Path libSrc = current.resolve("lib-src");
for (String code : libraryCode) {
tb.writeJavaFiles(libSrc, code);
}
Path libClasses = current.resolve("libClasses");
Files.createDirectories(libClasses);
new JavacTask(tb)
.options("--enable-preview",
"-source", JAVA_VERSION)
.outdir(libClasses)
.files(tb.findJavaFiles(libSrc))
.run();
Path src = current.resolve("src");
tb.writeJavaFiles(src, testCode);
Path classes = current.resolve("libClasses");
Files.createDirectories(libClasses);
var log =
new JavacTask(tb)
.options("--enable-preview",
"-source", JAVA_VERSION,
"-XDrawDiagnostics",
"-Xlint:-preview",
"--class-path", libClasses.toString())
.outdir(classes)
.files(tb.findJavaFiles(src))
.run(expectedErrors.length > 0 ? Task.Expect.FAIL : Task.Expect.SUCCESS)
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
if (expectedErrors.length > 0 && !List.of(expectedErrors).equals(log)) {
throw new AssertionError("Incorrect errors, expected: " + List.of(expectedErrors) +
", actual: " + log);
}
}
@Test
public void testInaccessiblePermitted(Path base) throws IOException {
Path current = base.resolve(".");
Path libSrc = current.resolve("lib-src");
tb.writeJavaFiles(libSrc,
"""
package lib;
public sealed interface S permits A, B {}
""",
"""
package lib;
public final class A implements S {}
""",
"""
package lib;
final class B implements S {}
""");
Path libClasses = current.resolve("libClasses");
Files.createDirectories(libClasses);
new JavacTask(tb)
.options("--enable-preview",
"-source", JAVA_VERSION)
.outdir(libClasses)
.files(tb.findJavaFiles(libSrc))
.run();
Path src = current.resolve("src");
tb.writeJavaFiles(src,
"""
package test;
import lib.*;
public class Test {
private int test(S obj) {
return switch (obj) {
case A a -> 0;
};
}
}
""");
Path classes = current.resolve("libClasses");
Files.createDirectories(libClasses);
var log =
new JavacTask(tb)
.options("--enable-preview",
"-source", JAVA_VERSION,
"-XDrawDiagnostics",
"-Xlint:-preview",
"--class-path", libClasses.toString())
.outdir(classes)
.files(tb.findJavaFiles(src))
.run(Task.Expect.FAIL)
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
List<String> expectedErrors = List.of(
"Test.java:5:16: compiler.err.not.exhaustive",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"1 error");
if (!expectedErrors.equals(log)) {
throw new AssertionError("Incorrect errors, expected: " + expectedErrors +
", actual: " + log);
}
Path bClass = libClasses.resolve("lib").resolve("B.class");
Files.delete(bClass);
var log2 =
new JavacTask(tb)
.options("--enable-preview",
"-source", JAVA_VERSION,
"-XDrawDiagnostics",
"-Xlint:-preview",
"--class-path", libClasses.toString())
.outdir(classes)
.files(tb.findJavaFiles(src))
.run(Task.Expect.FAIL)
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
if (!expectedErrors.equals(log2)) {
throw new AssertionError("Incorrect errors, expected: " + expectedErrors +
", actual: " + log2);
}
}
@Test
public void testExhaustiveStatement1(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public interface Lib {}
"""},
"""
package test;
public class Test {
private int test(Object obj) {
switch (obj) {
case Object o: return 0;
}
}
}
""");
}
@Test
public void testExhaustiveStatement2(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public interface Lib {}
"""},
"""
package test;
public class Test {
private void test(Object obj) {
switch (obj) {
case String s: return;
};
}
}
""",
"Test.java:4:9: compiler.err.not.exhaustive.statement",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"1 error");
}
@Test
public void testExhaustiveStatement3(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public sealed interface S permits A, B {}
""",
"""
package lib;
public final class A implements S {}
""",
"""
package lib;
public final class B implements S {}
"""},
"""
package test;
import lib.*;
public class Test {
private int test(S obj) {
return switch (obj) {
case A a -> 0;
case S s -> 1;
};
}
}
""");
}
@Test
public void testExhaustiveStatement4(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public sealed interface S permits A, B {}
""",
"""
package lib;
public final class A implements S {}
""",
"""
package lib;
public final class B implements S {}
"""},
"""
package test;
import lib.*;
public class Test {
private int test(S obj) {
return switch (obj) {
case A a -> 0;
};
}
}
""",
"Test.java:5:16: compiler.err.not.exhaustive",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"1 error");
}
@Test
public void testExhaustiveStatement5(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public sealed interface S permits A, B {}
""",
"""
package lib;
public final class A implements S {}
""",
"""
package lib;
public final class B implements S {}
"""},
"""
package test;
import lib.*;
public class Test {
private int test(S obj) {
return switch (obj) {
case A a -> 0;
case B b -> 0;
};
}
}
""");
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8262891
* @summary Check guards implementation.
* @compile --enable-preview -source ${jdk.version} Guards.java
* @run main/othervm --enable-preview Guards
*/
import java.util.Objects;
import java.util.function.Function;
public class Guards {
public static void main(String... args) {
new Guards().run();
}
void run() {
run(this::typeTestPatternSwitchTest);
run(this::typeTestPatternSwitchExpressionTest);
run(this::testBooleanSwitchExpression);
assertEquals("a", testPatternInGuard("a"));
assertEquals(null, testPatternInGuard(1));
}
void run(Function<Object, String> convert) {
assertEquals("zero", convert.apply(0));
assertEquals("one", convert.apply(1));
assertEquals("other", convert.apply(-1));
assertEquals("any", convert.apply(""));
}
String typeTestPatternSwitchTest(Object o) {
switch (o) {
case Integer i && i == 0: return "zero";
case Integer i && i == 1: return "one";
case Integer i: return "other";
case Object x: return "any";
}
}
String typeTestPatternSwitchExpressionTest(Object o) {
return switch (o) {
case Integer i && i == 0 -> "zero";
case Integer i && i == 1 -> { yield "one"; }
case Integer i -> "other";
case Object x -> "any";
};
}
String testBooleanSwitchExpression(Object o) {
String x;
if (switch (o) {
case Integer i && i == 0 -> (x = "zero") != null;
case Integer i && i == 1 -> { x = "one"; yield true; }
case Integer i -> { x = "other"; yield true; }
case Object other -> (x = "any") != null;
}) {
return x;
} else {
throw new IllegalStateException("TODO - needed?");
}
}
String testPatternInGuard(Object o) {
if (o instanceof (CharSequence cs && cs instanceof String s)) {
return s;
}
return null;
}
void assertEquals(String expected, String actual) {
if (!Objects.equals(expected, actual)) {
throw new AssertionError("Expected: " + expected + ", but got: " + actual);
}
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8262891
* @summary Check errors reported for guarded patterns.
* @compile/fail/ref=GuardsErrors.out -XDrawDiagnostics --enable-preview -source ${jdk.version} GuardsErrors.java
*/
public class GuardsErrors {
void typeTestPatternSwitchTest(Object o, int check) {
switch (o) {
case Integer i && i == check -> System.err.println(); //error: check is not effectivelly final
default -> {}
}
check = 0;
}
}

View File

@ -0,0 +1,4 @@
GuardsErrors.java:35:36: compiler.err.cant.ref.non.effectively.final.var: check, (compiler.misc.guard)
- compiler.note.preview.filename: GuardsErrors.java, DEFAULT
- compiler.note.preview.recompile
1 error

View File

@ -0,0 +1,168 @@
/*
* @test /nodynamiccopyright/
* @bug 8262891
* @summary Check null handling for non-pattern switches.
* @compile --enable-preview -source ${jdk.version} NullSwitch.java
* @run main/othervm --enable-preview NullSwitch
*/
public class NullSwitch {
public static void main(String[] args) {
new NullSwitch().switchTest();
}
void switchTest() {
assertEquals(0, matchingSwitch1(""));
assertEquals(1, matchingSwitch1("a"));
assertEquals(100, matchingSwitch1(0));
assertEquals(-1, matchingSwitch1(null));
assertEquals(-2, matchingSwitch1(0.0));
assertEquals(0, matchingSwitch2(""));
assertEquals(1, matchingSwitch2(null));
assertEquals(1, matchingSwitch2(0.0));
assertEquals(0, matchingSwitch3(""));
assertEquals(1, matchingSwitch3("a"));
assertEquals(100, matchingSwitch3(0));
assertEquals(-1, matchingSwitch3(null));
assertEquals(-2, matchingSwitch3(0.0));
assertEquals(0, matchingSwitch4(""));
assertEquals(1, matchingSwitch4(null));
assertEquals(1, matchingSwitch4(0.0));
assertEquals(0, matchingSwitch5(""));
assertEquals(1, matchingSwitch5("a"));
assertEquals(100, matchingSwitch5(0));
assertEquals(-1, matchingSwitch5(null));
assertEquals(-2, matchingSwitch5(0.0));
assertEquals(0, matchingSwitch6(""));
assertEquals(1, matchingSwitch6(null));
assertEquals(1, matchingSwitch6(0.0));
assertEquals(0, matchingSwitch7(""));
assertEquals(1, matchingSwitch7("a"));
assertEquals(100, matchingSwitch7(0));
assertEquals(-1, matchingSwitch7(null));
assertEquals(-2, matchingSwitch7(0.0));
assertEquals(0, matchingSwitch8(""));
assertEquals(1, matchingSwitch8(null));
assertEquals(1, matchingSwitch8(0.0));
assertEquals(0, matchingSwitch9(""));
assertEquals(1, matchingSwitch9(null));
assertEquals(1, matchingSwitch9(0.0));
assertEquals(0, matchingSwitch10(""));
assertEquals(1, matchingSwitch10(null));
assertEquals(1, matchingSwitch10(0.0));
assertEquals(0, matchingSwitch11(""));
assertEquals(2, matchingSwitch11(null));
assertEquals(1, matchingSwitch11(0.0));
assertEquals(0, matchingSwitch12(""));
assertEquals(2, matchingSwitch12(null));
assertEquals(1, matchingSwitch12(0.0));
}
private int matchingSwitch1(Object obj) {
return switch (obj) {
case String s -> s.length();
case null, Integer i -> i == null ? -1 : 100 + i;
default -> -2;
};
}
private int matchingSwitch2(Object obj) {
return switch (obj) {
case String s -> 0;
case null, default -> 1;
};
}
private int matchingSwitch3(Object obj) {
return switch (obj) {
case String s -> s.length();
case Integer i, null -> i == null ? -1 : 100 + i;
default -> -2;
};
}
private int matchingSwitch4(Object obj) {
return switch (obj) {
case String s -> 0;
case default, null -> 1;
};
}
private int matchingSwitch5(Object obj) {
return switch (obj) {
case String s: yield s.length();
case null:
case Integer i: yield i == null ? -1 : 100 + i;
default: yield -2;
};
}
private int matchingSwitch6(Object obj) {
return switch (obj) {
case String s: yield 0;
case null:
default: yield 1;
};
}
private int matchingSwitch7(Object obj) {
return switch (obj) {
case String s: yield s.length();
case Integer i:
case null: yield i == null ? -1 : 100 + i;
default: yield -2;
};
}
private int matchingSwitch8(Object obj) {
return switch (obj) {
case String s: yield 0;
default:
case null: yield 1;
};
}
private int matchingSwitch9(Object obj) {
return switch (obj) {
case String s: yield 0;
case Object o: yield 1;
};
}
private int matchingSwitch10(Object obj) {
switch (obj) {
case String s: return 0;
case Object o: return 1;
}
}
private int matchingSwitch11(Object obj) {
try {
return switch (obj) {
case String s: yield 0;
default: yield 1;
};
} catch (NullPointerException ex) {
return 2;
}
}
private int matchingSwitch12(Object obj) {
try {
switch (obj) {
case String s: return 0;
default: return 1;
}
} catch (NullPointerException ex) {
return 2;
}
}
static void assertEquals(int expected, int actual) {
if (expected != actual) {
throw new AssertionError("Expected: " + expected + ", actual: " + actual);
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8262891
* @summary Test parenthesized pattern
* @compile --enable-preview -source ${jdk.version} Parenthesized.java
* @run main/othervm --enable-preview Parenthesized
*/
public class Parenthesized {
public static void main(String... args) {
new Parenthesized().run();
}
void run() {
Object o = "";
switch (o) {
case (String s && s.isEmpty()) -> System.err.println("OK: " + s);
default -> throw new AssertionError();
}
System.err.println(switch (o) {
case (String s && s.isEmpty()) -> "OK: " + s;
default -> throw new AssertionError();
});
if (o instanceof (String s && s.isEmpty())) {
System.err.println("OK: " + s);
}
}
}

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8262891
* @summary Verify pattern switches work properly when the set of sealed types changes.
* @compile --enable-preview -source ${jdk.version} SealedTypeChanges.java
* @compile --enable-preview -source ${jdk.version} SealedTypeChanges2.java
* @run main/othervm --enable-preview SealedTypeChanges
*/
import java.util.function.Consumer;
public class SealedTypeChanges {
public static void main(String... args) throws Exception {
new SealedTypeChanges().run();
}
void run() throws Exception {
doRun(this::expressionIntf, this::validateIncompatibleClassChangeError);
doRun(this::statementIntf, this::validateIncompatibleClassChangeError);
doRun(this::expressionCls, this::validateIncompatibleClassChangeError);
doRun(this::statementCls, this::validateIncompatibleClassChangeError);
doRun(this::expressionCoveredIntf, this::validateTestException);
doRun(this::statementCoveredIntf, this::validateTestException);
doRun(this::expressionCoveredCls, this::validateTestException);
doRun(this::statementCoveredCls, this::validateTestException);
}
<T> void doRun(Consumer<T> t, Consumer<Throwable> validateException) throws Exception {
t.accept((T) new A());
try {
t.accept((T) Class.forName("SealedTypeChangesClass").newInstance());
throw new AssertionError("Expected an exception, but none thrown.");
} catch (Throwable ex) {
validateException.accept(ex);
}
}
void validateIncompatibleClassChangeError(Throwable t) {
if (!(t instanceof IncompatibleClassChangeError)) {
throw new AssertionError("Unexpected exception", t);
}
}
void validateTestException(Throwable t) {
if (!(t instanceof TestException)) {
throw new AssertionError("Unexpected exception", t);
}
}
void statementIntf(SealedTypeChangesIntf obj) {
switch (obj) {
case A a -> System.err.println(1);
}
}
int expressionIntf(SealedTypeChangesIntf obj) {
return switch (obj) {
case A a -> 0;
};
}
void statementCls(SealedTypeChangesCls obj) {
switch (obj) {
case A a -> System.err.println(1);
}
}
int expressionCls(SealedTypeChangesCls obj) {
return switch (obj) {
case A a -> 0;
};
}
void statementCoveredIntf(SealedTypeChangesIntf obj) {
switch (obj) {
case A a -> System.err.println(1);
case SealedTypeChangesIntf o -> throw new TestException();
}
}
int expressionCoveredIntf(SealedTypeChangesIntf obj) {
return switch (obj) {
case A a -> 0;
case SealedTypeChangesIntf o -> throw new TestException();
};
}
void statementCoveredCls(SealedTypeChangesCls obj) {
switch (obj) {
case A a -> System.err.println(1);
case SealedTypeChangesCls o -> throw new TestException();
}
}
int expressionCoveredCls(SealedTypeChangesCls obj) {
return switch (obj) {
case A a -> 0;
case SealedTypeChangesCls o -> throw new TestException();
};
}
final static class A extends SealedTypeChangesCls implements SealedTypeChangesIntf {}
class TestException extends RuntimeException {}
}
sealed interface SealedTypeChangesIntf permits SealedTypeChanges.A {}
sealed abstract class SealedTypeChangesCls permits SealedTypeChanges.A {}

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2021, 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.
*/
sealed interface SealedTypeChangesIntf permits SealedTypeChanges.A, SealedTypeChangesClass {}
sealed abstract class SealedTypeChangesCls permits SealedTypeChanges.A, SealedTypeChangesClass {}
final class SealedTypeChangesClass extends SealedTypeChangesCls implements SealedTypeChangesIntf {}

View File

@ -0,0 +1,31 @@
/**
* @test
* @compile --enable-preview -source ${jdk.version} -doe SimpleAndGuardPattern.java
* @run main/othervm --enable-preview SimpleAndGuardPattern
*/
import java.util.List;
import java.util.Objects;
public class SimpleAndGuardPattern {
public static void main(String... args) throws Throwable {
if (!Objects.equals(4, simple("test"))) {
throw new IllegalStateException();
}
if (!Objects.equals(4, simple("TEST"))) {
throw new IllegalStateException();
}
if (!Objects.equals(-1, simple("other"))) {
throw new IllegalStateException();
}
}
private static int simple(Object o) throws Throwable {
return switch (o) {
case String s && s.equalsIgnoreCase("test") -> s.length();
default -> -1;
};
}
}

View File

@ -0,0 +1,193 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8262891
* @summary Verify errors related to pattern switches.
* @compile/fail/ref=SwitchErrors.out --enable-preview -source ${jdk.version} -XDrawDiagnostics -XDshould-stop.at=FLOW SwitchErrors.java
*/
public class SwitchErrors {
void incompatibleSelectorObjectString(Object o) {
switch (o) {
case "A": break;
case CharSequence cs: break;
}
}
void incompatibleSelectorObjectInteger(Object o) {
switch (o) {
case 1: break;
case CharSequence cs: break;
}
}
void incompatibleSelectorIntegerString(Integer i) {
switch (i) {
case "A": break;
case CharSequence cs: break;
}
}
void incompatibleSelectorPrimitive(int i) {
switch (i) {
case null: break;
case "A": break;
case CharSequence cs: break;
}
}
void totalAndDefault1(Object o) {
switch (o) {
case Object obj: break;
default: break;
}
}
void totalAndDefault2(Object o) {
switch (o) {
case Object obj: break;
case null, default: break;
}
}
void totalAndDefault3(Object o) {
switch (o) {
default: break;
case Object obj: break;
}
}
void duplicatedTotal(Object o) {
switch (o) {
case Object obj: break;
case Object obj: break;
}
}
void duplicatedDefault1(Object o) {
switch (o) {
case null, default: break;
default: break;
}
}
void duplicatedDefault2(Object o) {
switch (o) {
case default: break;
default: break;
}
}
void duplicatedDefault3(Object o) {
switch (o) {
case default, default: break;
}
}
void duplicatedNullCase1(Object o) {
switch (o) {
case null: break;
case null: break;
}
}
void duplicatedNullCase2(Object o) {
switch (o) {
case null, null: break;
}
}
void duplicatedTypePatterns1(Object o) {
switch (o) {
case String s, Integer i: break;
}
}
void duplicatedTypePatterns2(Object o) {
switch (o) {
case String s:
case Integer i: break;
}
}
void duplicatedTypePatterns3(Object o) {
switch (o) {
case String s:
System.err.println(1);
case Integer i: break;
}
}
void flowIntoTypePatterns(Object o) {
switch (o) {
case null:
System.err.println(1);
case Integer i: break;
}
}
void incompatible1(String str) {
switch (str) {
case Integer i: break;
default: break;
}
}
void incompatible2(java.util.List l) {
switch (l) {
case java.util.List<Integer> l2: break;
}
}
void erroneous(Object o) {
switch (o) {
case String s: break;
case Undefined u: break;
case Integer i: break;
default: break;
}
}
void primitivePattern(Object o) {
switch (o) {
case int i: break;
default: break;
}
}
void patternAndDefault1(Object o) {
switch (o) {
case String s, default: break;
}
}
void patternAndDefault2(Object o) {
switch (o) {
case String s:
case default: break;
}
}
void patternAndDefault3(Object o) {
switch (o) {
case default, String s: break;
}
}
void patternAndDefault4(Object o) {
switch (o) {
case default:
case String s: break;
}
}
void nullAfterTotal(Object o) {
switch (o) {
case Object obj: break;
case null: break;
}
}
void sealedNonAbstract(SealedNonAbstract obj) {
switch (obj) {//does not cover SealedNonAbstract
case A a -> {}
}
}
sealed class SealedNonAbstract permits A {}
final class A extends SealedNonAbstract {}
}

View File

@ -0,0 +1,44 @@
SwitchErrors.java:33:18: compiler.err.constant.label.not.compatible: java.lang.String, java.lang.Object
SwitchErrors.java:39:18: compiler.err.constant.label.not.compatible: int, java.lang.Object
SwitchErrors.java:45:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
SwitchErrors.java:46:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.CharSequence)
SwitchErrors.java:51:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: compiler.misc.type.null, int)
SwitchErrors.java:52:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int)
SwitchErrors.java:53:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.CharSequence)
SwitchErrors.java:59:20: compiler.err.total.pattern.and.default
SwitchErrors.java:65:13: compiler.err.pattern.dominated
SwitchErrors.java:65:24: compiler.err.total.pattern.and.default
SwitchErrors.java:71:18: compiler.err.total.pattern.and.default
SwitchErrors.java:77:18: compiler.err.duplicate.total.pattern
SwitchErrors.java:83:20: compiler.err.duplicate.default.label
SwitchErrors.java:89:20: compiler.err.duplicate.default.label
SwitchErrors.java:94:27: compiler.err.duplicate.default.label
SwitchErrors.java:100:13: compiler.err.duplicate.case.label
SwitchErrors.java:105:13: compiler.err.duplicate.case.label
SwitchErrors.java:110:28: compiler.err.flows.through.to.pattern
SwitchErrors.java:116:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:123:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:130:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:135:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer)
SwitchErrors.java:141:18: compiler.err.instanceof.reifiable.not.safe: java.util.List, java.util.List<java.lang.Integer>
SwitchErrors.java:147:18: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:154:18: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array)
SwitchErrors.java:160:28: compiler.err.flows.through.from.pattern
SwitchErrors.java:166:18: compiler.err.flows.through.from.pattern
SwitchErrors.java:171:27: compiler.err.flows.through.to.pattern
SwitchErrors.java:177:18: compiler.err.flows.through.to.pattern
SwitchErrors.java:183:13: compiler.err.pattern.dominated
SwitchErrors.java:32:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:38:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:44:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:50:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:98:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:104:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:109:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:114:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:120:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:127:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:187:9: compiler.err.not.exhaustive.statement
- compiler.note.preview.filename: SwitchErrors.java, DEFAULT
- compiler.note.preview.recompile
41 errors

View File

@ -0,0 +1,344 @@
/*
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
/*
* @test
* @bug 8262891
* @summary Check behavior of pattern switches.
* @compile --enable-preview -source ${jdk.version} Switches.java
* @run main/othervm --enable-preview Switches
*/
public class Switches {
public static void main(String... args) {
new Switches().run();
}
void run() {
run(this::typeTestPatternSwitchTest);
run(this::typeTestPatternSwitchExpressionTest);
run(this::testBooleanSwitchExpression);
assertFalse(testNullSwitch(null));
assertTrue(testNullSwitch(""));
runArrayTypeTest(this::testArrayTypeStatement);
runArrayTypeTest(this::testArrayTypeExpression);
runEnumTest(this::testEnumExpression1);
runEnumTest(this::testEnumExpression2);
runEnumTest(this::testEnumWithGuards1);
runEnumTest(this::testEnumWithGuards2);
runEnumTest(this::testEnumWithGuardsExpression1);
runEnumTest(this::testEnumWithGuardsExpression2);
runEnumTest(this::testStringWithGuards1);
runEnumTest(this::testStringWithGuardsExpression1);
runEnumTest(this::testIntegerWithGuards1);
runEnumTest(this::testIntegerWithGuardsExpression1);
runStringWithConstant(this::testStringWithConstant);
runStringWithConstant(this::testStringWithConstantExpression);
npeTest(this::npeTestStatement);
npeTest(this::npeTestExpression);
exhaustiveStatementSane("");
exhaustiveStatementSane(null);
}
void run(Function<Object, Integer> mapper) {
assertEquals(2, mapper.apply("2"));
assertEquals(3, mapper.apply("3"));
assertEquals(8, mapper.apply(new StringBuilder("4")));
assertEquals(2, mapper.apply(2));
assertEquals(3, mapper.apply(3));
assertEquals(-1, mapper.apply(2.0));
assertEquals(-1, mapper.apply(new Object()));
}
void runArrayTypeTest(Function<Object, String> mapper) {
assertEquals("arr0", mapper.apply(new int[0]));
assertEquals("str6", mapper.apply("string"));
assertEquals("i1", mapper.apply(1));
assertEquals("", mapper.apply(1.0));
}
void runEnumTest(Function<E, String> mapper) {
assertEquals("a", mapper.apply(E.A));
assertEquals("b", mapper.apply(E.B));
assertEquals("C", mapper.apply(E.C));
assertEquals("null", mapper.apply(null));
}
void runStringWithConstant(Function<String, Integer> mapper) {
assertEquals(1, mapper.apply("A"));
assertEquals(2, mapper.apply("AA"));
assertEquals(0, mapper.apply(""));
assertEquals(-1, mapper.apply(null));
}
void npeTest(Consumer<I> testCase) {
try {
testCase.accept(null);
throw new AssertionError("Expected a NullPointerException, but got nothing.");
} catch (NullPointerException ex) {
//OK
}
}
int typeTestPatternSwitchTest(Object o) {
switch (o) {
case String s: return Integer.parseInt(s.toString());
case CharSequence s: return 2 * Integer.parseInt(s.toString());
case Integer i: return i;
case Object x: return -1;
}
}
int typeTestPatternSwitchExpressionTest(Object o) {
return switch (o) {
case String s -> Integer.parseInt(s.toString());
case @Deprecated CharSequence s -> { yield 2 * Integer.parseInt(s.toString()); }
case final Integer i -> i;
case Object x -> -1;
};
}
int testBooleanSwitchExpression(Object o) {
Object x;
if (switch (o) {
default -> false;
}) {
return -3;
} else if (switch (o) {
case String s -> (x = s) != null;
default -> false;
}) {
return Integer.parseInt(x.toString());
} else if (switch (o) {
case CharSequence s -> {
x = s;
yield true;
}
default -> false;
}) {
return 2 * Integer.parseInt(x.toString());
}
return typeTestPatternSwitchTest(o);
}
boolean testNullSwitch(Object o) {
return switch (o) {
case null -> false;
default -> true;
};
}
String testArrayTypeStatement(Object o) {
String res;
switch (o) {
case Integer i -> res = "i" + i;
case int[] arr -> res = "arr" + arr.length;
case String str -> res = "str" + str.length();
default -> res = "";
}
return res;
}
String testArrayTypeExpression(Object o) {
return switch (o) {
case Integer i -> "i" + i;
case int[] arr -> "arr" + arr.length;
case String str -> "str" + str.length();
default -> "";
};
}
int testStringWithConstant(String str) {
switch (str) {
case "A": return 1;
case null: return -1;
case String s: return s.length();
}
}
int testStringWithConstantExpression(String str) {
return switch (str) {
case "A" -> 1;
case null -> -1;
case String s -> s.length();
};
}
String testEnumExpression1(E e) {
return switch (e) {
case A -> "a";
case B -> "b";
case null, E x -> String.valueOf(x);
};
}
String testEnumExpression2(E e) {
return switch (e) {
case A -> "a";
case B -> "b";
case E x, null -> String.valueOf(x);
};
}
String testEnumWithGuards1(E e) {
switch (e) {
case A: return "a";
case B: return "b";
case E x && "A".equals(x.name()): return "broken";
case C: return String.valueOf(e);
case null, E x: return String.valueOf(x);
}
}
String testEnumWithGuardsExpression1(E e) {
return switch (e) {
case A -> "a";
case B -> "b";
case E x && "A".equals(x.name()) -> "broken";
case C -> String.valueOf(e);
case null, E x -> String.valueOf(x);
};
}
String testEnumWithGuards2(E e) {
switch (e) {
case A: return "a";
case B: return "b";
case E x && "C".equals(x.name()): return "C";
case C: return "broken";
case null, E x: return String.valueOf(x);
}
}
String testEnumWithGuardsExpression2(E e) {
return switch (e) {
case A -> "a";
case B -> "b";
case E x && "C".equals(x.name()) -> "C";
case C -> "broken";
case null, E x -> String.valueOf(x);
};
}
String testStringWithGuards1(E e) {
switch (e != null ? e.name() : null) {
case "A": return "a";
case "B": return "b";
case String x && "C".equals(x): return "C";
case "C": return "broken";
case null, String x: return String.valueOf(x);
}
}
String testStringWithGuardsExpression1(E e) {
return switch (e != null ? e.name() : null) {
case "A" -> "a";
case "B" -> "b";
case String x && "C".equals(x) -> "C";
case "C" -> "broken";
case null, String x -> String.valueOf(x);
};
}
String testIntegerWithGuards1(E e) {
switch (e != null ? e.ordinal() : null) {
case 0: return "a";
case 1: return "b";
case Integer x && x.equals(2): return "C";
case 2: return "broken";
case null, Integer x: return String.valueOf(x);
}
}
String testIntegerWithGuardsExpression1(E e) {
return switch (e != null ? e.ordinal() : null) {
case 0 -> "a";
case 1 -> "b";
case Integer x && x.equals(2) -> "C";
case 2 -> "broken";
case null, Integer x -> String.valueOf(x);
};
}
void npeTestStatement(I i) {
switch (i) {
case A a -> {}
case B b -> {}
}
}
void npeTestExpression(I i) {
int j = switch (i) {
case A a -> 0;
case B b -> 1;
};
}
void exhaustiveStatementSane(Object o) {
switch (o) {
case Object obj:; //no break intentionally - should not fall through to any possible default
}
switch (o) {
case null, Object obj:; //no break intentionally - should not fall through to any possible default
}
switch (o) {
case Object obj, null:; //no break intentionally - should not fall through to any possible default
}
}
sealed interface I {}
final class A implements I {}
final class B implements I {}
void assertEquals(int expected, int actual) {
if (expected != actual) {
throw new AssertionError("Expected: " + expected + ", but got: " + actual);
}
}
void assertEquals(String expected, String actual) {
if (!Objects.equals(expected, actual)) {
throw new AssertionError("Expected: " + expected + ", but got: " + actual);
}
}
void assertTrue(boolean actual) {
if (!actual) {
throw new AssertionError("Expected: true, but got false");
}
}
void assertFalse(boolean actual) {
if (actual) {
throw new AssertionError("Expected: false, but got true");
}
}
public enum E {
A, B, C;
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8262891
* @summary Verify StackMapTable is sensible for simple ordinary switches
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* jdk.jdeps/com.sun.tools.javap
* @build toolbox.ToolBox toolbox.JavapTask
* @run compile OrdinarySwitchStackMapTest.java
* @run main OrdinarySwitchStackMapTest
*/
import java.nio.file.Path;
import java.nio.file.Paths;
import toolbox.JavapTask;
import toolbox.Task;
import toolbox.ToolBox;
public class OrdinarySwitchStackMapTest {
class Test {
void method0(int i) throws Exception {
switch (i) {
case 0: System.err.println(0); break;
case 1: System.err.println(1); break;
default: System.err.println(2); break;
}
}
}
public static void main(String args[]) throws Exception {
ToolBox tb = new ToolBox();
Path pathToClass = Paths.get(ToolBox.testClasses, "OrdinarySwitchStackMapTest$Test.class");
String javapOut = new JavapTask(tb)
.options("-v")
.classes(pathToClass.toString())
.run()
.getOutput(Task.OutputKind.DIRECT);
if (!javapOut.contains("StackMapTable: number_of_entries = 4"))
throw new AssertionError("The number of entries of the stack map "
+ "table should be equal to 4");
}
}

View File

@ -1,5 +1,6 @@
SwitchNoExtraTypes.java:11:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: boolean, int)
SwitchNoExtraTypes.java:17:16: compiler.err.prob.found.req: (compiler.misc.possible.loss.of.precision: long, int)
SwitchNoExtraTypes.java:23:16: compiler.err.prob.found.req: (compiler.misc.possible.loss.of.precision: float, int)
SwitchNoExtraTypes.java:29:16: compiler.err.prob.found.req: (compiler.misc.possible.loss.of.precision: double, int)
4 errors
SwitchNoExtraTypes.java:11:16: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)
SwitchNoExtraTypes.java:12:18: compiler.err.constant.label.not.compatible: boolean, boolean
SwitchNoExtraTypes.java:18:18: compiler.err.constant.label.not.compatible: int, long
SwitchNoExtraTypes.java:24:18: compiler.err.constant.label.not.compatible: int, float
SwitchNoExtraTypes.java:30:18: compiler.err.constant.label.not.compatible: int, double
5 errors

View File

@ -1,2 +1,4 @@
SwitchObject.java:10:16: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Object, int)
1 error
SwitchObject.java:10:16: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)
SwitchObject.java:11:18: compiler.err.constant.label.not.compatible: int, java.lang.Object
SwitchObject.java:12:18: compiler.err.constant.label.not.compatible: java.lang.String, java.lang.Object
3 errors

View File

@ -0,0 +1,139 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8262891
* @summary Verify "case null" behavior.
* @compile --enable-preview -source ${jdk.version} SwitchNull.java
* @run main/othervm --enable-preview SwitchNull
*/
public class SwitchNull {
public static void main(String... args) {
SwitchNull instance = new SwitchNull();
instance.run();
}
private void run() {
assertEquals(0, switchIntegerBox(Integer.MIN_VALUE));
assertEquals(1, switchIntegerBox(-2));
assertEquals(2, switchIntegerBox(-1));
assertEquals(3, switchIntegerBox(0));
assertEquals(4, switchIntegerBox(1));
assertEquals(5, switchIntegerBox(2));
assertEquals(6, switchIntegerBox(Integer.MAX_VALUE));
assertEquals(-1, switchIntegerBox(null));
assertEquals(-2, switchIntegerBox(3));
assertEquals(0, switchString(""));
assertEquals(1, switchString("a"));
assertEquals(2, switchString("A"));
assertEquals(-1, switchString(null));
assertEquals(-2, switchString("c"));
assertEquals(0, switchEnum(E.A));
assertEquals(1, switchEnum(E.B));
assertEquals(2, switchEnum(E.C));
assertEquals(-1, switchEnum(null));
assertEquals(0, switchEnumWithDefault(E.A));
assertEquals(1, switchEnumWithDefault(E.B));
assertEquals(1, switchEnumWithDefault(E.C));
assertEquals(-1, switchEnumWithDefault(null));
testSwitchIntegerBoxExhaustive();
}
private int switchIntegerBox(Integer i) {
switch (i) {
case Integer.MIN_VALUE: return 0;
case -2: return 1;
case -1: return 2;
case 0: return 3;
case 1: return 4;
case 2: return 5;
case Integer.MAX_VALUE: return 6;
case null: return -1;
default: return -2;
}
}
private int switchString(String s) {
switch (s) {
case "": return 0;
case "a": return 1;
case "A": return 2;
case null: return -1;
default: return -2;
}
}
private int switchEnum(E e) {
switch (e) {
case A: return 0;
case B: return 1;
case C: return 2;
case null: return -1;
}
throw new AssertionError(String.valueOf(e));
}
private int switchEnumWithDefault(E e) {
switch (e) {
case A: return 0;
default: return 1;
case null: return -1;
}
}
private void testSwitchIntegerBoxExhaustive() {
int i = Integer.MIN_VALUE;
do {
int result = switchIntegerBoxExhaustive(i);
int expected = i == 0 ? 0 : 1;
assertEquals(expected, result);
} while (i++ < Integer.MAX_VALUE);
int result = switchIntegerBoxExhaustive(null);
assertEquals(-1, result);
}
private int switchIntegerBoxExhaustive(Integer i) {
return switch (i) {
case null -> -1;
case 0 -> 0;
default -> 1;
};
}
enum E {
A,
B,
C;
}
private void assertEquals(int expected, int actual) {
if (expected != actual) {
throw new AssertionError("Expected: " + expected + ", actual: " + actual);
}
}
}

View File

@ -0,0 +1,2 @@
SwitchNullDisabled.java:13:18: compiler.err.preview.feature.disabled: (compiler.misc.feature.case.null)
1 error

View File

@ -1,8 +1,10 @@
/*
* @test /nodynamiccopyright/
* @bug 8206986
* @summary Verify "case null" is not allowed.
* @compile/fail/ref=SwitchNullDisabled.out -XDrawDiagnostics SwitchNullDisabled.java
* @summary Verify "case null" is not allowed for -source 16
* @compile/fail/ref=SwitchNullDisabled.out -XDrawDiagnostics -source 16 -Xlint:-options SwitchNullDisabled.java
* @compile/fail/ref=SwitchNullDisabled-preview.out -XDrawDiagnostics SwitchNullDisabled.java
* @compile --enable-preview -source ${jdk.version} SwitchNullDisabled.java
*/
public class SwitchNullDisabled {

View File

@ -1,2 +1,2 @@
SwitchNullDisabled.java:11:18: compiler.err.switch.null.not.allowed
SwitchNullDisabled.java:13:18: compiler.err.preview.feature.disabled: (compiler.misc.feature.case.null)
1 error

View File

@ -95,6 +95,8 @@ public class SourceTreeScannerTest extends AbstractTreeScannerTest {
private class ScanTester extends TreeScanner<Void,Void> {
/** Main entry method for the class. */
int test(JCCompilationUnit tree) {
if (!tree.sourcefile.toString().contains("EmptyBreak.java"))
return 0;
sourcefile = tree.sourcefile;
found = new HashSet<Tree>();
scan(tree, null);
@ -143,28 +145,30 @@ public class SourceTreeScannerTest extends AbstractTreeScannerTest {
if (o instanceof JCTree) {
JCTree tree = (JCTree) o;
//System.err.println("EXPECT: " + tree.getKind() + " " + trim(tree, 64));
expect.add(tree);
for (Field f: getFields(tree)) {
if (TypeBoundKind.class.isAssignableFrom(f.getType())) {
// not part of public API
continue;
}
try {
//System.err.println("FIELD: " + f.getName());
if (tree instanceof JCModuleDecl && f.getName().equals("mods")) {
// The modifiers will not found by TreeScanner,
// but the embedded annotations will be.
reflectiveScan(((JCModuleDecl) tree).mods.annotations);
} else if (tree instanceof JCCase &&
((JCCase) tree).getCaseKind() == CaseKind.RULE &&
f.getName().equals("stats")) {
//value case, visit value:
reflectiveScan(((JCCase) tree).getBody());
} else {
reflectiveScan(f.get(tree));
if (!tree.hasTag(JCTree.Tag.DEFAULTCASELABEL)) {
expect.add(tree);
for (Field f: getFields(tree)) {
if (TypeBoundKind.class.isAssignableFrom(f.getType())) {
// not part of public API
continue;
}
try {
//System.err.println("FIELD: " + f.getName());
if (tree instanceof JCModuleDecl && f.getName().equals("mods")) {
// The modifiers will not found by TreeScanner,
// but the embedded annotations will be.
reflectiveScan(((JCModuleDecl) tree).mods.annotations);
} else if (tree instanceof JCCase &&
((JCCase) tree).getCaseKind() == CaseKind.RULE &&
f.getName().equals("stats")) {
//value case, visit value:
reflectiveScan(((JCCase) tree).getBody());
} else {
reflectiveScan(f.get(tree));
}
} catch (IllegalAccessException e) {
error(e.toString());
}
} catch (IllegalAccessException e) {
error(e.toString());
}
}
} else if (o instanceof List) {

View File

@ -92,6 +92,7 @@ public class ListModuleDeps {
public Object[][] jdkModules() {
return new Object[][]{
{"jdk.compiler", new String[]{
"java.base/jdk.internal.javac",
"java.base/jdk.internal.jmod",
"java.base/jdk.internal.misc",
"java.base/sun.reflect.annotation",