changeset 52351:d5bad2570dd7 patterns

Handling of switch expressions with patterns.
author jlahoda
date Fri, 31 Aug 2018 13:50:57 +0200
parents bc6e1e4b584d
children 423d48955408
files src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransSwitches.java src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java test/langtools/tools/javac/patterns/SwitchExpressionWithPatterns.java
diffstat 9 files changed, 361 insertions(+), 131 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Aug 31 08:11:40 2018 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Aug 31 13:50:57 2018 +0200
@@ -1597,7 +1597,7 @@
                 }
 
                 flow.aliveAfterCase(caseEnv, c, make);
-                prevBindings = c.alive ? matchBindings : null;
+                prevBindings = c.completesNormally ? matchBindings : null;
             }
         } finally {
             switchEnv.info.scope.leave();
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java	Fri Aug 31 08:11:40 2018 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CompileStates.java	Fri Aug 31 13:50:57 2018 +0200
@@ -60,9 +60,10 @@
         FLOW(5),
         TRANSTYPES(6),
         UNLAMBDA(7),
-        TRANSPATTERNS(8),
-        LOWER(9),
-        GENERATE(10);
+        TRANSSWITCHES(8),
+        TRANSPATTERNS(9),
+        LOWER(10),
+        GENERATE(11);
 
         CompileState(int value) {
             this.value = value;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java	Fri Aug 31 08:11:40 2018 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java	Fri Aug 31 13:50:57 2018 +0200
@@ -267,7 +267,7 @@
             CaseAliveAnalyzer analyzer = new CaseAliveAnalyzer();
 
             analyzer.analyzeTree(env, that.stats, make);
-            that.alive = analyzer.isAlive();
+            that.completesNormally = analyzer.isAlive();
         } finally {
             log.popDiagnosticHandler(diagHandler);
         }
@@ -728,7 +728,6 @@
                         }
                     }
                     scanStats(c.stats);
-                    c.completesNormally = alive;
                     if (alive && c.caseKind == JCCase.RULE) {
                         System.err.println("synthetic break: " + tree);
                         scanSyntheticBreak(make, tree);
@@ -780,7 +779,6 @@
                     }
                 }
                 scanStats(c.stats);
