Skip to content

Commit c98e14c

Browse files
committed
Fix #8530: Support inline unapply
1 parent ea27651 commit c98e14c

File tree

12 files changed

+123
-22
lines changed

12 files changed

+123
-22
lines changed

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,6 @@ object PrepareInlineable {
241241
ctx.error(em"Implementation restriction: No inline methods allowed where opaque type aliases are in scope", inlined.sourcePos)
242242
if (ctx.outer.inInlineMethod)
243243
ctx.error(ex"Implementation restriction: nested inline methods are not supported", inlined.sourcePos)
244-
if (inlined.name.isUnapplyName)
245-
ctx.error(em"Implementation restriction: inline ${inlined.name} methods are not supported", inlined.sourcePos)
246244

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

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,24 @@ class Typer extends Namer
12551255
if (bounds != null) sym.info = bounds
12561256
}
12571257
b
1258+
case t: UnApply if t.symbol.is(Inline) =>
1259+
// An inline unapply `P.unapply` in a plattern `P(x1,x2,...)` is transformed into
1260+
// `{ class $anon { def unapply(x1: T1, x2: T2, ...): R = P.unapply(x1, x2, ...) }; new $anon }.unapply`
1261+
// and the call `P.unapply(x1, x2, ...)` is inlined.
1262+
val sym = t.symbol
1263+
val cls = ctx.newNormalizedClassSymbol(ctx.owner, tpnme.ANON_CLASS, Synthetic | Final, List(defn.ObjectType), coord = sym.coord)
1264+
val constr = ctx.newConstructor(cls, Synthetic, Nil, Nil, coord = sym.coord).entered
1265+
val unappplySym = ctx.newSymbol(cls, sym.name.toTermName, Synthetic | Method, sym.info, coord = sym.coord).entered
1266+
val unapply = polyDefDef(unappplySym, targs => argss =>
1267+
Inliner.inlineCall(ref(sym).appliedToTypes(targs).appliedToArgss(argss).withSpan(t.span))
1268+
)
1269+
val cdef = ClassDef(cls, DefDef(constr), List(unapply))
1270+
val newUnapply = Block(cdef :: Nil, New(cls.typeRef, Nil))
1271+
val targs = t.fun match
1272+
case TypeApply(_, targs) => targs
1273+
case _ => Nil
1274+
val newFun = newUnapply.select(unappplySym).appliedToTypeTrees(targs).withSpan(t.span)
1275+
cpy.UnApply(t)(newFun, t.implicits, t.patterns)
12581276
case t => t
12591277
}
12601278
}

tests/neg/inine-unnapply.scala

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/neg/inline-unapply.scala

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/pos/i8530.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
object MyBoooleanUnapply:
2+
inline def unapply(x: Int): Boolean = true
3+
4+
object MyOptionUnapply:
5+
inline def unapply(x: Int): Option[Long] = Some(x)
6+
7+
object MyUnapplyImplicits:
8+
inline def unapply(x: Int)(using DummyImplicit): Option[Long] = Some(x)
9+
10+
object MyPolyUnapply:
11+
inline def unapply[T](x: T): Option[T] = Some(x)
12+
13+
object MySeqUnapply:
14+
inline def unapplySeq(x: Int): Seq[Int] = Seq(x, x)
15+
16+
object MyWhiteboxUnapply:
17+
inline def unapply(x: Int) <: Option[Any] = Some(x)
18+
19+
def test: Unit =
20+
val x = 5 match
21+
case MyBoooleanUnapply() =>
22+
case MyOptionUnapply(y) => y: Long
23+
case MyUnapplyImplicits(y) => y: Long
24+
case MyPolyUnapply(a) => a: Int
25+
case MySeqUnapply(a, b) => (a: Int, b: Int)
26+
case MyWhiteboxUnapply(x) => x: Int

tests/pos/inine-unnapply.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
object Foo {
3+
inline def unapply(x: Any): Boolean = ???
4+
inline def unapplySeq(x: Any): Seq[Any] = ???
5+
}

tests/pos/inline-unapply.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
object Test {
2+
3+
class C(val x: Int, val y: Int)
4+
5+
inline def unapply(c: C): Some[(Int, Int)] = Some((c.x, c.y))
6+
7+
}
8+
object Test2 {
9+
10+
class C(x: Int, y: Int)
11+
12+
inline def unapply(c: C): Option[(Int, Int)] = inline c match {
13+
case x: C => Some((1, 1))
14+
}
15+
}

tests/run-macros/i8530-2.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
foo

tests/run-macros/i8530/App_2.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
object Test {
3+
def main(args: Array[String]): Unit = {
4+
0 match
5+
case Succ(n) => ???
6+
case _ =>
7+
8+
2 match
9+
case Succ(n) => assert(n == 1)
10+
}
11+
12+
}

tests/run-macros/i8530/Macro_1.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import scala.quoted._
2+
3+
object Succ:
4+
5+
inline def unapply(n: Int): Option[Int] = ${ impl('n) }
6+
7+
private def impl(n: Expr[Int])(using QuoteContext): Expr[Option[Int]] =
8+
'{ if $n == 0 then None else Some($n - 1)}

tests/run/i8530.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
MyBoooleanUnapply
2+
2
3+
3
4+
(4,5)
5+
5

tests/run/i8530.scala

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
object MyBoooleanUnapply:
2+
inline def unapply(x: Int): Boolean = true
3+
4+
object MyOptionUnapply:
5+
inline def unapply(x: Int): Option[Long] = Some(x)
6+
7+
object MyPolyUnapply:
8+
inline def unapply[T](x: T): Option[T] = Some(x)
9+
10+
object MySeqUnapply:
11+
inline def unapplySeq(x: Int): Seq[Int] = Seq(x, x + 1)
12+
13+
object MyWhiteboxUnapply:
14+
inline def unapply(x: Int) <: Option[Any] = Some(x)
15+
16+
17+
@main def Test =
18+
1 match
19+
case MyBoooleanUnapply() => println("MyBoooleanUnapply")
20+
21+
2 match
22+
case MyOptionUnapply(y) => println(y)
23+
24+
3 match
25+
case MyPolyUnapply(a) => println(a)
26+
27+
4 match
28+
case MySeqUnapply(a, b) => println((a, b))
29+
30+
5 match
31+
case MyWhiteboxUnapply(x) => println(x: Int)
32+
33+
end Test

0 commit comments

Comments
 (0)