Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Java 17 sealed in Java sources #10348

Merged
merged 1 commit into from May 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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]]()
som-snytt marked this conversation as resolved.
Show resolved Hide resolved

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 }
}