Skip to content

Commit 8f94e14

Browse files
committed
Fix scala#8530: Support inline unapply
1 parent b64b8a9 commit 8f94e14

File tree

9 files changed

+93
-7
lines changed

9 files changed

+93
-7
lines changed

compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -378,10 +378,16 @@ object PatternMatcher {
378378
// This plan will never execute because it'll be guarded by a `NonNullTest`.
379379
ResultPlan(tpd.Throw(tpd.nullLiteral))
380380
else {
381-
val mt @ MethodType(_) = extractor.tpe.widen
382-
var unapp = extractor.appliedTo(ref(scrutinee).ensureConforms(mt.paramInfos.head))
383-
if (implicits.nonEmpty) unapp = unapp.appliedToArgs(implicits)
384-
unapplyPlan(unapp, args)
381+
extractor.tpe.widen match
382+
case mt @ MethodType(_) =>
383+
var unapp = extractor.appliedTo(ref(scrutinee).ensureConforms(mt.paramInfos.head))
384+
if (implicits.nonEmpty) unapp = unapp.appliedToArgs(implicits)
385+
unapplyPlan(unapp, args)
386+
case app: AppliedType =>
387+
// TODO optimize away the lambda
388+
var unapp = extractor.select(nme.apply).appliedTo(ref(scrutinee).ensureConforms(app.argInfos.head))
389+
if (implicits.nonEmpty) unapp = unapp.appliedToArgs(implicits)
390+
unapplyPlan(unapp, args)
385391
}
386392
if (scrutinee.info.isNotNull || nonNull(scrutinee)) unappPlan
387393
else TestPlan(NonNullTest, scrutinee, tree.span, unappPlan)

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: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,22 @@ class Typer extends Namer
12551255
if (bounds != null) sym.info = bounds
12561256
}
12571257
b
1258+
case t: UnApply if t.symbol.is(Inline) =>
1259+
val sym = t.symbol
1260+
val cls = ctx.newNormalizedClassSymbol(ctx.owner, tpnme.ANON_CLASS, Synthetic | Final, List(defn.ObjectType), coord = t.span)
1261+
val constr = ctx.newConstructor(cls, Synthetic, Nil, Nil).entered
1262+
val unappplySym = ctx.newSymbol(cls, sym.name.toTermName, Synthetic | Method, sym.info).entered
1263+
val unapply = polyDefDef(unappplySym, targs => argss =>
1264+
Inliner.inlineCall(ref(sym).appliedToTypes(targs).appliedToArgss(argss).withSpan(t.span))
1265+
)
1266+
val cdef = ClassDef(cls, DefDef(constr), List(unapply))
1267+
val newUnapply = Block(cdef :: Nil, New(cls.typeRef, Nil))
1268+
val targs = t.fun match
1269+
case TypeApply(_, targs) => targs
1270+
case _ => Nil
1271+
val newFun = newUnapply.select(unappplySym).appliedToTypeTrees(targs).withSpan(t.span)
1272+
assert(t.implicits.isEmpty)
1273+
cpy.UnApply(t)(newFun, t.implicits, t.patterns)
12581274
case t => t
12591275
}
12601276
}

compiler/test/dotc/pos-test-pickling.blacklist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ reference
1212
scala-days-2019-slides
1313
i7048e.scala
1414
i8052.scala
15+
i8530.scala
1516

1617
# Stale symbol: package object scala
1718
seqtype-cycle

compiler/test/dotc/run-test-pickling.blacklist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ t10889
2323
macros-in-same-project1
2424
i5257.scala
2525
i7868.scala
26+
i8530.scala
2627
enum-java
2728
zero-arity-case-class.scala
2829
tuple-ops.scala

tests/neg/inine-unnapply.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

22
object Foo {
3-
inline def unapply(x: Any): Boolean = ??? // error: Implementation restriction: inline unapply methods are not supported
3+
inline def unapply(x: Any): Boolean = ???
44
inline def unapplySeq(x: Any): Seq[Any] = ??? // error: Implementation restriction: inline unapplySeq methods are not supported
55
}

tests/pos/i8530.scala

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

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

0 commit comments

Comments
 (0)