Skip to content

Commit ab01e88

Browse files
Support inline unapplySeq and with leading given parameters (#16358)
Based on #15191 by @gorilskij and enables support for #12991. Fixes #8577 Fixes #12991 Fixes #15188
2 parents 82abb46 + 4fdb20c commit ab01e88

31 files changed

+367
-27
lines changed

compiler/src/dotty/tools/dotc/inlines/Inlines.scala

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -181,37 +181,28 @@ object Inlines:
181181
// as its right hand side. The call to the wrapper unapply serves as the signpost for pattern matching.
182182
// After pattern matching, the anonymous class is removed in phase InlinePatterns with a beta reduction step.
183183
//
184-
// An inline unapply `P.unapply` in a plattern `P(x1,x2,...)` is transformed into
185-
// `{ class $anon { def unapply(t0: T0)(using t1: T1, t2: T2, ...): R = P.unapply(t0)(using t1, t2, ...) }; new $anon }.unapply`
186-
// and the call `P.unapply(x1, x2, ...)` is inlined.
184+
// An inline unapply `P.unapply` in a pattern `P[...](using ...)(x1,x2,...)(using t1: T1, t2: T2, ...)` is transformed into
185+
// `{ class $anon { def unapply(s: S)(using t1: T1, t2: T2, ...): R = P.unapply[...](using ...)(s)(using t1, t2, ...) }; new $anon }.unapply(using y1,y2,...)`
186+
// and the call `P.unapply[...](using ...)(x1, x2, ...)(using t1, t2, ...)` is inlined.
187187
// This serves as a placeholder for the inlined body until the `patternMatcher` phase. After pattern matcher
188188
// transforms the patterns into terms, the `inlinePatterns` phase removes this anonymous class by β-reducing
189189
// the call to the `unapply`.
190190

191-
object SplitFunAndGivenArgs:
192-
def unapply(tree: Tree): (Tree, List[List[Tree]]) = tree match
193-
case Apply(SplitFunAndGivenArgs(fn, argss), args) => (fn, argss :+ args)
194-
case _ => (tree, Nil)
195-
val UnApply(SplitFunAndGivenArgs(fun, leadingImplicits), trailingImplicits, patterns) = unapp
196-
if leadingImplicits.flatten.nonEmpty then
197-
// To support them see https://github.com/lampepfl/dotty/pull/13158
198-
report.error("inline unapply methods with given parameters before the scrutinee are not supported", fun)
191+
val UnApply(fun, trailingImplicits, patterns) = unapp
199192

200193
val sym = unapp.symbol
201194

202195
var unapplySym1: Symbol = NoSymbol // created from within AnonClass() and used afterwards
203196

204197
val newUnapply = AnonClass(ctx.owner, List(defn.ObjectType), sym.coord) { cls =>
205-
val targs = fun match
206-
case TypeApply(_, targs) => targs
207-
case _ => Nil
208-
val unapplyInfo = sym.info match
209-
case info: PolyType => info.instantiate(targs.map(_.tpe))
210-
case info => info
211-
212-
val unapplySym = newSymbol(cls, sym.name.toTermName, Synthetic | Method, unapplyInfo, coord = sym.coord).entered
198+
// `fun` is a partially applied method that contains all type applications of the method.
199+
// The methodic type `fun.tpe.widen` is the type of the function starting from the scrutinee argument
200+
// and its type parameters are instantiated.
201+
val unapplySym = newSymbol(cls, sym.name.toTermName, Synthetic | Method, fun.tpe.widen, coord = sym.coord).entered
213202
val unapply = DefDef(unapplySym.asTerm, argss =>
214-
inlineCall(fun.appliedToArgss(argss).withSpan(unapp.span))(using ctx.withOwner(unapplySym))
203+
val body = fun.appliedToArgss(argss).withSpan(unapp.span)
204+
if body.symbol.is(Transparent) then inlineCall(body)(using ctx.withOwner(unapplySym))
205+
else body
215206
)
216207
unapplySym1 = unapplySym
217208
List(unapply)

tests/neg/i12991.scala

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

tests/pos-macros/i8577a/Macro_1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package i8577
2+
3+
import scala.quoted._
4+
5+
object Macro:
6+
opaque type StrCtx = StringContext
7+
def apply(ctx: StringContext): StrCtx = ctx
8+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
9+
10+
def implUnapply(sc: Expr[Macro.StrCtx], input: Expr[Int])(using Quotes): Expr[Option[Seq[Int]]] =
11+
'{ Some(Seq(${input})) }

tests/pos-macros/i8577a/Main_2.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package i8577
2+
3+
def main: Unit =
4+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
5+
extension (inline ctx: Macro.StrCtx) inline def unapplySeq(inline input: Int): Option[Seq[Int]] =
6+
${ implUnapply('ctx, 'input) }
7+
8+
val mac"$x" = 1
9+
assert(x == 1)

tests/pos-macros/i8577b/Macro_1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package i8577
2+
3+
import scala.quoted._
4+
5+
object Macro:
6+
opaque type StrCtx = StringContext
7+
def apply(ctx: StringContext): StrCtx = ctx
8+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
9+
10+
def implUnapply[U](sc: Expr[Macro.StrCtx], input: Expr[U])(using Type[U])(using Quotes): Expr[Option[Seq[U]]] =
11+
'{ Some(Seq(${input})) }

tests/pos-macros/i8577b/Main_2.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package i8577
2+
3+
def main: Unit =
4+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
5+
extension (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: U): Option[Seq[U]] =
6+
${ implUnapply('ctx, 'input) }
7+
8+
val mac"$x" = 1
9+
assert(x == 1)

tests/pos-macros/i8577c/Macro_1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package i8577
2+
3+
import scala.quoted._
4+
5+
object Macro:
6+
opaque type StrCtx = StringContext
7+
def apply(ctx: StringContext): StrCtx = ctx
8+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
9+
10+
def implUnapply[T](sc: Expr[Macro.StrCtx], input: Expr[T])(using Type[T])(using Quotes): Expr[Option[Seq[T]]] =
11+
'{ Some(Seq(${input})) }

tests/pos-macros/i8577c/Main_2.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package i8577
2+
3+
def main: Unit =
4+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
5+
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq(inline input: T): Option[Seq[T]] =
6+
${ implUnapply('ctx, 'input) }
7+
8+
val mac"$x" = 1
9+
assert(x == 1)

tests/pos-macros/i8577d/Macro_1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package i8577
2+
3+
import scala.quoted._
4+
5+
object Macro:
6+
opaque type StrCtx = StringContext
7+
def apply(ctx: StringContext): StrCtx = ctx
8+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
9+
10+
def implUnapply[T](sc: Expr[Macro.StrCtx], input: Expr[T])(using Type[T])(using Quotes): Expr[Option[Seq[T]]] =
11+
'{ Some(Seq(${input})) }

tests/pos-macros/i8577d/Main_2.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package i8577
2+
3+
def main: Unit =
4+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
5+
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: T): Option[Seq[T]] =
6+
${ implUnapply('ctx, 'input) }
7+
8+
val mac"$x" = 1
9+
assert(x == 1)

tests/pos-macros/i8577e/Macro_1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package i8577
2+
3+
import scala.quoted._
4+
5+
object Macro:
6+
opaque type StrCtx = StringContext
7+
def apply(ctx: StringContext): StrCtx = ctx
8+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
9+
10+
def implUnapply[T, U](sc: Expr[Macro.StrCtx], input: Expr[U])(using Type[U])(using Quotes): Expr[Option[Seq[U]]] =
11+
'{ Some(Seq(${input})) }

tests/pos-macros/i8577e/Main_2.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package i8577
2+
3+
def main: Unit =
4+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
5+
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: U): Option[Seq[U]] =
6+
${ implUnapply('ctx, 'input) }
7+
8+
val mac"$x" = 1
9+
assert(x == 1)

