changeset 60090:f358ec606422 sealed-types

refactoring: relocating checks to attribution phase
author vromero
date Thu, 27 Feb 2020 22:56:46 -0500
parents 02506a5c9735
children 67525d425cf7 a631c875d3bc
files src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java test/langtools/tools/javac/sealed/SealedCompilationTests.java
diffstat 4 files changed, 85 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java	Thu Feb 27 13:49:58 2020 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java	Thu Feb 27 22:56:46 2020 -0500
@@ -26,10 +26,7 @@
 package com.sun.tools.javac.code;
 
 import java.lang.annotation.Annotation;
-import java.util.ArrayDeque;
-import java.util.Collections;
-import java.util.EnumMap;
-import java.util.Map;
+import java.util.*;
 
 import javax.lang.model.type.*;
 
@@ -42,6 +39,7 @@
 import com.sun.tools.javac.jvm.PoolConstant;
 import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.util.DefinedBy.Api;
+import com.sun.tools.javac.util.List;
 
 import static com.sun.tools.javac.code.BoundKind.*;
 import static com.sun.tools.javac.code.Flags.*;
@@ -978,6 +976,10 @@
          */
         public List<Type> permitted;
 
+        /** The class, or interfaces, that are sealed supertypes of this class or interface
+         */
+        public java.util.Set<Type> sealedSupers = Set.of();
+
         public boolean isPermittedExplicit = false;
 
         public boolean hasSealedSuperInSameCU;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Feb 27 13:49:58 2020 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Feb 27 22:56:46 2020 -0500
@@ -27,6 +27,7 @@
 
 import java.util.*;
 import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
 
 import javax.lang.model.element.ElementKind;
 import javax.tools.JavaFileObject;
@@ -5032,6 +5033,9 @@
                     if (subType.getTag() == TYPEVAR) {
                         log.error(TreeInfo.declarationFor(subType.tsym, env.tree), Errors.TypeVarListedInPermits);
                     }
+                    if (subType.tsym.isAnonymous() && !c.isEnum()) {
+                        log.error(TreeInfo.declarationFor(subType.tsym, env.tree), Errors.CantInheritFromSealed(c));
+                    }
                     if (permittedTypes.contains(subType)) {
                         log.error(TreeInfo.declarationFor(subType.tsym, env.tree), Errors.DuplicatedTypeInPermits(subType));
                     } else {
@@ -5060,6 +5064,49 @@
                 }
             }
 
+            ClassType ct = (ClassType)c.type;
+
+            if (!ct.sealedSupers.isEmpty() && c.isLocal() && !c.isEnum()) {
+                log.error(TreeInfo.declarationFor(c, env.tree), Errors.LocalClassesCantExtendSealed);
+            }
+
+            if (!ct.sealedSupers.isEmpty()) {
+                for (Type supertype : ct.sealedSupers) {
+                    if (!((ClassType)supertype).permitted.map(t -> t.tsym).contains(c.type.tsym)) {
+                        if (((ClassType)supertype.tsym.type).isPermittedExplicit) {
+                            log.error(TreeInfo.declarationFor(c.type.tsym, env.tree), Errors.CantInheritFromSealed(supertype.tsym));
+                        }
+                    }
+                }
+                if (!isNonSealed(c) && !isFinal(c) && !isSealed(c)) {
+                    log.error(TreeInfo.declarationFor(c, env.tree), Errors.NonSealedSealedOrFinalExpected);
+                }
+
+                if (!ct.hasSealedSuperInSameCU) {
+                    // that supertype most have a permits clause allowing this class to extend it
+                    List<Type> closureOutsideOfSameCU = types.closure(c.type).stream()
+                            .filter(supertype ->
+                                    TreeInfo.declarationFor(supertype.tsym, env.toplevel) == null ||
+                                            TreeInfo.declarationFor(c.outermostClass(), env.toplevel) == null)
+                            .collect(List.collector());
+                    Set<Type> explicitlySealedSuperTypesOutsideOfCU = closureOutsideOfSameCU.stream()
+                            .filter(type -> type != c.type && type.tsym.isSealed()).collect(Collectors.toSet());
+                    for (Type supertype : explicitlySealedSuperTypesOutsideOfCU) {
+                        if (!((ClassType)supertype).permitted.map(t -> t.tsym).contains(c.type.tsym)) {
+                            log.error(TreeInfo.declarationFor(c, env.tree), Errors.CantInheritFromSealed(supertype.tsym));
+                        }
+                    }
+
+                    if (!isNonSealed(c) && !isFinal(c) && !isSealed(c)) {
+                        log.error(TreeInfo.declarationFor(c, env.tree), Errors.NonSealedSealedOrFinalExpected);
+                    }
+                }
+            }
+
+            if ((c.flags_field & Flags.NON_SEALED) != 0 && ct.sealedSupers.isEmpty()) {
+                log.error(TreeInfo.declarationFor(c, env.tree), Errors.NonSealedWithNoSealedSupertype);
+            }
+
             // The info.lint field in the envs stored in typeEnvs is deliberately uninitialized,
             // because the annotations were not available at the time the env was created. Therefore,
             // we look up the environment chain for the first enclosing environment for which the
