Skip to content

Commit

Permalink
Avoid crash when superType does not exist after erasure
Browse files Browse the repository at this point in the history
Fixes #19929

Two main changes:

 - In TypeErasure, throw a TypeError instead of a FatalError if a supertype of an applied
   type does not exist. That way, we get a proper error with a position.
 - Move some catch-and-rethrow logic from ReTyper to TreeChecker. ReTyper alreayd had special
   exceptions that disabled the logic for all uses of ReTyper except TreeChecker. Unfortunately
   the ReTyper override also disabled the special TypeError handling in Typer.
  • Loading branch information
odersky committed Mar 13, 2024
1 parent 3694d95 commit 7d0d088
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 37 deletions.
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/core/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -751,12 +751,12 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
private def checkedSuperType(tp: TypeProxy)(using Context): Type =
val tp1 = tp.translucentSuperType
if !tp1.exists then
val msg = tp.typeConstructor match
val typeErr = tp.typeConstructor match
case tycon: TypeRef =>
MissingType(tycon.prefix, tycon.name).toMessage.message
MissingType(tycon.prefix, tycon.name)
case _ =>
i"Cannot resolve reference to $tp"
throw FatalError(msg)
TypeError(em"Cannot resolve reference to $tp")
throw typeErr
tp1

/** Widen term ref, skipping any `()` parameter of an eventual getter. Used to erase a TermRef.
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/core/TypeErrors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ abstract class TypeError(using creationContext: Context) extends Exception(""):
def toMessage(using Context): Message

/** Uses creationContext to produce the message */
override def getMessage: String = toMessage.message
override def getMessage: String =
try toMessage.message catch case ex: Throwable => "TypeError"

object TypeError:
def apply(msg: Message)(using Context) = new TypeError:
Expand Down
54 changes: 29 additions & 25 deletions compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -407,31 +407,35 @@ object TreeChecker {
}

override def typedUnadapted(tree: untpd.Tree, pt: Type, locked: TypeVars)(using Context): Tree = {
val res = tree match {
case _: untpd.TypedSplice | _: untpd.Thicket | _: EmptyValDef[?] =>
super.typedUnadapted(tree, pt, locked)
case _ if tree.isType =>
promote(tree)
case _ =>
val tree1 = super.typedUnadapted(tree, pt, locked)
def isSubType(tp1: Type, tp2: Type) =
(tp1 eq tp2) || // accept NoType / NoType
(tp1 <:< tp2)
def divergenceMsg(tp1: Type, tp2: Type) =
s"""Types differ
|Original type : ${tree.typeOpt.show}
|After checking: ${tree1.tpe.show}
|Original tree : ${tree.show}
|After checking: ${tree1.show}
|Why different :
""".stripMargin + core.TypeComparer.explained(_.isSubType(tp1, tp2))
if (tree.hasType) // it might not be typed because Typer sometimes constructs new untyped trees and resubmits them to typedUnadapted
assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt))
tree1
}
checkNoOrphans(res.tpe)
phasesToCheck.foreach(_.checkPostCondition(res))
res
try
val res = tree match
case _: untpd.TypedSplice | _: untpd.Thicket | _: EmptyValDef[?] =>
super.typedUnadapted(tree, pt, locked)
case _ if tree.isType =>
promote(tree)
case _ =>
val tree1 = super.typedUnadapted(tree, pt, locked)
def isSubType(tp1: Type, tp2: Type) =
(tp1 eq tp2) || // accept NoType / NoType
(tp1 <:< tp2)
def divergenceMsg(tp1: Type, tp2: Type) =
s"""Types differ
|Original type : ${tree.typeOpt.show}
|After checking: ${tree1.tpe.show}
|Original tree : ${tree.show}
|After checking: ${tree1.show}
|Why different :
""".stripMargin + core.TypeComparer.explained(_.isSubType(tp1, tp2))
if (tree.hasType) // it might not be typed because Typer sometimes constructs new untyped trees and resubmits them to typedUnadapted
assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt))
tree1
checkNoOrphans(res.tpe)
phasesToCheck.foreach(_.checkPostCondition(res))
res
catch case NonFatal(ex) if !ctx.run.enrichedErrorMessage =>
val treeStr = tree.show(using ctx.withPhase(ctx.phase.prev.megaPhase))
println(ctx.run.enrichErrorMessage(s"exception while retyping $treeStr of class ${tree.className} # ${tree.uniqueId}"))
throw ex
}

def checkNotRepeated(tree: Tree)(using Context): tree.type = {
Expand Down
7 changes: 0 additions & 7 deletions compiler/src/dotty/tools/dotc/typer/ReTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,6 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking
override def addCanThrowCapabilities(expr: untpd.Tree, cases: List[CaseDef])(using Context): untpd.Tree =
expr

override def typedUnadapted(tree: untpd.Tree, pt: Type, locked: TypeVars)(using Context): Tree =
try super.typedUnadapted(tree, pt, locked)
catch case NonFatal(ex) if ctx.phase != Phases.typerPhase && ctx.phase != Phases.inliningPhase && !ctx.run.enrichedErrorMessage =>
val treeStr = tree.show(using ctx.withPhase(ctx.phase.prev.megaPhase))
println(ctx.run.enrichErrorMessage(s"exception while retyping $treeStr of class ${tree.className} # ${tree.uniqueId}"))
throw ex

override def inlineExpansion(mdef: DefDef)(using Context): List[Tree] = mdef :: Nil

override def inferView(from: Tree, to: Type)(using Context): Implicits.SearchResult =
Expand Down
5 changes: 5 additions & 0 deletions tests/neg/i19929.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- Error: tests/neg/i19929.scala:5:6 -----------------------------------------------------------------------------------
5 | val _: a.M = ??? // error was crash
| ^
| cannot resolve reference to type (a : A).M
| the classfile defining the type might be missing from the classpath
5 changes: 5 additions & 0 deletions tests/neg/i19929.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
trait A:
private type M

def foo(a: A{type M = Int}) =
val _: a.M = ??? // error was crash

0 comments on commit 7d0d088

Please sign in to comment.