-                c.completesNormally = alive;
             }
             if ((constants == null || !constants.isEmpty()) && !hasDefault) {
                 log.error(tree, Errors.NotExhaustive);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Fri Aug 31 08:11:40 2018 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Fri Aug 31 13:50:57 2018 +0200
@@ -63,6 +63,7 @@
 import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
 import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT;
 import com.sun.tools.javac.tree.JCTree.GenericSwitch.SwitchKind;
+import com.sun.tools.javac.tree.JCTree.JCSwitchExpression;
 import static com.sun.tools.javac.tree.JCTree.Tag.*;
 
 
@@ -3357,45 +3358,6 @@
     }
 
     public void visitSwitch(JCSwitch tree) {
-        //expand multiple label cases:
-        ListBuffer<JCCase> cases = new ListBuffer<>();
-
-        for (JCCase c : tree.cases) {
-            switch (c.pats.size()) {
-                case 0: //default
-                case 1: //single label
-                    cases.append(c);
-                    break;
-                default: //multiple labels, expand:
-                    //case C1, C2, C3: ...
-                    //=>
-                    //case C1:
-                    //case C2:
-                    //case C3: ...
-                    List<JCPattern> patterns = c.pats;
-                    while (patterns.tail.nonEmpty()) {
-                        cases.append(make_at(c.pos()).Case(JCCase.STATEMENT,
-                                                           List.of(patterns.head),
-                                                           List.nil(),
-                                                           null));
-                        patterns = patterns.tail;
-                    }
-                    c.pats = patterns;
-                    cases.append(c);
-                    break;
-            }
-        }
-
-        for (JCCase c : cases) {
-            if (c.caseKind == JCCase.RULE && c.completesNormally) {
-                JCBreak b = make_at(c.pos()).Break(null);
-                b.target = tree;
-                c.stats = c.stats.append(b);
-            }
-        }
-
-        tree.cases = cases.toList();
-
         Type target;
         switch (tree.kind) {
             case ENUM: target = tree.selector.type; break;
@@ -3630,74 +3592,8 @@
 
     @Override
     public void visitSwitchExpression(JCSwitchExpression tree) {
-        //translates switch expression to statement switch:
-        //switch (selector) {
-        //    case C: break value;
-        //    ...
-        //}
-        //=>
-        //(letexpr T exprswitch$;
-        //         switch (selector) {
-        //             case C: { exprswitch$ = value; break; }
-        //         }
-        //         exprswitch$
-        //)
-        VarSymbol dollar_switchexpr = new VarSymbol(Flags.FINAL|Flags.SYNTHETIC,
-                           names.fromString("exprswitch" + tree.pos + target.syntheticNameChar()),
-                           tree.type,
-                           currentMethodSym);
-
-        ListBuffer<JCStatement> stmtList = new ListBuffer<>();
-
-        stmtList.append(make.at(tree.pos()).VarDef(dollar_switchexpr, null).setType(dollar_switchexpr.type));
-        JCSwitch switchStatement = make.Switch(tree.selector, null);
-        switchStatement.kind = tree.kind;
-        switchStatement.cases =
-                tree.cases.stream()
-                          .map(c -> convertCase(dollar_switchexpr, switchStatement, tree, c))
-                          .collect(List.collector());
-        if (tree.cases.stream().noneMatch(c -> c.pats.isEmpty())) {
-            JCThrow thr = make.Throw(makeNewClass(syms.incompatibleClassChangeErrorType,
-                                                  List.nil()));
-            JCCase c = make.Case(JCCase.STATEMENT, List.nil(), List.of(thr), null);
-            switchStatement.cases = switchStatement.cases.append(c);
-        }
-
-        stmtList.append(translate(switchStatement));
-
-        result = make.LetExpr(stmtList.toList(), make.Ident(dollar_switchexpr))
-                     .setType(dollar_switchexpr.type);
+        Assert.error("Shuld not get here!");
     }
-        //where:
-        private JCCase convertCase(VarSymbol dollar_switchexpr, JCSwitch switchStatement,
-                                   JCSwitchExpression switchExpr, JCCase c) {
-            make.at(c.pos());
-            ListBuffer<JCStatement> statements = new ListBuffer<>();
-            statements.addAll(new TreeTranslator() {
-                @Override
-                public void visitLambda(JCLambda tree) {}
-                @Override
-                public void visitClassDef(JCClassDecl tree) {}
-                @Override
-                public void visitMethodDef(JCMethodDecl tree) {}
-                @Override
-                public void visitBreak(JCBreak tree) {
-                    if (tree.target == switchExpr) {
-                        tree.target = switchStatement;
-                        JCExpressionStatement assignment =
-                                make.Exec(make.Assign(make.Ident(dollar_switchexpr),
-                                                      translate(tree.value))
-                                              .setType(dollar_switchexpr.type));
-                        result = make.Block(0, List.of(assignment,
-                                                       tree));
-                        tree.value = null;
-                    } else {
-                        result = tree;
-                    }
-                }
-            }.translate(c.stats));
-            return make.Case(JCCase.STATEMENT, c.pats, statements.toList(), null);
-        }
 
     public void visitNewArray(JCNewArray tree) {
         tree.elemtype = translate(tree.elemtype);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java	Fri Aug 31 08:11:40 2018 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java	Fri Aug 31 13:50:57 2018 +0200
@@ -289,24 +289,23 @@
                 List<JCStatement> resultStatements = List.of(fallthroughInit);
 
                 for (JCCase clause : tree.cases) {
-                    List<JCExpression> matches = clause.pats.isEmpty() ? List.of(make.Literal(BOOLEAN, 1)) : clause.pats.stream().map(pat -> make.PatternTest(tree.selector, pat)).collect(List.collector());
-                    for (JCExpression jcMatches : matches) {
-                        jcMatches.setType(syms.booleanType);
-                        JCStatement body;
-                        List<JCStatement> stats = clause.stats;
-                        if (clause.alive) {
-                            stats = stats.append(make.at(tree.pos).Exec(make.Assign(make.Ident(fallthroughSym), make.Literal(BOOLEAN, 1).setType(syms.booleanType)).setType(syms.booleanType)));
-                        }
-                        body = make.Block(0, stats);
-                        JCStatement translatedIf = translate(make.If(jcMatches, body, null));
-                        JCIf testStatement = translatedIf.hasTag(Tag.IF) ? (JCIf)translatedIf : (JCIf) ((JCBlock)translatedIf).stats.tail.head;
+                    Assert.check(clause.pats.size() <= 1);
+                    final JCExpression jcMatches = clause.pats.nonEmpty() ? make.PatternTest(tree.selector, clause.pats.head) : make.Literal(BOOLEAN, 1);
+                    jcMatches.setType(syms.booleanType);
+                    JCStatement body;
+                    List<JCStatement> stats = clause.stats;
+                    if (clause.completesNormally) {
+                        stats = stats.append(make.at(tree.pos).Exec(make.Assign(make.Ident(fallthroughSym), make.Literal(BOOLEAN, 1).setType(syms.booleanType)).setType(syms.booleanType)));
+                    }
+                    body = make.Block(0, stats);
+                    JCStatement translatedIf = translate(make.If(jcMatches, body, null));
+                    JCIf testStatement = translatedIf.hasTag(Tag.IF) ? (JCIf)translatedIf : (JCIf) ((JCBlock)translatedIf).stats.tail.head;
 
-                        testStatement.cond = makeBinary(Tag.OR,
-                                make.Ident(fallthroughSym),
-                                testStatement.cond);
+                    testStatement.cond = makeBinary(Tag.OR,
+                            make.Ident(fallthroughSym),
+                            testStatement.cond);
 
-                        resultStatements = resultStatements.append(translatedIf);
-                    }
+                    resultStatements = resultStatements.append(translatedIf);
                 }
                 pendingMatchLabel.body = make.Block(0, resultStatements);
                 result = pendingMatchLabel;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransSwitches.java	Fri Aug 31 13:50:57 2018 +0200
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2018, 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.tools.javac.comp;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.jvm.Target;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCBreak;
+import com.sun.tools.javac.tree.JCTree.JCCase;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
+import com.sun.tools.javac.tree.JCTree.JCLambda;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCNewClass;
+import com.sun.tools.javac.tree.JCTree.JCPattern;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCSwitch;
+import com.sun.tools.javac.tree.JCTree.JCSwitchExpression;
+import com.sun.tools.javac.tree.JCTree.JCThrow;
+import com.sun.tools.javac.tree.TreeInfo;
+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.JCDiagnostic.DiagnosticPosition;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Names;
+
+/**
+ * This pass translates JDK12 switch constructs, like cases with multiple patterns, rule switches
+ * and switch expressions.
+ */
+public class TransSwitches extends TreeTranslator {
+
+    protected static final Context.Key<TransSwitches> transSwitchesKey = new Context.Key<>();
+
+    public static TransSwitches instance(Context context) {
+        TransSwitches instance = context.get(transSwitchesKey);
+        if (instance == null)
+            instance = new TransSwitches(context);
+        return instance;
+    }
+
+    private Symtab syms;
+    private Resolve rs;
+    private Names names;
+    private TypeEnvs typeEnvs;
+    private Target target;
+
+    /** The current method symbol.
+     */
+    private MethodSymbol currentMethodSym;
+
+    /** Environment for symbol lookup, set by translateTopLevelClass.
+     */
+    private Env<AttrContext> attrEnv;
+
+    private TreeMaker make;
+
+    public TransSwitches(Context context) {
+        context.put(transSwitchesKey, this);
+        syms = Symtab.instance(context);
+        rs = Resolve.instance(context);
+        names = Names.instance(context);
+        typeEnvs = TypeEnvs.instance(context);
+        target = Target.instance(context);
+    }
+
+    public void visitSwitch(JCSwitch tree) {
+        //expand multiple label cases:
+        ListBuffer<JCCase> cases = new ListBuffer<>();
+
+        for (JCCase c : tree.cases) {
+            switch (c.pats.size()) {
+                case 0: //default
+                case 1: //single label
+                    cases.append(c);
+                    break;
+                default: //multiple labels, expand:
+                    //case C1, C2, C3: ...
+                    //=>
+                    //case C1:
+                    //case C2:
+                    //case C3: ...
+                    List<JCPattern> patterns = c.pats;
+                    while (patterns.tail.nonEmpty()) {
+                        JCCase cse = make_at(c.pos()).Case(JCCase.STATEMENT,
+                                                           List.of(patterns.head),
+                                                           List.nil(),
+                                                           null);
+                        cse.completesNormally = true;
+                        cases.append(cse);
+                        patterns = patterns.tail;
+                    }
+                    c.pats = patterns;
+                    cases.append(c);
+                    break;
+            }
+        }
+
+        for (JCCase c : cases) {
+            if (c.caseKind == JCCase.RULE && c.completesNormally) {
+                JCBreak b = make_at(c.pos()).Break(null);
+                b.target = tree;
+                c.stats = c.stats.append(b);
+            }
+        }
+
+        tree.selector = translate(tree.selector);
+        tree.cases = translateCases(cases.toList());
+
+        result = tree;
+    }
+
+    @Override
+    public void visitSwitchExpression(JCSwitchExpression tree) {
+        //translates switch expression to statement switch:
+        //switch (selector) {
+        //    case C: break value;
+        //    ...
+        //}
+        //=>
+        //(letexpr T exprswitch$;
+        //         switch (selector) {
+        //             case C: { exprswitch$ = value; break; }
+        //         }
+        //         exprswitch$
+        //)
+        VarSymbol dollar_switchexpr = new VarSymbol(Flags.FINAL|Flags.SYNTHETIC,
+                           names.fromString("exprswitch" + tree.pos + target.syntheticNameChar()),
+                           tree.type,
+                           currentMethodSym);
+
+        ListBuffer<JCStatement> stmtList = new ListBuffer<>();
+
+        stmtList.append(make.at(tree.pos()).VarDef(dollar_switchexpr, null).setType(dollar_switchexpr.type));
+        JCSwitch switchStatement = make.Switch(tree.selector, null);
+        switchStatement.kind = tree.kind;
+        switchStatement.cases =
+                tree.cases.stream()
+                          .map(c -> convertCase(dollar_switchexpr, switchStatement, tree, c))
+                          .collect(List.collector());
+        if (tree.cases.stream().noneMatch(c -> c.pats.isEmpty())) {
+            JCThrow thr = make.Throw(makeNewClass(syms.incompatibleClassChangeErrorType,
+                                                  List.nil()));
+            JCCase c = make.Case(JCCase.STATEMENT, List.nil(), List.of(thr), null);
+            switchStatement.cases = switchStatement.cases.append(c);
+        }
+
+        stmtList.append(translate(switchStatement));
+
+        result = make.LetExpr(stmtList.toList(), make.Ident(dollar_switchexpr))
+                     .setType(dollar_switchexpr.type);
+    }
+        //where:
+        private JCCase convertCase(VarSymbol dollar_switchexpr, JCSwitch switchStatement,
+                                   JCSwitchExpression switchExpr, JCCase c) {
+            make.at(c.pos());
+            ListBuffer<JCStatement> statements = new ListBuffer<>();
+            statements.addAll(new TreeTranslator() {
+                @Override
+                public void visitLambda(JCLambda tree) {}
+                @Override
+                public void visitClassDef(JCClassDecl tree) {}
+                @Override
+                public void visitMethodDef(JCMethodDecl tree) {}
+                @Override
+                public void visitBreak(JCBreak tree) {
+                    if (tree.target == switchExpr) {
+                        tree.target = switchStatement;
+                        JCExpressionStatement assignment =
+                                make.Exec(make.Assign(make.Ident(dollar_switchexpr),
+                                                      translate(tree.value))
+                                              .setType(dollar_switchexpr.type));
+                        result = make.Block(0, List.of(assignment,
+                                                       tree));
+                        tree.value = null;
+                    } else {
+                        result = tree;
+                    }
+                }
+            }.translate(c.stats));
+            JCCase res = make.Case(c.caseKind, c.pats, statements.toList(), null);
+            res.completesNormally = c.completesNormally;
+            return res;
+        }
+
+    public void visitClassDef(JCClassDecl tree) {
+        MethodSymbol currentMethodSymPrev = currentMethodSym;
+        Env<AttrContext> prevEnv = attrEnv;
+
+        try {
+            currentMethodSym = null;
+            attrEnv = typeEnvs.get(tree.sym);
+            if (attrEnv == null)
+                attrEnv = prevEnv;
+            super.visitClassDef(tree);
+        } finally {
+            attrEnv = prevEnv;
+            currentMethodSym = currentMethodSymPrev;
+        }
+    }
+
+    public void visitMethodDef(JCMethodDecl tree) {
+        MethodSymbol prevMethodSym = currentMethodSym;
+        try {
+            currentMethodSym = tree.sym;
+            super.visitMethodDef(tree);
+        } finally {
+            currentMethodSym = prevMethodSym;
+        }
+    }
+
+    public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
+        try {
+            this.make = make;
+            translate(cdef);
+        } finally {
+            // note that recursive invocations of this method fail hard
+            this.make = null;
+        }
+
+        return cdef;
+    }
+
+    //from Lower (probably generalize):
+    private DiagnosticPosition make_pos;
+
+    /** Equivalent to make.at(pos.getStartPosition()) with side effect of caching
+     *  pos as make_pos, for use in diagnostics.
+     **/
+    TreeMaker make_at(DiagnosticPosition pos) {
+        make_pos = pos;
+        return make.at(pos);
+    }
+
+    /** Make an attributed class instance creation expression.
+     *  @param ctype    The class type.
+     *  @param args     The constructor arguments.
+     */
+    JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
+        JCNewClass tree = make.NewClass(null,
+            null, make.QualIdent(ctype.tsym), args, null);
+        tree.constructor = rs.resolveConstructor(
+            make_pos, attrEnv, ctype, TreeInfo.types(args), List.nil());
+        tree.type = ctype;
+        return tree;
+    }
+}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Fri Aug 31 08:11:40 2018 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Fri Aug 31 13:50:57 2018 +0200
@@ -1561,7 +1561,13 @@
                 compileStates.put(env, CompileState.UNLAMBDA);
             }
 
