changeset 56794:424c4d694771 patterns-deconstruction

Support for deconstruction patterns in switches.
author jlahoda
date Fri, 14 Jun 2019 13:35:47 +0200
parents a9a1999af6da
children e6e27550e2d5
files src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java test/langtools/tools/javac/patterns/SwitchExpressionWithPatterns.java test/langtools/tools/javac/switchexpr/ExhaustiveSealedSwitch.java test/langtools/tools/javac/switchexpr/ExhaustiveSealedSwitchExtra.java
diffstat 6 files changed, 142 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Jun 14 13:26:15 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Jun 14 13:35:47 2019 +0200
@@ -1636,7 +1636,7 @@
                     addVars(c.stats, switchEnv.info.scope);
                 }
 
-                c.completesNormally = flow.aliveAfter(caseEnv, c, make);
+                c.completesNormally = caseKind != JCCase.RULE && flow.aliveAfter(caseEnv, c, make);
                 prevBindings = c.completesNormally ? matchBindings : null;
             }
         } finally {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java	Fri Jun 14 13:26:15 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java	Fri Jun 14 13:35:47 2019 +0200
@@ -51,9 +51,12 @@
 import static com.sun.tools.javac.code.Flags.*;
 import static com.sun.tools.javac.code.Flags.BLOCK;
 import static com.sun.tools.javac.code.Kinds.Kind.*;
+import com.sun.tools.javac.code.Type.ClassType;
 import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
+import static com.sun.tools.javac.code.TypeTag.CLASS;
 import static com.sun.tools.javac.code.TypeTag.VOID;
 import static com.sun.tools.javac.tree.JCTree.Tag.*;
+import java.util.Iterator;
 
 /** This pass implements dataflow analysis for Java programs though
  *  different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that
@@ -745,6 +748,9 @@
                                 case LITERALPATTERN: {
                                     break;  // does not dominate any other pattern.
                                 }
+                                case DECONSTRUCTIONPATTERN: {
+                                    break; //TODO: domination for deconstruction patterns
+                                }
                                 default: {
                                     Assert.check(false);
                                     break;
@@ -800,6 +806,10 @@
                     constants.add(s.name);
                 }
             }
+            Set<Type> permittedTypes = new HashSet<>();
+            if (tree.selector.type.hasTag(CLASS) && ((ClassType) tree.selector.type).permitted.nonEmpty()) {
+                ((ClassType) tree.selector.type).permitted.stream().forEach(permittedTypes::add);
+            }
             boolean hasDefault = false;
             Liveness prevAlive = alive;
             for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
@@ -817,6 +827,20 @@
                             if (lit.type != null)
                                 constants.remove(lit.type.constValue());
                         }
+                        if (permittedTypes != null) {
+                            if (pat.hasTag(BINDINGPATTERN)) {
+                                JCPattern bindingPattern = pat;
+                                Iterator<Type> permIt = permittedTypes.iterator();
+                                while (permIt.hasNext()) {
+                                    if (types.isSameType(permIt.next(), bindingPattern.type)) {
+                                        permIt.remove();
+                                        break;
+                                    }
+                                }
+                            } else {
+                                //TODO: deconstructor patterns?
+                            }
+                        }
                     }
                 }
                 scanStats(c.stats);
@@ -830,7 +854,7 @@
                     }
                 }
             }
-            if ((constants == null || !constants.isEmpty()) && !hasDefault) {
+            if ((constants == null || !constants.isEmpty()) && (permittedTypes == null || !permittedTypes.isEmpty()) && !hasDefault) {
                 log.error(tree, Errors.NotExhaustive);
             }
             alive = prevAlive;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java	Fri Jun 14 13:26:15 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java	Fri Jun 14 13:35:47 2019 +0200
@@ -306,11 +306,11 @@
             for (int i = 0; i < ((JCDeconstructionPattern) patt).getNestedPatterns().size(); i++) {
                 JCPattern nested = ((JCDeconstructionPattern) patt).getNestedPatterns().get(i);
                 params[i + 1] = preparePatternExtractor(nested, nested.type, nestedBindings[i] = new ListBuffer<>());
-                if (nested.hasTag(Tag.DECONSTRUCTIONPATTERN)) {
-                    bindingVars.append(syms.lengthVar);
-                } else {
+                if (nested.hasTag(Tag.BINDINGPATTERN)) {
                     bindingVars.appendList(nestedBindings[i].toList());
                     nestedBindings[i].clear();
+                } else {
+                    bindingVars.append(syms.lengthVar);
                 }
             }
             
@@ -622,10 +622,25 @@
                     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)));
+                    } else {
+                        JCBreak stop = make.at(tree.pos).Break(null);
+                        stop.target = pendingMatchLabel;
+                        stats = stats.append(stop);
                     }
                     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;
+                    JCIf testStatement = null;
+
+                    if (translatedIf.hasTag(Tag.IF)) {
+                        testStatement = (JCIf)translatedIf;
+                    } else {
+                        for (JCStatement st : ((JCBlock)translatedIf).stats) {
+                            if (st.hasTag(Tag.IF)) {
+                                testStatement = (JCIf) st;
+                                break;
+                            }
+                        }
+                    }
 
                     testStatement.cond = makeBinary(Tag.OR,
                             make.Ident(fallthroughSym),
--- a/test/langtools/tools/javac/patterns/SwitchExpressionWithPatterns.java	Fri Jun 14 13:26:15 2019 +0200
+++ b/test/langtools/tools/javac/patterns/SwitchExpressionWithPatterns.java	Fri Jun 14 13:35:47 2019 +0200
@@ -46,6 +46,8 @@
             assertEquals(3, f.applyAsInt((long) 0));
             assertEquals(3, f.applyAsInt((float) 0));
             assertEquals(4, f.applyAsInt((byte) 13));
+            assertEquals(105, f.applyAsInt(new R(0, 5)));
+            assertEquals(7, f.applyAsInt(new R(1, 6)));
         }
     }
 
@@ -55,6 +57,8 @@
             case 41: check++; //fall-through
             case Integer i: check++; break check;
             case Long l, Float f: break 3;
+            case R(0, var j): break 100 + j;
+            case R(var i, var j): break i + j;
             default: break 4;
         };
 
@@ -66,6 +70,8 @@
             case 41 -> 2;
             case Integer j -> { break 1; }
             case Long l, Float f -> 3;
+            case R(0, var j) -> 100 + j;
+            case R(var i, var j) -> i + j;
             default -> { break 4; }
         };
     }
@@ -76,6 +82,8 @@
             case 41: check++; //fall-through
             case Integer j: check++; break;
             case Long l, Float f: check = 3; break;
+            case R(0, var j): check = 100 + j; break;
+            case R(var i, var j): check = i + j; break;
             default: check = 4; break;
         }
         return check;
@@ -87,6 +95,8 @@
             case 41 -> check = 2;
             case Integer j -> { check = 1; }
             case Long l, Float f -> check = 3;
+            case R(0, var j) -> check = 100 + j;
+            case R(var i, var j) -> check = i + j;
             default -> { check = 4; }
         };
         return check;
@@ -96,4 +106,6 @@
         if (expected != actual)
             throw new AssertionError("Expected: " + expected + ", actual: " + actual);
     }
+
+    record R(int i, int j);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/switchexpr/ExhaustiveSealedSwitch.java	Fri Jun 14 13:35:47 2019 +0200
@@ -0,0 +1,59 @@
+/*
+ * 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
+ * @summary Verify that an switch expression over sealed type can be exhaustive without default.
+ * @compile --enable-preview -source ${jdk.version} ExhaustiveSealedSwitch.java
+ * @compile --enable-preview -source ${jdk.version} ExhaustiveSealedSwitchExtra.java
+ * @run main/othervm --enable-preview ExhaustiveSealedSwitch
+ */
+
+public class ExhaustiveSealedSwitch {
+    public static void main(String... args) throws Exception {
+        new ExhaustiveSealedSwitch().run();
+    }
+
+    private void run() throws Exception {
+        ExhaustiveSealedSwitchIntf i = (ExhaustiveSealedSwitchIntf) Class.forName("ExhaustiveSealedSwitchC").newInstance();
+
+        try {
+            print(i);
+            throw new AssertionError("Expected exception did not occur.");
+        } catch (IncompatibleClassChangeError err) {
+            //ok
+        }
+    }
+
+    private String print(ExhaustiveSealedSwitchIntf t) {
+        return switch (t) {
+            case ExhaustiveSealedSwitchA a -> "A";
+            case ExhaustiveSealedSwitchB b -> "B";
+        };
+    }
+
+}
+sealed interface ExhaustiveSealedSwitchIntf permits ExhaustiveSealedSwitchA, ExhaustiveSealedSwitchB {
+}
+class ExhaustiveSealedSwitchA implements ExhaustiveSealedSwitchIntf {}
+class ExhaustiveSealedSwitchB implements ExhaustiveSealedSwitchIntf {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/switchexpr/ExhaustiveSealedSwitchExtra.java	Fri Jun 14 13:35:47 2019 +0200
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+sealed interface ExhaustiveSealedSwitchIntf permits ExhaustiveSealedSwitchA, ExhaustiveSealedSwitchB, ExhaustiveSealedSwitchC {
+}
+class ExhaustiveSealedSwitchC implements ExhaustiveSealedSwitchIntf {}