diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index f2be78d4c725..ef45ec841511 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1676,19 +1676,38 @@ object desugar { case PatDef(mods, pats, tpt, rhs) => val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt)) flatTree(pats1 map (makePatDef(tree, mods, _, rhs))) - case ParsedTry(body, handler, finalizer) => - handler match { - case Match(EmptyTree, cases) => Try(body, cases, finalizer) - case EmptyTree => Try(body, Nil, finalizer) - case _ => - Try(body, - List(CaseDef(Ident(nme.DEFAULT_EXCEPTION_NAME), EmptyTree, Apply(handler, Ident(nme.DEFAULT_EXCEPTION_NAME)))), - finalizer) - } } desugared.withSpan(tree.span) } + /** Turn a fucntion value `handlerFun` into a catch case for a try. + * If `handlerFun` is a partial function, translate to + * + * case ex => + * val ev$1 = handlerFun + * if ev$1.isDefinedAt(ex) then ev$1.apply(ex) else throw ex + * + * Otherwise translate to + * + * case ex => handlerFun.apply(ex) + */ + def makeTryCase(handlerFun: tpd.Tree)(using Context): CaseDef = + val handler = TypedSplice(handlerFun) + val excId = Ident(nme.DEFAULT_EXCEPTION_NAME) + val rhs = + if handlerFun.tpe.widen.isRef(defn.PartialFunctionClass) then + val tmpName = UniqueName.fresh() + val tmpId = Ident(tmpName) + val init = ValDef(tmpName, TypeTree(), handler) + val test = If( + Apply(Select(tmpId, nme.isDefinedAt), excId), + Apply(Select(tmpId, nme.apply), excId), + Throw(excId)) + Block(init :: Nil, test) + else + Apply(Select(handler, nme.apply), excId) + CaseDef(excId, EmptyTree, rhs) + /** Create a class definition with the same info as the refined type given by `parent` * and `refinements`. * diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 5fc6a2c79f9c..24bfa98401a7 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -556,25 +556,7 @@ object Trees { type ThisTree[-T >: Untyped] = WhileDo[T] } - /** try block catch handler finally finalizer - * - * Note: if the handler is a case block CASES of the form - * - * { case1 ... caseN } - * - * the parser returns Match(EmptyTree, CASES). Desugaring and typing this yields a closure - * node - * - * { def $anonfun(x: Throwable) = x match CASES; Closure(Nil, $anonfun) } - * - * At some later stage when we normalize the try we can revert this to - * - * Match(EmptyTree, CASES) - * - * or else if stack is non-empty - * - * Match(EmptyTree, $anonfun(x)>) - */ + /** try block catch cases finally finalizer */ case class Try[-T >: Untyped] private[ast] (expr: Tree[T], cases: List[CaseDef[T]], finalizer: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[-T >: Untyped] = Try[T] diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 11b1ec32960e..548c406d4aa6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1396,6 +1396,15 @@ class Typer extends Namer assignType(cpy.Try(tree)(expr2, cases2, finalizer1), expr2, cases2) } + def typedTry(tree: untpd.ParsedTry, pt: Type)(using Context): Try = + val cases: List[untpd.CaseDef] = tree.handler match + case Match(EmptyTree, cases) => cases + case EmptyTree => Nil + case handler => + val handler1 = typed(handler, defn.FunctionType(1).appliedTo(defn.ThrowableType, pt)) + desugar.makeTryCase(handler1) :: Nil + typedTry(untpd.Try(tree.expr, cases, tree.finalizer).withSpan(tree.span), pt) + def typedThrow(tree: untpd.Throw)(using Context): Tree = { val expr1 = typed(tree.expr, defn.ThrowableType) Throw(expr1).withSpan(tree.span) @@ -2277,6 +2286,7 @@ class Typer extends Namer case tree: untpd.Tuple => typedTuple(tree, pt) case tree: untpd.DependentTypeTree => typed(untpd.TypeTree().withSpan(tree.span), pt) case tree: untpd.InfixOp => typedInfixOp(tree, pt) + case tree: untpd.ParsedTry => typedTry(tree, pt) case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt) case untpd.EmptyTree => tpd.EmptyTree case tree: untpd.Quote => typedQuote(tree, pt) diff --git a/tests/run/i8662.check b/tests/run/i8662.check new file mode 100644 index 000000000000..2c94e4837100 --- /dev/null +++ b/tests/run/i8662.check @@ -0,0 +1,2 @@ +OK +OK diff --git a/tests/run/i8662.scala b/tests/run/i8662.scala new file mode 100644 index 000000000000..59c17950cdb6 --- /dev/null +++ b/tests/run/i8662.scala @@ -0,0 +1,27 @@ +object Test { + class EThrow extends Exception + class ECatch extends Exception + + def ok(): Unit = + try throw new EThrow + catch { case _: ECatch => } + + def notOk(): Unit = { + val pf: PartialFunction[Throwable, Unit] = { + case e: ECatch => + } + try throw new EThrow + catch pf + } + + def test(f: => Unit): Unit = + try f catch { + case _: EThrow => println("OK") + // case e: Throwable => println("! expect an EThrow but got " + e.getClass) + } + + def main(args: Array[String]): Unit = { + test { ok() } + test { notOk() } + } +} \ No newline at end of file