Skip to content

Fix #8662: Fix desugaring of try-catches that are partial functions #8664

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
*
Expand Down
20 changes: 1 addition & 19 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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, <case x: Throwable => $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]
Expand Down
10 changes: 10 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions tests/run/i8662.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
OK
OK
27 changes: 27 additions & 0 deletions tests/run/i8662.scala
Original file line number Diff line number Diff line change
@@ -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() }
}
}