Skip to content

Commit 822e792

Browse files
committed
Avoid crash when superType does not exist after erasure
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.
1 parent 4c2305d commit 822e792

File tree

6 files changed

+45
-37
lines changed

6 files changed

+45
-37
lines changed

compiler/src/dotty/tools/dotc/core/TypeErasure.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -751,12 +751,12 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
751751
private def checkedSuperType(tp: TypeProxy)(using Context): Type =
752752
val tp1 = tp.translucentSuperType
753753
if !tp1.exists then
754-
val msg = tp.typeConstructor match
754+
val typeErr = tp.typeConstructor match
755755
case tycon: TypeRef =>
756-
MissingType(tycon.prefix, tycon.name).toMessage.message
756+
MissingType(tycon.prefix, tycon.name)
757757
case _ =>
758-
i"Cannot resolve reference to $tp"
759-
throw FatalError(msg)
758+
TypeError(em"Cannot resolve reference to $tp")
759+
throw typeErr
760760
tp1
761761

762762
/** Widen term ref, skipping any `()` parameter of an eventual getter. Used to erase a TermRef.

compiler/src/dotty/tools/dotc/core/TypeErrors.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ abstract class TypeError(using creationContext: Context) extends Exception(""):
4646
def toMessage(using Context): Message
4747

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

5152
object TypeError:
5253
def apply(msg: Message)(using Context) = new TypeError:

compiler/src/dotty/tools/dotc/transform/TreeChecker.scala

+29-25
Original file line numberDiff line numberDiff line change
@@ -418,31 +418,35 @@ object TreeChecker {
418418
}
419419

420420
override def typedUnadapted(tree: untpd.Tree, pt: Type, locked: TypeVars)(using Context): Tree = {
421-
val res = tree match {
422-
case _: untpd.TypedSplice | _: untpd.Thicket | _: EmptyValDef[?] =>
423-
super.typedUnadapted(tree, pt, locked)
424-
case _ if tree.isType =>
425-
promote(tree)
426-
case _ =>
427-
val tree1 = super.typedUnadapted(tree, pt, locked)
428-
def isSubType(tp1: Type, tp2: Type) =
429-
(tp1 eq tp2) || // accept NoType / NoType
430-
(tp1 <:< tp2)
431-
def divergenceMsg(tp1: Type, tp2: Type) =
432-
s"""Types differ
433-
|Original type : ${tree.typeOpt.show}
434-
|After checking: ${tree1.tpe.show}
435-
|Original tree : ${tree.show}
436-
|After checking: ${tree1.show}
437-
|Why different :
438-
""".stripMargin + core.TypeComparer.explained(_.isSubType(tp1, tp2))
439-
if (tree.hasType) // it might not be typed because Typer sometimes constructs new untyped trees and resubmits them to typedUnadapted
440-
assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt))
441-
tree1
442-
}
443-
checkNoOrphans(res.tpe)
444-
phasesToCheck.foreach(_.checkPostCondition(res))
445-
res
421+
try
422+
val res = tree match
423+
case _: untpd.TypedSplice | _: untpd.Thicket | _: EmptyValDef[?] =>
424+
super.typedUnadapted(tree, pt, locked)
425+
case _ if tree.isType =>
426+
promote(tree)
427+
case _ =>
428+
val tree1 = super.typedUnadapted(tree, pt, locked)
429+
def isSubType(tp1: Type, tp2: Type) =
430+
(tp1 eq tp2) || // accept NoType / NoType
431+
(tp1 <:< tp2)
432+
def divergenceMsg(tp1: Type, tp2: Type) =
433+
s"""Types differ
434+
|Original type : ${tree.typeOpt.show}
435+
|After checking: ${tree1.tpe.show}
436+
|Original tree : ${tree.show}
437+
|After checking: ${tree1.show}
438+
|Why different :
439+
""".stripMargin + core.TypeComparer.explained(_.isSubType(tp1, tp2))
440+
if (tree.hasType) // it might not be typed because Typer sometimes constructs new untyped trees and resubmits them to typedUnadapted
441+
assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt))
442+
tree1
443+
checkNoOrphans(res.tpe)
444+
phasesToCheck.foreach(_.checkPostCondition(res))
445+
res
446+
catch case NonFatal(ex) if !ctx.run.enrichedErrorMessage =>
447+
val treeStr = tree.show(using ctx.withPhase(ctx.phase.prev.megaPhase))
448+
println(ctx.run.enrichErrorMessage(s"exception while retyping $treeStr of class ${tree.className} # ${tree.uniqueId}"))
449+
throw ex
446450
}
447451

448452
def checkNotRepeated(tree: Tree)(using Context): tree.type = {

compiler/src/dotty/tools/dotc/typer/ReTyper.scala

-7
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,6 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking
170170
override def addCanThrowCapabilities(expr: untpd.Tree, cases: List[CaseDef])(using Context): untpd.Tree =
171171
expr
172172

173-
override def typedUnadapted(tree: untpd.Tree, pt: Type, locked: TypeVars)(using Context): Tree =
174-
try super.typedUnadapted(tree, pt, locked)
175-
catch case NonFatal(ex) if ctx.phase != Phases.typerPhase && ctx.phase != Phases.inliningPhase && !ctx.run.enrichedErrorMessage =>
176-
val treeStr = tree.show(using ctx.withPhase(ctx.phase.prev.megaPhase))
177-
println(ctx.run.enrichErrorMessage(s"exception while retyping $treeStr of class ${tree.className} # ${tree.uniqueId}"))
178-
throw ex
179-
180173
override def inlineExpansion(mdef: DefDef)(using Context): List[Tree] = mdef :: Nil
181174

182175
override def inferView(from: Tree, to: Type)(using Context): Implicits.SearchResult =

tests/neg/i19929.check

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- Error: tests/neg/i19929.scala:5:6 -----------------------------------------------------------------------------------
2+
5 | val _: a.M = ??? // error was crash
3+
| ^
4+
| cannot resolve reference to type (a : A).M
5+
| the classfile defining the type might be missing from the classpath

tests/neg/i19929.scala

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
trait A:
2+
private type M
3+
4+
def foo(a: A{type M = Int}) =
5+
val _: a.M = ??? // error was crash

0 commit comments

Comments
 (0)