Skip to content

Commit 72cda35

Browse files
oderskyWojciechMazur
authored andcommitted
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. [Cherry-picked 822e792]
1 parent cfcdbbb commit 72cda35

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
@@ -388,31 +388,35 @@ object TreeChecker {
388388
}
389389

390390
override def typedUnadapted(tree: untpd.Tree, pt: Type, locked: TypeVars)(using Context): Tree = {
391-
val res = tree match {
392-
case _: untpd.TypedSplice | _: untpd.Thicket | _: EmptyValDef[?] =>
393-
super.typedUnadapted(tree, pt, locked)
394-
case _ if tree.isType =>
395-
promote(tree)
396-
case _ =>
397-
val tree1 = super.typedUnadapted(tree, pt, locked)
398-
def isSubType(tp1: Type, tp2: Type) =
399-
(tp1 eq tp2) || // accept NoType / NoType
400-
(tp1 <:< tp2)
401-
def divergenceMsg(tp1: Type, tp2: Type) =
402-
s"""Types differ
403-
|Original type : ${tree.typeOpt.show}
404-
|After checking: ${tree1.tpe.show}
405-
|Original tree : ${tree.show}
406-
|After checking: ${tree1.show}
407-
|Why different :
408-
""".stripMargin + core.TypeComparer.explained(_.isSubType(tp1, tp2))
409-
if (tree.hasType) // it might not be typed because Typer sometimes constructs new untyped trees and resubmits them to typedUnadapted
410-
assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt))
411-
tree1
412-
}
413-
checkNoOrphans(res.tpe)
414-
phasesToCheck.foreach(_.checkPostCondition(res))
415-
res
391+
try
392+
val res = tree match
393+
case _: untpd.TypedSplice | _: untpd.Thicket | _: EmptyValDef[?] =>
394+
super.typedUnadapted(tree, pt, locked)
395+
case _ if tree.isType =>
396+
promote(tree)
397+
case _ =>
398+
val tree1 = super.typedUnadapted(tree, pt, locked)
399+
def isSubType(tp1: Type, tp2: Type) =
400+
(tp1 eq tp2) || // accept NoType / NoType
401+
(tp1 <:< tp2)
402+
def divergenceMsg(tp1: Type, tp2: Type) =
403+
s"""Types differ
404+
|Original type : ${tree.typeOpt.show}
405+
|After checking: ${tree1.tpe.show}
406+
|Original tree : ${tree.show}
407+
|After checking: ${tree1.show}
408+
|Why different :
409+
""".stripMargin + core.TypeComparer.explained(_.isSubType(tp1, tp2))
410+
if (tree.hasType) // it might not be typed because Typer sometimes constructs new untyped trees and resubmits them to typedUnadapted
411+
assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt))
412+
tree1
413+
checkNoOrphans(res.tpe)
414+
phasesToCheck.foreach(_.checkPostCondition(res))
415+
res
416+
catch case NonFatal(ex) if !ctx.run.enrichedErrorMessage =>
417+
val treeStr = tree.show(using ctx.withPhase(ctx.phase.prev.megaPhase))
418+
println(ctx.run.enrichErrorMessage(s"exception while retyping $treeStr of class ${tree.className} # ${tree.uniqueId}"))
419+
throw ex
416420
}
417421

418422
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
@@ -149,13 +149,6 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking
149149
override def addCanThrowCapabilities(expr: untpd.Tree, cases: List[CaseDef])(using Context): untpd.Tree =
150150
expr
151151

152-
override def typedUnadapted(tree: untpd.Tree, pt: Type, locked: TypeVars)(using Context): Tree =
153-
try super.typedUnadapted(tree, pt, locked)
154-
catch case NonFatal(ex) if ctx.phase != Phases.typerPhase && ctx.phase != Phases.inliningPhase && !ctx.run.enrichedErrorMessage =>
155-
val treeStr = tree.show(using ctx.withPhase(ctx.phase.prev.megaPhase))
156-
println(ctx.run.enrichErrorMessage(s"exception while retyping $treeStr of class ${tree.className} # ${tree.uniqueId}"))
157-
throw ex
158-
159152
override def inlineExpansion(mdef: DefDef)(using Context): List[Tree] = mdef :: Nil
160153

161154
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)