-            if (shouldStop(CompileState.TRANSPATTERNS))
+            if (shouldStop(CompileState.TRANSSWITCHES))
+                return;
+
+            env.tree = TransSwitches.instance(context).translateTopLevelClass(env, env.tree, localMake);
+            compileStates.put(env, CompileState.TRANSSWITCHES);
+
+            if (shouldStop(CompileState.TRANSSWITCHES))
                 return;
 
             env.tree = TransPatterns.instance(context).translateTopLevelClass(env, env.tree, localMake);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Fri Aug 31 08:11:40 2018 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Fri Aug 31 13:50:57 2018 +0200
@@ -1278,7 +1278,6 @@
         public final CaseKind caseKind;
         public List<JCPattern> pats;
         public List<JCStatement> stats;
-        public boolean alive;
         public JCTree body;
         public boolean completesNormally;
         protected JCCase(@SuppressWarnings("removal") CaseKind caseKind, List<JCPattern> pats,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/patterns/SwitchExpressionWithPatterns.java	Fri Aug 31 13:50:57 2018 +0200
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2018, 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
+ * @summary Basic test for patterns in switch expression
+ * @compile --enable-preview -source 12 SwitchExpressionWithPatterns.java
+ * @run main/othervm --enable-preview SwitchExpressionWithPatterns
+ */
+public class SwitchExpressionWithPatterns {
+
+    public static void main(String[] args) {
+        assertEquals(1, test((Integer) 42));
+        assertEquals(2, test(41));
+        assertEquals(-2, test((long) 0));
+        assertEquals(-2, test((float) 0));
+        assertEquals(-1, test((byte) 13));
+    }
+
+    private static int test(Object in) {
+        int check = 0;
+        return switch (in) {
+            case 41: check++; //fall-through
+            case Integer j: check++; break check;
+            case Long l, Float f: break -2;
+            default: break -1;
+        };
+
+    }
+
+    private static void assertEquals(int expected, int actual) {
+        if (expected != actual)
+            throw new AssertionError("Expected: " + expected + ", actual: " + actual);
+    }
+}