tests/pos-macros/i8577f/Macro_1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package i8577
2+
3+
import scala.quoted._
4+
5+
object Macro:
6+
opaque type StrCtx = StringContext
7+
def apply(ctx: StringContext): StrCtx = ctx
8+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
9+
10+
def implUnapply[T, U](sc: Expr[Macro.StrCtx], input: Expr[(T, U)])(using Type[T], Type[U])(using Quotes): Expr[Option[Seq[(T, U)]]] =
11+
'{ Some(Seq(${input})) }

tests/pos-macros/i8577f/Main_2.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package i8577
2+
3+
def main: Unit =
4+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
5+
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: (T, U)): Option[Seq[(T, U)]] =
6+
${ implUnapply('ctx, 'input) }
7+
8+
val mac"$x" = (1, 2)
9+
assert(x == (1, 2))
10+
11+
val mac"$y" = (1, "a")
12+
assert(y == (1, "a"))

tests/pos-macros/i8577g/Macro_1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package i8577
2+
3+
import scala.quoted._
4+
5+
object Macro:
6+
opaque type StrCtx = StringContext
7+
def apply(ctx: StringContext): StrCtx = ctx
8+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
9+
10+
def implUnapply[T, U](sc: Expr[Macro.StrCtx], input: Expr[T | U])(using Type[T], Type[U])(using Quotes): Expr[Option[Seq[T | U]]] =
11+
'{ Some(Seq(${input})) }

tests/pos-macros/i8577g/Main_2.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package i8577
2+
3+
def main: Unit =
4+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
5+
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: T | U): Option[Seq[T | U]] =
6+
${ implUnapply('ctx, 'input) }
7+
8+
val mac"$x" = 1
9+
assert(x == 1)

tests/pos-macros/i8577h/Macro_1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package i8577
2+
3+
import scala.quoted._
4+
5+
object Macro:
6+
opaque type StrCtx = StringContext
7+
def apply(ctx: StringContext): StrCtx = ctx
8+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
9+
10+
def implUnapply[T, U](sc: Expr[Macro.StrCtx], input: Expr[T | U])(using Type[T], Type[U])(using Quotes): Expr[Option[Seq[T | U]]] =
11+
'{ Some(Seq(${input})) }

