Skip to content

Commit

Permalink
Fix #8530: Support inline unapply
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasstucki committed Mar 14, 2020
1 parent ea27651 commit c98e14c
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 22 deletions.
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,6 @@ object PrepareInlineable {
ctx.error(em"Implementation restriction: No inline methods allowed where opaque type aliases are in scope", inlined.sourcePos)
if (ctx.outer.inInlineMethod)
ctx.error(ex"Implementation restriction: nested inline methods are not supported", inlined.sourcePos)
if (inlined.name.isUnapplyName)
ctx.error(em"Implementation restriction: inline ${inlined.name} methods are not supported", inlined.sourcePos)

if (inlined.is(Macro) && !ctx.isAfterTyper) {

Expand Down
18 changes: 18 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,24 @@ class Typer extends Namer
if (bounds != null) sym.info = bounds
}
b
case t: UnApply if t.symbol.is(Inline) =>
// An inline unapply `P.unapply` in a plattern `P(x1,x2,...)` is transformed into
// `{ class $anon { def unapply(x1: T1, x2: T2, ...): R = P.unapply(x1, x2, ...) }; new $anon }.unapply`
// and the call `P.unapply(x1, x2, ...)` is inlined.
val sym = t.symbol
val cls = ctx.newNormalizedClassSymbol(ctx.owner, tpnme.ANON_CLASS, Synthetic | Final, List(defn.ObjectType), coord = sym.coord)
val constr = ctx.newConstructor(cls, Synthetic, Nil, Nil, coord = sym.coord).entered
val unappplySym = ctx.newSymbol(cls, sym.name.toTermName, Synthetic | Method, sym.info, coord = sym.coord).entered
val unapply = polyDefDef(unappplySym, targs => argss =>
Inliner.inlineCall(ref(sym).appliedToTypes(targs).appliedToArgss(argss).withSpan(t.span))
)
val cdef = ClassDef(cls, DefDef(constr), List(unapply))
val newUnapply = Block(cdef :: Nil, New(cls.typeRef, Nil))
val targs = t.fun match
case TypeApply(_, targs) => targs
case _ => Nil
val newFun = newUnapply.select(unappplySym).appliedToTypeTrees(targs).withSpan(t.span)
cpy.UnApply(t)(newFun, t.implicits, t.patterns)
case t => t
}
}
Expand Down
5 changes: 0 additions & 5 deletions tests/neg/inine-unnapply.scala

This file was deleted.

15 changes: 0 additions & 15 deletions tests/neg/inline-unapply.scala

This file was deleted.

26 changes: 26 additions & 0 deletions tests/pos/i8530.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
object MyBoooleanUnapply:
inline def unapply(x: Int): Boolean = true

object MyOptionUnapply:
inline def unapply(x: Int): Option[Long] = Some(x)

object MyUnapplyImplicits:
inline def unapply(x: Int)(using DummyImplicit): Option[Long] = Some(x)

object MyPolyUnapply:
inline def unapply[T](x: T): Option[T] = Some(x)

object MySeqUnapply:
inline def unapplySeq(x: Int): Seq[Int] = Seq(x, x)

object MyWhiteboxUnapply:
inline def unapply(x: Int) <: Option[Any] = Some(x)

def test: Unit =
val x = 5 match
case MyBoooleanUnapply() =>
case MyOptionUnapply(y) => y: Long
case MyUnapplyImplicits(y) => y: Long
case MyPolyUnapply(a) => a: Int
case MySeqUnapply(a, b) => (a: Int, b: Int)
case MyWhiteboxUnapply(x) => x: Int
5 changes: 5 additions & 0 deletions tests/pos/inine-unnapply.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

object Foo {
inline def unapply(x: Any): Boolean = ???
inline def unapplySeq(x: Any): Seq[Any] = ???
}
15 changes: 15 additions & 0 deletions tests/pos/inline-unapply.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
object Test {

class C(val x: Int, val y: Int)

inline def unapply(c: C): Some[(Int, Int)] = Some((c.x, c.y))

}
object Test2 {

class C(x: Int, y: Int)

inline def unapply(c: C): Option[(Int, Int)] = inline c match {
case x: C => Some((1, 1))
}
}
1 change: 1 addition & 0 deletions tests/run-macros/i8530-2.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foo
12 changes: 12 additions & 0 deletions tests/run-macros/i8530/App_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

object Test {
def main(args: Array[String]): Unit = {
0 match
case Succ(n) => ???
case _ =>

2 match
case Succ(n) => assert(n == 1)
}

}
8 changes: 8 additions & 0 deletions tests/run-macros/i8530/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import scala.quoted._

object Succ:

inline def unapply(n: Int): Option[Int] = ${ impl('n) }

private def impl(n: Expr[Int])(using QuoteContext): Expr[Option[Int]] =
'{ if $n == 0 then None else Some($n - 1)}
5 changes: 5 additions & 0 deletions tests/run/i8530.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
MyBoooleanUnapply
2
3
(4,5)
5
33 changes: 33 additions & 0 deletions tests/run/i8530.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
object MyBoooleanUnapply:
inline def unapply(x: Int): Boolean = true

object MyOptionUnapply:
inline def unapply(x: Int): Option[Long] = Some(x)

object MyPolyUnapply:
inline def unapply[T](x: T): Option[T] = Some(x)

object MySeqUnapply:
inline def unapplySeq(x: Int): Seq[Int] = Seq(x, x + 1)

object MyWhiteboxUnapply:
inline def unapply(x: Int) <: Option[Any] = Some(x)


@main def Test =
1 match
case MyBoooleanUnapply() => println("MyBoooleanUnapply")

2 match
case MyOptionUnapply(y) => println(y)

3 match
case MyPolyUnapply(a) => println(a)

4 match
case MySeqUnapply(a, b) => println((a, b))

5 match
case MyWhiteboxUnapply(x) => println(x: Int)

end Test

0 comments on commit c98e14c

Please sign in to comment.