Skip to content

Commit

Permalink
Exhaustive check in mixed compilation
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Apr 1, 2023
1 parent 9edd2d3 commit eeac512
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 8 deletions.
31 changes: 30 additions & 1 deletion src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
Expand Up @@ -101,7 +101,31 @@ trait Analyzer extends AnyRef
// compiler run). This is good enough for the resident compiler, which was the most affected.
undoLog.clear()
private val toCheckAfterTyper = ArrayDeque.empty[CompilationUnit.ToCheckAfterTyper]
private val completable = ArrayDeque.empty[Symbol]
private val matched = ArrayDeque.empty[Symbol]
def addCheckAfterTyper(check: CompilationUnit.ToCheckAfterTyper): Unit = toCheckAfterTyper.append(check)
// symbols such as Java classes which may require late completion
def addLateCompletion(sym: Symbol): Unit = completable.append(sym)
def registerMatchedJavaClass(sym: Symbol): Unit = matched.append(sym)
private def completeSealedJava(sym: Symbol): Unit = if (sym.isSealed) {
sym.attachments.get[PermittedSubclassSymbols] match {
case Some(permitted) =>
permitted.permits.foreach { child =>
child.initialize
completeSealedJava(child)
}
case _ =>
completable.foreach { maybe =>
if (maybe.sourceFile == sym.sourceFile) {
maybe.initialize
val ps = maybe.parentSymbols
if (ps.contains(sym))
completeSealedJava(maybe)
}
}
}
}

override def run(): Unit = {
val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.typerNanos) else null
global.echoPhaseSummary(this)
Expand All @@ -112,8 +136,13 @@ trait Analyzer extends AnyRef
undoLog.clear()
}
finishComputeParamAlias()
while (matched.nonEmpty) completeSealedJava(matched.removeHead())
try while (toCheckAfterTyper.nonEmpty) toCheckAfterTyper.removeHead().apply()
finally toCheckAfterTyper.clearAndShrink()
finally {
matched.clearAndShrink()
completable.clearAndShrink()
toCheckAfterTyper.clearAndShrink()
}
// defensive measure in case the bookkeeping in deferred macro expansion is buggy
clearDelayed()
if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.typerNanos, start)
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/scala/tools/nsc/typechecker/Namers.scala
Expand Up @@ -795,6 +795,13 @@ trait Namers extends MethodSynthesis {
tree.symbol = enterClassSymbol(tree)
tree.symbol setInfo completerOf(tree)

// matching on Java types may force completions, so register this class as a late completion
if (tree.symbol.isJava)
currentRun.typerPhase match {
case ph: typerFactory.TyperPhase => ph.addLateCompletion(tree.symbol)
case _ =>
}

if (mods.isCase) {
val m = ensureCompanionObject(tree, caseModuleDef)
m.moduleClass.updateAttachment(new ClassForCaseCompanionAttachment(tree))
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Expand Up @@ -2640,6 +2640,14 @@ 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 registerMatchedJavaClass(sym: Symbol): Unit =
if (sym.isJava && sym.isSealed)
currentRun.typerPhase match {
case ph: typerFactory.TyperPhase => ph.registerMatchedJavaClass(sym)
case _ =>
}
registerMatchedJavaClass(selectorTp.typeSymbol)

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

Expand Down
14 changes: 7 additions & 7 deletions test/files/neg/t12159d.check
@@ -1,7 +1,7 @@
s.scala:5: error: illegal inheritance from sealed class H
class S extends H {
^
s.scala:8: error: illegal inheritance from sealed trait I
trait T extends I {
^
2 errors
t_3.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_3.java
@@ -0,0 +1,14 @@
// javaVersion: 17+
package p;

sealed abstract public class X_3 {
}

final class W extends X_3 {
}

final class Y extends X_3 {
}

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

class C {
def f(x: X_3) =
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 {
}

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

0 comments on commit eeac512

Please sign in to comment.