Skip to content

Commit

Permalink
Merge pull request #10348 from som-snytt/followup/12159-mixed-java-se…
Browse files Browse the repository at this point in the history
…aled

Support Java 17 `sealed` in Java sources
  • Loading branch information
lrytz committed May 1, 2023
2 parents aa326fc + a0f546d commit bd9b10a
Show file tree
Hide file tree
Showing 17 changed files with 195 additions and 4 deletions.
6 changes: 5 additions & 1 deletion src/compiler/scala/tools/nsc/javac/JavaParsers.scala
Expand Up @@ -19,6 +19,7 @@ package javac
import symtab.Flags
import JavaTokens._
import scala.annotation._
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.language.implicitConversions
import scala.reflect.internal.util.{ListOfNil, Position}
Expand All @@ -45,6 +46,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {

abstract class JavaParser extends ParserCommon {
val in: JavaScanner
def unit: CompilationUnit

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

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

def interfaceDecl(mods: Modifiers): List[Tree] = {
if (mods.hasFlag(SEALED)) patmat.javaClassesByUnit(unit.source) = mutable.Set.empty
accept(INTERFACE)
val pos = in.currentPos
val name = identForType()
Expand Down
Expand Up @@ -13,9 +13,10 @@
package scala.tools.nsc.transform.patmat

import scala.annotation.tailrec
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.reflect.internal.{Mode, Types}
import scala.reflect.internal.util.Statistics
import scala.reflect.internal.util.{SourceFile, Statistics}
import scala.tools.nsc.Global
import scala.tools.nsc.ast
import scala.tools.nsc.transform.{Transform, TypingTransformers}
Expand Down Expand Up @@ -58,6 +59,9 @@ trait PatternMatching extends Transform

val phaseName: String = "patmat"

/** Symbols to force for determining children of sealed Java classes. */
val javaClassesByUnit = perRunCaches.newMap[SourceFile, mutable.Set[Symbol]]()

def newTransformer(unit: CompilationUnit): AstTransformer = new MatchTransformer(unit)

class MatchTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/scala/tools/nsc/typechecker/Namers.scala
Expand Up @@ -795,6 +795,8 @@ trait Namers extends MethodSynthesis {
tree.symbol = enterClassSymbol(tree)
tree.symbol setInfo completerOf(tree)

if (tree.symbol.isJava) patmat.javaClassesByUnit.get(tree.symbol.pos.source).foreach(_.addOne(tree.symbol))

if (mods.isCase) {
val m = ensureCompanionObject(tree, caseModuleDef)
m.moduleClass.updateAttachment(new ClassForCaseCompanionAttachment(tree))
Expand Down
28 changes: 26 additions & 2 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Expand Up @@ -15,12 +15,11 @@ package tools.nsc
package typechecker

import scala.annotation.{tailrec, unused}
import scala.collection.mutable
import scala.collection.mutable, mutable.ListBuffer
import scala.reflect.internal.{Chars, TypesStats}
import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Statistics, StringContextStripMarginOps}
import scala.tools.nsc.Reporting.{MessageFilter, Suppression, WConf, WarningCategory}
import scala.util.chaining._
import mutable.ListBuffer
import symtab.Flags._
import Mode._

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

def initChildren(sym: Symbol): Unit =
if (sym.isJava && sym.isSealed)
sym.attachments.get[PermittedSubclassSymbols] match {
case Some(PermittedSubclassSymbols(permits)) =>
for (child <- permits if child.isJava)
initChildren(child.initialize)
case _ =>
val seen = mutable.HashSet.empty[Symbol]
def populate(): Unit =
patmat.javaClassesByUnit.get(sym.pos.source) match {
case Some(classes) =>
classes.find(!seen(_)) match {
case Some(unseen) =>
seen += unseen
unseen.initialize.companionSymbol.moduleClass.initialize
if (unseen.hasAttachment[PermittedSubclassSymbols]) initChildren(unseen)
populate()
case _ =>
}
case _ =>
}
populate()
}
initChildren(selectorTp.typeSymbol)

def finish(cases: List[CaseDef], matchType: Type) =
treeCopy.Match(tree, selector1, cases) setType matchType

Expand Down
7 changes: 7 additions & 0 deletions test/files/neg/t12159d.check
@@ -0,0 +1,7 @@
t.scala:7: warning: match may not be exhaustive.
It would fail on the following input: W()
x match {
^
error: No warnings can be incurred under -Werror.
1 warning
1 error
14 changes: 14 additions & 0 deletions test/files/neg/t12159d/X.java
@@ -0,0 +1,14 @@
// javaVersion: 17+
package p;

sealed abstract public class X {
}

final class W extends X {
}

final class Y extends X {
}

final class Z extends X {
}
12 changes: 12 additions & 0 deletions test/files/neg/t12159d/t.scala
@@ -0,0 +1,12 @@
// javaVersion: 17+
// scalac: -Werror
package p

class C {
def f(x: X) =
x match {
case y: Y => y.toString
case z: Z => z.toString
}
}

11 changes: 11 additions & 0 deletions test/files/neg/t12159e.check
@@ -0,0 +1,11 @@
t.scala:7: warning: match may not be exhaustive.
It would fail on the following input: W()
x match {
^
t.scala:12: warning: match may not be exhaustive.
It would fail on the following inputs: Z(), Z2()
x match {
^
error: No warnings can be incurred under -Werror.
2 warnings
1 error
20 changes: 20 additions & 0 deletions test/files/neg/t12159e/X.java
@@ -0,0 +1,20 @@
// javaVersion: 17+
package p;

sealed abstract public class X {
}

final class W extends X {
}

final class Y extends X {
}

sealed class Z extends X permits Z1, Z2 {
}

final class Z1 extends Z {
}

final class Z2 extends Z {
}
18 changes: 18 additions & 0 deletions test/files/neg/t12159e/t.scala
@@ -0,0 +1,18 @@
// javaVersion: 17+
// scalac: -Werror
package p

class C {
def f(x: X) =
x match {
case y: Y => y.toString
case z: Z => z.toString
}
def g(x: X) =
x match {
case w: W => w.toString
case y: Y => y.toString
case z: Z1 => z.toString
}
}

11 changes: 11 additions & 0 deletions test/files/neg/t12159f.check
@@ -0,0 +1,11 @@
t.scala:7: warning: match may not be exhaustive.
It would fail on the following input: W()
x match {
^
t.scala:12: warning: match may not be exhaustive.
It would fail on the following inputs: Z(), Z2()
x match {
^
error: No warnings can be incurred under -Werror.
2 warnings
1 error
14 changes: 14 additions & 0 deletions test/files/neg/t12159f/X.java
@@ -0,0 +1,14 @@
// javaVersion: 17+
package p;

sealed abstract public class X {
}

final class W extends X {
}

final class Y extends X {
}

sealed class Z extends X permits Z1, Z2 {
}
8 changes: 8 additions & 0 deletions test/files/neg/t12159f/Z.java
@@ -0,0 +1,8 @@
// javaVersion: 17+
package p;

final class Z1 extends Z {
}

final class Z2 extends Z {
}
18 changes: 18 additions & 0 deletions test/files/neg/t12159f/t.scala
@@ -0,0 +1,18 @@
// javaVersion: 17+
// scalac: -Werror
package p

class C {
def f(x: X) =
x match {
case y: Y => y.toString
case z: Z => z.toString
}
def g(x: X) =
x match {
case w: W => w.toString
case y: Y => y.toString
case z: Z1 => z.toString
}
}

7 changes: 7 additions & 0 deletions test/files/neg/t12159g.check
@@ -0,0 +1,7 @@
t.scala:4: warning: match may not be exhaustive.
It would fail on the following inputs: Oz(), Z()
def n(a: X) = a match { case _: Y => 42 }
^
error: No warnings can be incurred under -Werror.
1 warning
1 error
12 changes: 12 additions & 0 deletions test/files/neg/t12159g/X.java
@@ -0,0 +1,12 @@

package p;
public sealed interface X {
public default int x() { return 27; }
}
final class Y implements X { }
final class O {
final static class Z implements X { }
final static class Inner {
final static class Oz implements X { }
}
}
5 changes: 5 additions & 0 deletions test/files/neg/t12159g/t.scala
@@ -0,0 +1,5 @@
// scalac: -Werror -Xlint
package p
class T {
def n(a: X) = a match { case _: Y => 42 }
}

0 comments on commit bd9b10a

Please sign in to comment.