Skip to content

Fix unreachable warning in deeply nested sealed hierarchy #18706

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

Merged
merged 4 commits into from
Oct 20, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,7 @@ object TypeOps:
else if symbol.is(Module) then
TermRef(this(tref.prefix), symbol.sourceModule)
else if (prefixTVar != null)
this(tref)
this(tref.applyIfParameterized(tref.typeParams.map(_ => WildcardType)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear why this is correct.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to detail it in the commit message (9460b7d) - given something like:

class Jacket[T]:
  sealed trait BodyType

As we map over the type Jacket.this.BodyType we don't want to take the raw type Jacket, stripped from its ThisType wrapper, and have it be the prefix of BodyType, because we would then get Jacket#BodyType which is ill-formed. Instead we mean to refer to any the BodyType nested class of any Jacket: Jacket[?]#BodyType.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation. I now see the type will be upper bound for prefixTVar, so it's harmless.

else {
prefixTVar = WildcardType // prevent recursive call from assigning it
// e.g. tests/pos/i15029.more.scala, create a TypeVar for `Instances`' B, so we can disregard `Ints`
Expand Down
11 changes: 0 additions & 11 deletions compiler/test/dotty/tools/dotc/reporting/TestReporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M
protected final val _consoleReporter = new ConsoleReporter(null, new PrintWriter(_consoleBuf))
final def consoleOutput: String = _consoleBuf.toString

private var _didCrash = false
final def compilerCrashed: Boolean = _didCrash

private var _skip: Boolean = false
final def setSkip(): Unit = _skip = true
final def skipped: Boolean = _skip
Expand All @@ -50,14 +47,6 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M
def log(msg: String) =
_messageBuf.append(msg)

def logStackTrace(thrown: Throwable): Unit = {
_didCrash = true
val sw = new java.io.StringWriter
val pw = new java.io.PrintWriter(sw)
thrown.printStackTrace(pw)
log(sw.toString)
}

/** Prints the message with the given position indication. */
def printMessageAndPos(dia: Diagnostic, extra: String)(using Context): Unit = {
val msg = messageAndPos(dia)
Expand Down
20 changes: 8 additions & 12 deletions compiler/test/dotty/tools/vulpix/ParallelTesting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>

final def countErrors (reporters: Seq[TestReporter]) = countErrorsAndWarnings(reporters)._1
final def countWarnings(reporters: Seq[TestReporter]) = countErrorsAndWarnings(reporters)._2
final def reporterFailed(r: TestReporter) = r.compilerCrashed || r.errorCount > 0
final def reporterFailed(r: TestReporter) = r.errorCount > 0

/**
* For a given test source, returns a check file against which the result of the test run
Expand Down Expand Up @@ -733,22 +733,20 @@ trait ParallelTesting extends RunnerOrchestration { self =>
lazy val (map, expCount) = getWarnMapAndExpectedCount(testSource.sourceFiles.toIndexedSeq)
lazy val obtCount = reporters.foldLeft(0)(_ + _.warningCount)
lazy val (expected, unexpected) = getMissingExpectedWarnings(map, reporters.iterator.flatMap(_.diagnostics))
lazy val diagnostics = reporters.flatMap(_.diagnostics.toSeq.sortBy(_.pos.line).map(e => s" at ${e.pos.line + 1}: ${e.message}"))
def showLines(title: String, lines: Seq[String]) = if lines.isEmpty then "" else title + lines.mkString("\n", "\n", "")
def hasMissingAnnotations = expected.nonEmpty || unexpected.nonEmpty
def showDiagnostics = "-> following the diagnostics:\n" +
reporters.flatMap(_.diagnostics.toSeq.sortBy(_.pos.line).map(e => s"${e.pos.line + 1}: ${e.message}")).mkString(" at ", "\n at ", "")
def showDiagnostics = showLines("-> following the diagnostics:", diagnostics)
Option:
if reporters.exists(_.compilerCrashed) then s"Compiler crashed when compiling: ${testSource.title}"
else if reporters.exists(_.errorCount > 0) then
if reporters.exists(_.errorCount > 0) then
s"""Compilation failed for: ${testSource.title}
|$showDiagnostics
|""".stripMargin.trim.linesIterator.mkString("\n", "\n", "")
else if obtCount == 0 then s"\nNo warnings found when compiling warn test $testSource"
else if expCount == 0 then s"\nNo warning expected/defined in $testSource -- use // warn"
else if expCount != obtCount then
s"""|Wrong number of warnings encountered when compiling $testSource
|expected: $expCount, actual: $obtCount
|${expected.mkString("Unfulfilled expectations:\n", "\n", "")}
|${unexpected.mkString("Unexpected warnings:\n", "\n", "")}
|${showLines("Unfulfilled expectations:", expected)}
|${showLines("Unexpected warnings:", unexpected)}
|$showDiagnostics
|""".stripMargin.trim.linesIterator.mkString("\n", "\n", "")
else if hasMissingAnnotations then s"\nWarnings found on incorrect row numbers when compiling $testSource\n$showDiagnostics"
Expand Down Expand Up @@ -862,7 +860,6 @@ trait ParallelTesting extends RunnerOrchestration { self =>
override def suppressErrors = true

override def maybeFailureMessage(testSource: TestSource, reporters: Seq[TestReporter]): Option[String] =
def compilerCrashed = reporters.exists(_.compilerCrashed)
lazy val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(testSource.sourceFiles.toIndexedSeq)
lazy val actualErrors = reporters.foldLeft(0)(_ + _.errorCount)
lazy val (expected, unexpected) = getMissingExpectedErrors(errorMap, reporters.iterator.flatMap(_.errors))
Expand All @@ -871,8 +868,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
reporters.flatMap(_.allErrors.sortBy(_.pos.line).map(e => s"${e.pos.line + 1}: ${e.message}")).mkString(" at ", "\n at ", "")

Option {
if compilerCrashed then s"Compiler crashed when compiling: ${testSource.title}"
else if actualErrors == 0 then s"\nNo errors found when compiling neg test $testSource"
if actualErrors == 0 then s"\nNo errors found when compiling neg test $testSource"
else if expectedErrors == 0 then s"\nNo errors expected/defined in $testSource -- use // error or // nopos-error"
else if expectedErrors != actualErrors then
s"""|Wrong number of errors encountered when compiling $testSource
Expand Down
28 changes: 28 additions & 0 deletions tests/warn/i18661.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class Jacket[T]:
sealed trait BodyType:
sealed trait OrganType:
case class Heart() extends Body.Organ
case class Brain() extends Body.Organ
object Organ extends OrganType
sealed trait Organ
object Body extends BodyType
sealed trait Body

type AnyJacket = Jacket[?]
type AnyBodyOrgan = AnyJacket#BodyType#Organ
type AnyBodyOrganHeart = AnyJacket#BodyType#OrganType#Heart
type AnyBodyOrganBrain = AnyJacket#BodyType#OrganType#Brain

def check( asr : AnyBodyOrgan ) : String =
asr match
case c : AnyBodyOrganHeart => "Heart"
case s : AnyBodyOrganBrain => "Brain" // was: unreachable

val jacket = new Jacket[Unit]
val heart = new jacket.Body.Organ.Heart()
val brain = new jacket.Body.Organ.Brain()

@main
def go =
println( check( heart ) )
println( check( brain ) )