Skip to content

Commit abc9815

Browse files
Merge pull request #8664 from dotty-staging/fix-#8662
Fix #8662: Fix desugaring of try-catches that are partial functions
2 parents 249f231 + 550931c commit abc9815

File tree

5 files changed

+68
-28
lines changed

5 files changed

+68
-28
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,19 +1676,38 @@ object desugar {
16761676
case PatDef(mods, pats, tpt, rhs) =>
16771677
val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt))
16781678
flatTree(pats1 map (makePatDef(tree, mods, _, rhs)))
1679-
case ParsedTry(body, handler, finalizer) =>
1680-
handler match {
1681-
case Match(EmptyTree, cases) => Try(body, cases, finalizer)
1682-
case EmptyTree => Try(body, Nil, finalizer)
1683-
case _ =>
1684-
Try(body,
1685-
List(CaseDef(Ident(nme.DEFAULT_EXCEPTION_NAME), EmptyTree, Apply(handler, Ident(nme.DEFAULT_EXCEPTION_NAME)))),
1686-
finalizer)
1687-
}
16881679
}
16891680
desugared.withSpan(tree.span)
16901681
}
16911682

1683+
/** Turn a fucntion value `handlerFun` into a catch case for a try.
1684+
* If `handlerFun` is a partial function, translate to
1685+
*
1686+
* case ex =>
1687+
* val ev$1 = handlerFun
1688+
* if ev$1.isDefinedAt(ex) then ev$1.apply(ex) else throw ex
1689+
*
1690+
* Otherwise translate to
1691+
*
1692+
* case ex => handlerFun.apply(ex)
1693+
*/
1694+
def makeTryCase(handlerFun: tpd.Tree)(using Context): CaseDef =
1695+
val handler = TypedSplice(handlerFun)
1696+
val excId = Ident(nme.DEFAULT_EXCEPTION_NAME)
1697+
val rhs =
1698+
if handlerFun.tpe.widen.isRef(defn.PartialFunctionClass) then
1699+
val tmpName = UniqueName.fresh()
1700+
val tmpId = Ident(tmpName)
1701+
val init = ValDef(tmpName, TypeTree(), handler)
1702+
val test = If(
1703+
Apply(Select(tmpId, nme.isDefinedAt), excId),
1704+
Apply(Select(tmpId, nme.apply), excId),
1705+
Throw(excId))
1706+
Block(init :: Nil, test)
1707+
else
1708+
Apply(Select(handler, nme.apply), excId)
1709+
CaseDef(excId, EmptyTree, rhs)
1710+
16921711
/** Create a class definition with the same info as the refined type given by `parent`
16931712
* and `refinements`.
16941713
*

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -556,25 +556,7 @@ object Trees {
556556
type ThisTree[-T >: Untyped] = WhileDo[T]
557557
}
558558

559-
/** try block catch handler finally finalizer
560-
*
561-
* Note: if the handler is a case block CASES of the form
562-
*
563-
* { case1 ... caseN }
564-
*
565-
* the parser returns Match(EmptyTree, CASES). Desugaring and typing this yields a closure
566-
* node
567-
*
568-
* { def $anonfun(x: Throwable) = x match CASES; Closure(Nil, $anonfun) }
569-
*
570-
* At some later stage when we normalize the try we can revert this to
571-
*
572-
* Match(EmptyTree, CASES)
573-
*
574-
* or else if stack is non-empty
575-
*
576-
* Match(EmptyTree, <case x: Throwable => $anonfun(x)>)
577-
*/
559+
/** try block catch cases finally finalizer */
578560
case class Try[-T >: Untyped] private[ast] (expr: Tree[T], cases: List[CaseDef[T]], finalizer: Tree[T])(implicit @constructorOnly src: SourceFile)
579561
extends TermTree[T] {
580562
type ThisTree[-T >: Untyped] = Try[T]

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,6 +1396,15 @@ class Typer extends Namer
13961396
assignType(cpy.Try(tree)(expr2, cases2, finalizer1), expr2, cases2)
13971397
}
13981398

1399+
def typedTry(tree: untpd.ParsedTry, pt: Type)(using Context): Try =
1400+
val cases: List[untpd.CaseDef] = tree.handler match
1401+
case Match(EmptyTree, cases) => cases
1402+
case EmptyTree => Nil
1403+
case handler =>
1404+
val handler1 = typed(handler, defn.FunctionType(1).appliedTo(defn.ThrowableType, pt))
1405+
desugar.makeTryCase(handler1) :: Nil
1406+
typedTry(untpd.Try(tree.expr, cases, tree.finalizer).withSpan(tree.span), pt)
1407+
13991408
def typedThrow(tree: untpd.Throw)(using Context): Tree = {
14001409
val expr1 = typed(tree.expr, defn.ThrowableType)
14011410
Throw(expr1).withSpan(tree.span)
@@ -2277,6 +2286,7 @@ class Typer extends Namer
22772286
case tree: untpd.Tuple => typedTuple(tree, pt)
22782287
case tree: untpd.DependentTypeTree => typed(untpd.TypeTree().withSpan(tree.span), pt)
22792288
case tree: untpd.InfixOp => typedInfixOp(tree, pt)
2289+
case tree: untpd.ParsedTry => typedTry(tree, pt)
22802290
case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt)
22812291
case untpd.EmptyTree => tpd.EmptyTree
22822292
case tree: untpd.Quote => typedQuote(tree, pt)

tests/run/i8662.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
OK
2+
OK

tests/run/i8662.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
object Test {
2+
class EThrow extends Exception
3+
class ECatch extends Exception
4+
5+
def ok(): Unit =
6+
try throw new EThrow
7+
catch { case _: ECatch => }
8+
9+
def notOk(): Unit = {
10+
val pf: PartialFunction[Throwable, Unit] = {
11+
case e: ECatch =>
12+
}
13+
try throw new EThrow
14+
catch pf
15+
}
16+
17+
def test(f: => Unit): Unit =
18+
try f catch {
19+
case _: EThrow => println("OK")
20+
// case e: Throwable => println("! expect an EThrow but got " + e.getClass)
21+
}
22+
23+
def main(args: Array[String]): Unit = {
24+
test { ok() }
25+
test { notOk() }
26+
}
27+
}

0 commit comments

Comments
 (0)