Skip to content

Commit bd9b10a

Browse files
authored
Merge pull request scala#10348 from som-snytt/followup/12159-mixed-java-sealed
Support Java 17 `sealed` in Java sources
2 parents aa326fc + a0f546d commit bd9b10a

File tree

17 files changed

+195
-4
lines changed

17 files changed

+195
-4
lines changed

src/compiler/scala/tools/nsc/javac/JavaParsers.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package javac
1919
import symtab.Flags
2020
import JavaTokens._
2121
import scala.annotation._
22+
import scala.collection.mutable
2223
import scala.collection.mutable.ListBuffer
2324
import scala.language.implicitConversions
2425
import scala.reflect.internal.util.{ListOfNil, Position}
@@ -45,6 +46,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
4546

4647
abstract class JavaParser extends ParserCommon {
4748
val in: JavaScanner
49+
def unit: CompilationUnit
4850

4951
def freshName(prefix : String): Name
5052
protected implicit def i2p(offset : Int) : Position
@@ -494,7 +496,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
494496
in.nextToken()
495497
case _ =>
496498
val unsealed = 0L // no flag for UNSEALED
497-
def consume(added: FlagSet): false = { in.nextToken(); /*flags |= added;*/ false }
499+
def consume(added: FlagSet): false = { in.nextToken(); flags |= added; false }
498500
def lookingAhead(s: String): Boolean = {
499501
import scala.reflect.internal.Chars._
500502
var i = 0
@@ -838,6 +840,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
838840
else Nil
839841

840842
def classDecl(mods: Modifiers): List[Tree] = {
843+
if (mods.hasFlag(SEALED)) patmat.javaClassesByUnit(unit.source) = mutable.Set.empty
841844
accept(CLASS)
842845
val pos = in.currentPos
843846
val name = identForType()
@@ -904,6 +907,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
904907
}
905908

906909
def interfaceDecl(mods: Modifiers): List[Tree] = {
910+
if (mods.hasFlag(SEALED)) patmat.javaClassesByUnit(unit.source) = mutable.Set.empty
907911
accept(INTERFACE)
908912
val pos = in.currentPos
909913
val name = identForType()

src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
package scala.tools.nsc.transform.patmat
1414

1515
import scala.annotation.tailrec
16+
import scala.collection.mutable
1617
import scala.collection.mutable.ListBuffer
1718
import scala.reflect.internal.{Mode, Types}
18-
import scala.reflect.internal.util.Statistics
19+
import scala.reflect.internal.util.{SourceFile, Statistics}
1920
import scala.tools.nsc.Global
2021
import scala.tools.nsc.ast
2122
import scala.tools.nsc.transform.{Transform, TypingTransformers}
@@ -58,6 +59,9 @@ trait PatternMatching extends Transform
5859

5960
val phaseName: String = "patmat"
6061

62+
/** Symbols to force for determining children of sealed Java classes. */
63+
val javaClassesByUnit = perRunCaches.newMap[SourceFile, mutable.Set[Symbol]]()
64+
6165
def newTransformer(unit: CompilationUnit): AstTransformer = new MatchTransformer(unit)
6266

6367
class MatchTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {

src/compiler/scala/tools/nsc/typechecker/Namers.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,8 @@ trait Namers extends MethodSynthesis {
795795
tree.symbol = enterClassSymbol(tree)
796796
tree.symbol setInfo completerOf(tree)
797797

798+
if (tree.symbol.isJava) patmat.javaClassesByUnit.get(tree.symbol.pos.source).foreach(_.addOne(tree.symbol))
799+
798800
if (mods.isCase) {
799801
val m = ensureCompanionObject(tree, caseModuleDef)
800802
m.moduleClass.updateAttachment(new ClassForCaseCompanionAttachment(tree))

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,11 @@ package tools.nsc
1515
package typechecker
1616

1717
import scala.annotation.{tailrec, unused}
18-
import scala.collection.mutable
18+
import scala.collection.mutable, mutable.ListBuffer
1919
import scala.reflect.internal.{Chars, TypesStats}
2020
import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Statistics, StringContextStripMarginOps}
2121
import scala.tools.nsc.Reporting.{MessageFilter, Suppression, WConf, WarningCategory}
2222
import scala.util.chaining._
23-
import mutable.ListBuffer
2423
import symtab.Flags._
2524
import Mode._
2625

@@ -2653,6 +2652,31 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
26532652
val selectorTp = packCaptured(selector1.tpe.widen).skolemizeExistential(context.owner, selector)
26542653
val casesTyped = typedCases(cases, selectorTp, pt)
26552654

2655+
def initChildren(sym: Symbol): Unit =
2656+
if (sym.isJava && sym.isSealed)
2657+
sym.attachments.get[PermittedSubclassSymbols] match {
2658+
case Some(PermittedSubclassSymbols(permits)) =>
2659+
for (child <- permits if child.isJava)
2660+
initChildren(child.initialize)
2661+
case _ =>
2662+
val seen = mutable.HashSet.empty[Symbol]
2663+
def populate(): Unit =
2664+
patmat.javaClassesByUnit.get(sym.pos.source) match {
2665+
case Some(classes) =>
2666+
classes.find(!seen(_)) match {
2667+
case Some(unseen) =>
2668+
seen += unseen
2669+
unseen.initialize.companionSymbol.moduleClass.initialize
2670+
if (unseen.hasAttachment[PermittedSubclassSymbols]) initChildren(unseen)
2671+
populate()
2672+
case _ =>
2673+
}
2674+
case _ =>
2675+
}
2676+
populate()
2677+
}
2678+
initChildren(selectorTp.typeSymbol)
2679+
26562680
def finish(cases: List[CaseDef], matchType: Type) =
26572681
treeCopy.Match(tree, selector1, cases) setType matchType
26582682

test/files/neg/t12159d.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
t.scala:7: warning: match may not be exhaustive.
2+
It would fail on the following input: W()
3+
x match {
4+
^
5+
error: No warnings can be incurred under -Werror.
6+
1 warning
7+
1 error

test/files/neg/t12159d/X.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// javaVersion: 17+
2+
package p;
3+
4+
sealed abstract public class X {
5+
}
6+
7+
final class W extends X {
8+
}
9+
10+
final class Y extends X {
11+
}
12+
13+
final class Z extends X {
14+
}

test/files/neg/t12159d/t.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// javaVersion: 17+
2+
// scalac: -Werror
3+
package p
4+
5+
class C {
6+
def f(x: X) =
7+
x match {
8+
case y: Y => y.toString
9+
case z: Z => z.toString
10+
}
11+
}
12+

test/files/neg/t12159e.check

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
t.scala:7: warning: match may not be exhaustive.
2+
It would fail on the following input: W()
3+
x match {
4+
^
5+
t.scala:12: warning: match may not be exhaustive.
6+
It would fail on the following inputs: Z(), Z2()
7+
x match {
8+
^
9+
error: No warnings can be incurred under -Werror.
10+
2 warnings
11+
1 error

test/files/neg/t12159e/X.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// javaVersion: 17+
2+
package p;
3+
4+
sealed abstract public class X {
5+
}
6+
7+
final class W extends X {
8+
}
9+
10+
final class Y extends X {
11+
}
12+
13+
sealed class Z extends X permits Z1, Z2 {
14+
}
15+
16+
final class Z1 extends Z {
17+
}
18+
19+
final class Z2 extends Z {
20+
}

test/files/neg/t12159e/t.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// javaVersion: 17+
2+
// scalac: -Werror
3+
package p
4+
5+
class C {
6+
def f(x: X) =
7+
x match {
8+
case y: Y => y.toString
9+
case z: Z => z.toString
10+
}
11+
def g(x: X) =
12+
x match {
13+
case w: W => w.toString
14+
case y: Y => y.toString
15+
case z: Z1 => z.toString
16+
}
17+
}
18+

test/files/neg/t12159f.check

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
t.scala:7: warning: match may not be exhaustive.
2+
It would fail on the following input: W()
3+
x match {
4+
^
5+
t.scala:12: warning: match may not be exhaustive.
6+
It would fail on the following inputs: Z(), Z2()
7+
x match {
8+
^
9+
error: No warnings can be incurred under -Werror.
10+
2 warnings
11+
1 error

test/files/neg/t12159f/X.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// javaVersion: 17+
2+
package p;
3+
4+
sealed abstract public class X {
5+
}
6+
7+
final class W extends X {
8+
}
9+
10+
final class Y extends X {
11+
}
12+
13+
sealed class Z extends X permits Z1, Z2 {
14+
}

test/files/neg/t12159f/Z.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// javaVersion: 17+
2+
package p;
3+
4+
final class Z1 extends Z {
5+
}
6+
7+
final class Z2 extends Z {
8+
}

test/files/neg/t12159f/t.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// javaVersion: 17+
2+
// scalac: -Werror
3+
package p
4+
5+
class C {
6+
def f(x: X) =
7+
x match {
8+
case y: Y => y.toString
9+
case z: Z => z.toString
10+
}
11+
def g(x: X) =
12+
x match {
13+
case w: W => w.toString
14+
case y: Y => y.toString
15+
case z: Z1 => z.toString
16+
}
17+
}
18+

test/files/neg/t12159g.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
t.scala:4: warning: match may not be exhaustive.
2+
It would fail on the following inputs: Oz(), Z()
3+
def n(a: X) = a match { case _: Y => 42 }
4+
^
5+
error: No warnings can be incurred under -Werror.
6+
1 warning
7+
1 error

test/files/neg/t12159g/X.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
package p;
3+
public sealed interface X {
4+
public default int x() { return 27; }
5+
}
6+
final class Y implements X { }
7+
final class O {
8+
final static class Z implements X { }
9+
final static class Inner {
10+
final static class Oz implements X { }
11+
}
12+
}

test/files/neg/t12159g/t.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// scalac: -Werror -Xlint
2+
package p
3+
class T {
4+
def n(a: X) = a match { case _: Y => 42 }
5+
}

0 commit comments

Comments
 (0)