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:
parent
3e48244084
commit
908aca29ca
@ -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/, \
|
||||
))
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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.
|
||||
*/
|
||||
|
@ -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 {}
|
@ -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.
|
||||
|
@ -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 {}
|
@ -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 {}
|
||||
|
@ -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();
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
@ -31,4 +31,4 @@ package com.sun.source.tree;
|
||||
*
|
||||
* @since 16
|
||||
*/
|
||||
public interface PatternTree extends Tree {}
|
||||
public interface PatternTree extends Tree, CaseLabelTree {}
|
||||
|
@ -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}.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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}.
|
||||
*
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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),
|
||||
;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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); }
|
||||
|
@ -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 ");
|
||||
|
@ -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;
|
||||
|
@ -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) {}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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())));
|
||||
}
|
||||
}
|
||||
|
133
test/jdk/java/lang/runtime/SwitchBootstrapsTest.java
Normal file
133
test/jdk/java/lang/runtime/SwitchBootstrapsTest.java
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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")
|
||||
};
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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:
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
};
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
35
test/langtools/tools/javac/diags/examples/PatternSwitch.java
Normal file
35
test/langtools/tools/javac/diags/examples/PatternSwitch.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
37
test/langtools/tools/javac/patterns/CaseDefault.java
Normal file
37
test/langtools/tools/javac/patterns/CaseDefault.java
Normal 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
|
||||
};
|
||||
}
|
||||
|
||||
}
|
2
test/langtools/tools/javac/patterns/CaseDefault.out
Normal file
2
test/langtools/tools/javac/patterns/CaseDefault.out
Normal file
@ -0,0 +1,2 @@
|
||||
CaseDefault.java:20:18: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.pattern.switch)
|
||||
1 error
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
89
test/langtools/tools/javac/patterns/Domination.java
Normal file
89
test/langtools/tools/javac/patterns/Domination.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
9
test/langtools/tools/javac/patterns/Domination.out
Normal file
9
test/langtools/tools/javac/patterns/Domination.out
Normal 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
|
643
test/langtools/tools/javac/patterns/Exhaustiveness.java
Normal file
643
test/langtools/tools/javac/patterns/Exhaustiveness.java
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
}
|
99
test/langtools/tools/javac/patterns/Guards.java
Normal file
99
test/langtools/tools/javac/patterns/Guards.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
42
test/langtools/tools/javac/patterns/GuardsErrors.java
Normal file
42
test/langtools/tools/javac/patterns/GuardsErrors.java
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
4
test/langtools/tools/javac/patterns/GuardsErrors.out
Normal file
4
test/langtools/tools/javac/patterns/GuardsErrors.out
Normal 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
|
168
test/langtools/tools/javac/patterns/NullSwitch.java
Normal file
168
test/langtools/tools/javac/patterns/NullSwitch.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
51
test/langtools/tools/javac/patterns/Parenthesized.java
Normal file
51
test/langtools/tools/javac/patterns/Parenthesized.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
131
test/langtools/tools/javac/patterns/SealedTypeChanges.java
Normal file
131
test/langtools/tools/javac/patterns/SealedTypeChanges.java
Normal 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 {}
|
26
test/langtools/tools/javac/patterns/SealedTypeChanges2.java
Normal file
26
test/langtools/tools/javac/patterns/SealedTypeChanges2.java
Normal 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 {}
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
193
test/langtools/tools/javac/patterns/SwitchErrors.java
Normal file
193
test/langtools/tools/javac/patterns/SwitchErrors.java
Normal 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 {}
|
||||
}
|
44
test/langtools/tools/javac/patterns/SwitchErrors.out
Normal file
44
test/langtools/tools/javac/patterns/SwitchErrors.out
Normal 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
|
344
test/langtools/tools/javac/patterns/Switches.java
Normal file
344
test/langtools/tools/javac/patterns/Switches.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
139
test/langtools/tools/javac/switchnull/SwitchNull.java
Normal file
139
test/langtools/tools/javac/switchnull/SwitchNull.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
SwitchNullDisabled.java:13:18: compiler.err.preview.feature.disabled: (compiler.misc.feature.case.null)
|
||||
1 error
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user