@@ -5110,6 +5157,10 @@
         }
     }
 
+    boolean isNonSealed(Symbol sym) { return sym != null && (sym.flags_field & NON_SEALED) != 0; }
+    boolean isFinal(Symbol sym) { return sym != null && (sym.flags_field & FINAL) != 0; }
+    boolean isSealed(Symbol sym) { return sym != null && (sym.flags_field & SEALED) != 0; }
+
     public void visitImport(JCImport tree) {
         // nothing to do
     }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Thu Feb 27 13:49:58 2020 -0500
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Thu Feb 27 22:56:46 2020 -0500
@@ -844,58 +844,27 @@
 
                 boolean anySuperInSameCUIsSealed = !explicitlySealedSuperTypesInCU.isEmpty();
                 if (anySuperInSameCUIsSealed) {
-                    java.util.List<Type> potentiallySealedSuperTypes = superTypesInASealedHierarchy(tree.sym, env, true);
+                    java.util.Set<Type> potentiallySealedSuperTypes = superTypesInASealedHierarchy(tree.sym, env, true);
                     if (!potentiallySealedSuperTypes.isEmpty()) {
                         for (Type supertype : potentiallySealedSuperTypes) {
                             if (!((ClassType)supertype).permitted.map(t -> t.tsym).contains(tree.sym.type.tsym)) {
                                 if (!((ClassType)supertype.tsym.type).isPermittedExplicit) {
-                                    if (tree.sym.isAnonymous() && !tree.sym.isEnum()) {
-                                        log.error(findTreeReferringSym(tree, supertype.tsym), Errors.CantInheritFromSealed(supertype.tsym));
-                                    } else {
+                                    if (!tree.sym.isAnonymous() || tree.sym.isEnum()) {
                                         ((ClassType)supertype).permitted = ((ClassType)supertype).permitted.append(tree.sym.type);
                                         ((ClassType)tree.sym.type).hasSealedSuperInSameCU = true;
                                     }
-                                } else {
-                                    log.error(findTreeReferringSym(tree, supertype.tsym), Errors.CantInheritFromSealed(supertype.tsym));
                                 }
                             } else {
                                 ((ClassType)tree.sym.type).hasSealedSuperInSameCU = true;
                             }
                         }
                     }
-
-                    if (!isNonSealed(tree.sym) && !isFinal(tree.sym) && !isSealed(tree.sym)) {
-                        log.error(tree, Errors.NonSealedSealedOrFinalExpected);
-                    }
                 }
 
-                boolean hasSuperTypesInSealedHierarchy = !superTypesInASealedHierarchy(tree.sym, env, false).isEmpty();
-                if ((tree.sym.flags_field & Flags.NON_SEALED) != 0 && !hasSuperTypesInSealedHierarchy) {
-                    log.error(tree, Errors.NonSealedWithNoSealedSupertype);
-                }
-
-                if (hasSuperTypesInSealedHierarchy && tree.sym.isLocal() && !tree.sym.isEnum()) {
-                    log.error(tree, Errors.LocalClassesCantExtendSealed);
-                }
-
-                if (hasSuperTypesInSealedHierarchy && !anySuperInSameCUIsSealed) {
-                    // that supertype most have a permits clause allowing this class to extend it
-                    List<Type> closureOutsideOfSameCU = types.closure(tree.sym.type).stream()
-                            .filter(supertype ->
-                                    TreeInfo.declarationFor(supertype.tsym, env.toplevel) == null ||
-                                            TreeInfo.declarationFor(tree.sym.outermostClass(), env.toplevel) == null)
-                            .collect(List.collector());
-                    Set<Type> explicitlySealedSuperTypesOutsideOfCU = closureOutsideOfSameCU.stream()
-                            .filter(type -> type != tree.sym.type && type.tsym.isSealed()).collect(Collectors.toSet());
-                    for (Type supertype : explicitlySealedSuperTypesOutsideOfCU) {
-                        if (!((ClassType)supertype).permitted.map(t -> t.tsym).contains(tree.sym.type.tsym)) {
-                            log.error(tree, Errors.CantInheritFromSealed(supertype.tsym));
-                        }
-                    }
-
-                    if (!isNonSealed(tree.sym) && !isFinal(tree.sym) && !isSealed(tree.sym)) {
-                        log.error(tree, Errors.NonSealedSealedOrFinalExpected);
-                    }
+                java.util.Set<Type> sealedSupers = superTypesInASealedHierarchy(tree.sym, env, false);
+                boolean hasSuperTypesInSealedHierarchy = !sealedSupers.isEmpty();
+                if (hasSuperTypesInSealedHierarchy) {
+                    ((ClassType)tree.sym.type).sealedSupers = sealedSupers;
                 }
             }
         }