tests/pos-macros/i8577h/Main_2.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package i8577
2+
3+
def main: Unit =
4+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
5+
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: U | T): Option[Seq[T | U]] =
6+
${ implUnapply('ctx, 'input) }
7+
8+
val mac"$x" = 1
9+
assert(x == 1)

tests/pos/i12991.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
object Foo:
2+
inline def unapply(using String)(i: Int): Some[Int] = Some(i)
3+
4+
object Bar:
5+
inline def unapply(using String)(using String)(i: Int): Some[Int] = Some(i)
6+
7+
object Baz:
8+
inline def unapply[T](using String)(i: T): Some[T] = Some(i)
9+
10+
given String = ""
11+
12+
val i = 10 match
13+
case Foo(x) => x
14+
case Bar(x) => x
15+
case Baz(x) => x

tests/pos/i15188.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object O
2+
3+
extension [T] (ctx: O.type) inline def unapplySeq(input: T): Option[Seq[T]] = Some(Seq(input))
4+
5+
@main
6+
def Main = {
7+
val O(x) = 3
8+
println(s"x: $x")
9+
}

tests/pos/i15188b.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class C
2+
3+
extension (ctx: C) inline def unapply(input: String): Option[String] = Some("hi")
4+
5+
@main def run = {
6+
val O = new C
7+
val O(x) = "3"
8+
}

tests/pos/i8577.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package i8577
2+
3+
type A; given A: A = ???;
4+
type B; given B: B = ???;
5+
type C; given C: C = ???;
6+
type D; given D: D = ???;
7+
type E; given E: E = ???;
8+
type F; given F: F = ???;
9+
10+
11+
object Macro:
12+
opaque type StrCtx = StringContext
13+
def apply(ctx: StringContext): StrCtx = ctx
14+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
15+
16+
def main: Unit =
17+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
18+
extension [T] (using A)(inline ctx: Macro.StrCtx)(using B) inline def unapplySeq[U](using C)(inline input: T)(using D)(using F): Option[Seq[T]] = ???
19+
20+
(??? : Int) match
21+
case mac"${x}" => 1

tests/run/i8577a.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import scala.quoted._
2+
3+
object Macro:
4+
opaque type StrCtx = StringContext
5+
def apply(ctx: StringContext): StrCtx = ctx
6+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
7+
8+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
9+
extension (inline ctx: Macro.StrCtx) inline def unapplySeq(inline input: Int): Option[Seq[Int]] =
10+
Some(Seq(input))
11+
12+
@main def Test: Unit =
13+
val mac"$x" = 1
14+
val y: Int = x
15+
assert(x == 1)

tests/run/i8577b.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import scala.quoted._
2+
3+
object Macro:
4+
opaque type StrCtx = StringContext
5+
def apply(ctx: StringContext): StrCtx = ctx
6+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
7+
8+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
9+
extension (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: U): Option[Seq[U]] =
10+
Some(Seq(input))
11+
12+
@main def Test: Unit =
13+
val mac"$x" = 1
14+
val y: Int = x
15+
assert(x == 1)

tests/run/i8577c.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import scala.quoted._
2+
3+
object Macro:
4+
opaque type StrCtx = StringContext
5+
def apply(ctx: StringContext): StrCtx = ctx
6+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
7+
8+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
9+
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq(inline input: T): Option[Seq[T]] =
10+
Some(Seq(input))
11+
12+
@main def Test: Unit =
13+
val mac"$x" = 1
14+
val y: Int = x
15+
assert(x == 1)

tests/run/i8577d.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import scala.quoted._
2+
3+
object Macro:
4+
opaque type StrCtx = StringContext
5+
def apply(ctx: StringContext): StrCtx = ctx
6+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
7+
8+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
9+
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: T): Option[Seq[T]] =
10+
Some(Seq(input))
11+
12+
@main def Test: Unit =
13+
val mac"$x" = 1
14+
val y: Int = x
15+
assert(x == 1)

tests/run/i8577e.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import scala.quoted._
2+
3+
object Macro:
4+
opaque type StrCtx = StringContext
5+
def apply(ctx: StringContext): StrCtx = ctx
6+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
7+
8+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
9+
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: (T, U)): Option[Seq[(T, U)]] =
10+
Some(Seq(input))
11+
12+
@main def Test: Unit =
13+
val mac"$x" = (1, 2)
14+
val x2: (Int, Int) = x
15+
assert(x == (1, 2))
16+
17+
val mac"$y" = (1, "a")
18+
val y2: (Int, String) = y
19+
assert(y == (1, "a"))

tests/run/i8577f.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import scala.quoted._
2+
3+
object Macro:
4+
opaque type StrCtx = StringContext
5+
def apply(ctx: StringContext): StrCtx = ctx
6+
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)
7+
8+
@main def Test: Unit =
9+
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
10+
extension (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: U): Option[Seq[U]] =
11+
Some(Seq(input))
12+
13+
val mac"$x" = 1
14+
val y: Int = x
15+
assert(x == 1)

0 commit comments

Comments
 (0)