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 2, 2023
1 parent 9edd2d3 commit 35c0384
Show file tree
Hide file tree
Showing 13 changed files with 176 additions and 9 deletions.
30 changes: 28 additions & 2 deletions src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
Expand Up @@ -13,7 +13,7 @@
package scala.tools.nsc
package typechecker

import scala.collection.mutable.ArrayDeque
import scala.collection.mutable, mutable.ArrayDeque

/** Defines the sub-components for the namer, packageobjects, and typer phases.
*/
Expand Down Expand Up @@ -101,7 +101,28 @@ 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 = mutable.Set.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.addOne(sym)
private def completeSealedJava(sym: Symbol): Unit = if (sym.isJava && sym.isSealed)
sym.attachments.get[PermittedSubclassSymbols] match {
case Some(permitted) =>
permitted.permits.foreach { child =>
child.initialize
completeSealedJava(child)
}
case _ =>
completable.foreach { maybe =>
if (maybe != sym && maybe.sourceFile == sym.sourceFile && maybe.parentSymbols.contains(sym)) {
maybe.initialize
completeSealedJava(maybe)
}
}
}

override def run(): Unit = {
val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.typerNanos) else null
global.echoPhaseSummary(this)
Expand All @@ -112,8 +133,13 @@ trait Analyzer extends AnyRef
undoLog.clear()
}
finishComputeParamAlias()
try matched.foreach(completeSealedJava)
finally matched.clear()
try while (toCheckAfterTyper.nonEmpty) toCheckAfterTyper.removeHead().apply()
finally toCheckAfterTyper.clearAndShrink()
finally {
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 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
}
}

0 comments on commit 35c0384

Please sign in to comment.