@@ -921,14 +890,14 @@
         boolean isFinal(Symbol sym) { return sym != null && (sym.flags_field & FINAL) != 0; }
         boolean isSealed(Symbol sym) { return sym != null && (sym.flags_field & SEALED) != 0; }
 
-        java.util.List<Type> superTypesInASealedHierarchy(ClassSymbol csym, Env<AttrContext> env, boolean inSameCUOnly) {
+        java.util.Set<Type> superTypesInASealedHierarchy(ClassSymbol csym, Env<AttrContext> env, boolean inSameCUOnly) {
             if (csym == null) {
-                return null;
+                return Set.of();
             }
 
             Type supertype = csym.type != null ?
                     types.supertype(csym.type) : null;
-            java.util.List<Type> supertypes = new ArrayList<>();
+            java.util.Set<Type> supertypes = new HashSet<>();
 
             if (supertype != null &&
                     supertype.tsym != null &&
@@ -949,7 +918,7 @@
 
             for (Type sup : new ArrayList<>(supertypes)) {
                 if (sup.tsym instanceof ClassSymbol) {
-                    java.util.List<Type> supers = superTypesInASealedHierarchy((ClassSymbol)sup.tsym, env, inSameCUOnly);
+                    java.util.Set<Type> supers = superTypesInASealedHierarchy((ClassSymbol)sup.tsym, env, inSameCUOnly);
                     if ((supers == null || supers.isEmpty()) && !sup.tsym.isSealed()) {
                         supertypes.remove(sup);
                     }
--- a/test/langtools/tools/javac/sealed/SealedCompilationTests.java	Thu Feb 27 13:49:58 2020 -0500
+++ b/test/langtools/tools/javac/sealed/SealedCompilationTests.java	Thu Feb 27 22:56:46 2020 -0500
@@ -229,13 +229,25 @@
 
     public void testAnonymousAndLambdaCantExtendSealed() {
         for (String s : List.of(
-                "sealed interface I1 extends Runnable {\n" +
-                "    public static I1 i = () -> {};\n" +
-                "}",
-                "sealed interface I2 extends Runnable {\n" +
-                "    public static void foo() { new I2() { public void run() { } }; }\n" +
-                "}"))
+                """
+                sealed interface I1 extends Runnable {
+                    public static I1 i = () -> {};
+                }
+                """
+                ))
             assertFail("compiler.err.cant.inherit.from.sealed", s);
+
+        for (String s : List.of(
+                """
+                sealed interface I2 extends Runnable {
+                    public static void foo() { new I2() { public void run() { } }; }
+                }
+                final class C implements I2 {
+                    @Override public void run() {}
+                }
+                """
+                ))
+                assertFail("compiler.err.local.classes.cant.extend.sealed", s);
     }
 
     public void testNoLocalSealedClasses() {