Skip to content

Commit

Permalink
Synthesize a PartialFunction from function literal
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand committed Jun 23, 2019
1 parent ec46ff8 commit 5182656
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 10 deletions.
26 changes: 16 additions & 10 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Expand Up @@ -3060,18 +3060,24 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
setError(fun)
}
} else {
// translate `x => x match { <cases> }` : PartialFunction to
// `new PartialFunction { def applyOrElse(x, default) = x match { <cases> } def isDefinedAt(x) = ... }`
def synthesizePartialFunction(tree: Tree): Tree = {
// go to outer context -- must discard the context that was created for the Function since we're discarding the function
// thus, its symbol, which serves as the current context.owner, is not the right owner
// you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner)
val outerTyper = newTyper(context.outer)
val p = vparams.head
if (p.tpt.tpe == null) p.tpt setType outerTyper.typedType(p.tpt).tpe

outerTyper.synthesizePartialFunction(p.name, p.pos, paramSynthetic = false, tree, mode, pt)
}
fun.body match {
// translate `x => x match { <cases> }` : PartialFunction to
// `new PartialFunction { def applyOrElse(x, default) = x match { <cases> } def isDefinedAt(x) = ... }`
case Match(sel, cases) if (sel ne EmptyTree) && (pt.typeSymbol == PartialFunctionClass) =>
// go to outer context -- must discard the context that was created for the Function since we're discarding the function
// thus, its symbol, which serves as the current context.owner, is not the right owner
// you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner)
val outerTyper = newTyper(context.outer)
val p = vparams.head
if (p.tpt.tpe == null) p.tpt setType outerTyper.typedType(p.tpt).tpe

outerTyper.synthesizePartialFunction(p.name, p.pos, paramSynthetic = false, fun.body, mode, pt)
synthesizePartialFunction(fun.body)

case _ if pt.typeSymbol == PartialFunctionClass =>
synthesizePartialFunction(Match(EmptyTree, List(CaseDef(EmptyTree, fun.body))))

case _ => doTypedFunction(fun, resProto)
}
Expand Down
3 changes: 3 additions & 0 deletions test/files/pos/partialfun.scala
Expand Up @@ -8,4 +8,7 @@ object partialfun {
case None => throw new MatchError(None)
} (None);

// Again, but using function literal
applyPartial(_.get)(None)

}
6 changes: 6 additions & 0 deletions test/files/pos/virtpatmat_partialfun_nsdnho.scala
Expand Up @@ -15,4 +15,10 @@ class Test {
// at scala.tools.nsc.typechecker.SuperAccessors$SuperAccTransformer.hostForAccessorOf(SuperAccessors.scala:474)
// at scala.tools.nsc.typechecker.SuperAccessors$SuperAccTransformer.needsProtectedAccessor(SuperAccessors.scala:457)
val c: (Int => (Any => Any)) = { m => { case _ => m.toInt } }


// Again, but using function literal
val a2: (Map[Int, Int] => (Any => Any)) = { m => { _ => m - 1} }
val b2: (Int => (Any => Any)) = { m => { _ => m } }
val c2: (Int => (Any => Any)) = { m => { _ => m.toInt } }
}
4 changes: 4 additions & 0 deletions test/files/run/partialfun.check
Expand Up @@ -4,3 +4,7 @@
0:isDefinedAt
1:isDefinedAt
2:apply
false
true
Vector(1, 2, 3, 4, 5)
Vector(1, 2, 3, 4, 5)
9 changes: 9 additions & 0 deletions test/files/run/partialfun.scala
Expand Up @@ -81,8 +81,17 @@ object Test {
chained(())
}

def fromFunctionLiteralTest(): Unit = {
def isEven(n: Int): Boolean = PartialFunction.cond(n)(_ % 2 == 0)
println(isEven(1))
println(isEven(2))
println((1 to 5).map(_.toString))
println((1 to 5).collect(_.toString))
}

def main(args: Array[String]): Unit = {
collectTest()
orElseTest()
fromFunctionLiteralTest()
}
}

0 comments on commit 5182656

Please sign in to comment.