Skip to content

Support inline unapplySeq and with leading given parameters #16358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 11 additions & 20 deletions compiler/src/dotty/tools/dotc/inlines/Inlines.scala
Original file line number Diff line number Diff line change
Expand Up @@ -181,37 +181,28 @@ object Inlines:
// as its right hand side. The call to the wrapper unapply serves as the signpost for pattern matching.
// After pattern matching, the anonymous class is removed in phase InlinePatterns with a beta reduction step.
//
// An inline unapply `P.unapply` in a plattern `P(x1,x2,...)` is transformed into
// `{ class $anon { def unapply(t0: T0)(using t1: T1, t2: T2, ...): R = P.unapply(t0)(using t1, t2, ...) }; new $anon }.unapply`
// and the call `P.unapply(x1, x2, ...)` is inlined.
// An inline unapply `P.unapply` in a pattern `P[...](using ...)(x1,x2,...)(using t1: T1, t2: T2, ...)` is transformed into
// `{ 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,...)`
// and the call `P.unapply[...](using ...)(x1, x2, ...)(using t1, t2, ...)` is inlined.
// This serves as a placeholder for the inlined body until the `patternMatcher` phase. After pattern matcher
// transforms the patterns into terms, the `inlinePatterns` phase removes this anonymous class by β-reducing
// the call to the `unapply`.

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

val sym = unapp.symbol

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

val newUnapply = AnonClass(ctx.owner, List(defn.ObjectType), sym.coord) { cls =>
val targs = fun match
case TypeApply(_, targs) => targs
case _ => Nil
val unapplyInfo = sym.info match
case info: PolyType => info.instantiate(targs.map(_.tpe))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this needed anymore? The comment above could maybe explain how type parameters are handled.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's handled by fun.tpe.widen (line 202)

Copy link
Contributor Author

@nicolasstucki nicolasstucki Dec 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment below and managed to simplify the leading implicits logic thanks to the use of fun.tpe.widen.

Now it even works with something as complex as

  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]] = ???

case info => info

val unapplySym = newSymbol(cls, sym.name.toTermName, Synthetic | Method, unapplyInfo, coord = sym.coord).entered
// `fun` is a partially applied method that contains all type applications of the method.
// The methodic type `fun.tpe.widen` is the type of the function starting from the scrutinee argument
// and its type parameters are instantiated.
val unapplySym = newSymbol(cls, sym.name.toTermName, Synthetic | Method, fun.tpe.widen, coord = sym.coord).entered
val unapply = DefDef(unapplySym.asTerm, argss =>
inlineCall(fun.appliedToArgss(argss).withSpan(unapp.span))(using ctx.withOwner(unapplySym))
val body = fun.appliedToArgss(argss).withSpan(unapp.span)
if body.symbol.is(Transparent) then inlineCall(body)(using ctx.withOwner(unapplySym))
else body
)
unapplySym1 = unapplySym
List(unapply)
Expand Down
7 changes: 0 additions & 7 deletions tests/neg/i12991.scala

This file was deleted.

11 changes: 11 additions & 0 deletions tests/pos-macros/i8577a/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package i8577

import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

def implUnapply(sc: Expr[Macro.StrCtx], input: Expr[Int])(using Quotes): Expr[Option[Seq[Int]]] =
'{ Some(Seq(${input})) }
9 changes: 9 additions & 0 deletions tests/pos-macros/i8577a/Main_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package i8577

def main: Unit =
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension (inline ctx: Macro.StrCtx) inline def unapplySeq(inline input: Int): Option[Seq[Int]] =
${ implUnapply('ctx, 'input) }

val mac"$x" = 1
assert(x == 1)
11 changes: 11 additions & 0 deletions tests/pos-macros/i8577b/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package i8577

import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

def implUnapply[U](sc: Expr[Macro.StrCtx], input: Expr[U])(using Type[U])(using Quotes): Expr[Option[Seq[U]]] =
'{ Some(Seq(${input})) }
9 changes: 9 additions & 0 deletions tests/pos-macros/i8577b/Main_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package i8577

def main: Unit =
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: U): Option[Seq[U]] =
${ implUnapply('ctx, 'input) }

val mac"$x" = 1
assert(x == 1)
11 changes: 11 additions & 0 deletions tests/pos-macros/i8577c/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package i8577

import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

def implUnapply[T](sc: Expr[Macro.StrCtx], input: Expr[T])(using Type[T])(using Quotes): Expr[Option[Seq[T]]] =
'{ Some(Seq(${input})) }
9 changes: 9 additions & 0 deletions tests/pos-macros/i8577c/Main_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package i8577

def main: Unit =
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq(inline input: T): Option[Seq[T]] =
${ implUnapply('ctx, 'input) }

val mac"$x" = 1
assert(x == 1)
11 changes: 11 additions & 0 deletions tests/pos-macros/i8577d/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package i8577

import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

def implUnapply[T](sc: Expr[Macro.StrCtx], input: Expr[T])(using Type[T])(using Quotes): Expr[Option[Seq[T]]] =
'{ Some(Seq(${input})) }
9 changes: 9 additions & 0 deletions tests/pos-macros/i8577d/Main_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package i8577

def main: Unit =
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: T): Option[Seq[T]] =
${ implUnapply('ctx, 'input) }

val mac"$x" = 1
assert(x == 1)
11 changes: 11 additions & 0 deletions tests/pos-macros/i8577e/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package i8577

import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

def implUnapply[T, U](sc: Expr[Macro.StrCtx], input: Expr[U])(using Type[U])(using Quotes): Expr[Option[Seq[U]]] =
'{ Some(Seq(${input})) }
9 changes: 9 additions & 0 deletions tests/pos-macros/i8577e/Main_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package i8577

def main: Unit =
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: U): Option[Seq[U]] =
${ implUnapply('ctx, 'input) }

val mac"$x" = 1
assert(x == 1)
11 changes: 11 additions & 0 deletions tests/pos-macros/i8577f/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package i8577

import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

def implUnapply[T, U](sc: Expr[Macro.StrCtx], input: Expr[(T, U)])(using Type[T], Type[U])(using Quotes): Expr[Option[Seq[(T, U)]]] =
'{ Some(Seq(${input})) }
12 changes: 12 additions & 0 deletions tests/pos-macros/i8577f/Main_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package i8577

def main: Unit =
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: (T, U)): Option[Seq[(T, U)]] =
${ implUnapply('ctx, 'input) }

val mac"$x" = (1, 2)
assert(x == (1, 2))

val mac"$y" = (1, "a")
assert(y == (1, "a"))
11 changes: 11 additions & 0 deletions tests/pos-macros/i8577g/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package i8577

import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

def implUnapply[T, U](sc: Expr[Macro.StrCtx], input: Expr[T | U])(using Type[T], Type[U])(using Quotes): Expr[Option[Seq[T | U]]] =
'{ Some(Seq(${input})) }
9 changes: 9 additions & 0 deletions tests/pos-macros/i8577g/Main_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package i8577

def main: Unit =
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: T | U): Option[Seq[T | U]] =
${ implUnapply('ctx, 'input) }

val mac"$x" = 1
assert(x == 1)
11 changes: 11 additions & 0 deletions tests/pos-macros/i8577h/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package i8577

import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

def implUnapply[T, U](sc: Expr[Macro.StrCtx], input: Expr[T | U])(using Type[T], Type[U])(using Quotes): Expr[Option[Seq[T | U]]] =
'{ Some(Seq(${input})) }
9 changes: 9 additions & 0 deletions tests/pos-macros/i8577h/Main_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package i8577

def main: Unit =
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: U | T): Option[Seq[T | U]] =
${ implUnapply('ctx, 'input) }

val mac"$x" = 1
assert(x == 1)
15 changes: 15 additions & 0 deletions tests/pos/i12991.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
object Foo:
inline def unapply(using String)(i: Int): Some[Int] = Some(i)

object Bar:
inline def unapply(using String)(using String)(i: Int): Some[Int] = Some(i)

object Baz:
inline def unapply[T](using String)(i: T): Some[T] = Some(i)

given String = ""

val i = 10 match
case Foo(x) => x
case Bar(x) => x
case Baz(x) => x
9 changes: 9 additions & 0 deletions tests/pos/i15188.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
object O

extension [T] (ctx: O.type) inline def unapplySeq(input: T): Option[Seq[T]] = Some(Seq(input))

@main
def Main = {
val O(x) = 3
println(s"x: $x")
}
8 changes: 8 additions & 0 deletions tests/pos/i15188b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class C

extension (ctx: C) inline def unapply(input: String): Option[String] = Some("hi")

@main def run = {
val O = new C
val O(x) = "3"
}
21 changes: 21 additions & 0 deletions tests/pos/i8577.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package i8577

type A; given A: A = ???;
type B; given B: B = ???;
type C; given C: C = ???;
type D; given D: D = ???;
type E; given E: E = ???;
type F; given F: F = ???;


object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

def main: Unit =
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
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]] = ???

(??? : Int) match
case mac"${x}" => 1
15 changes: 15 additions & 0 deletions tests/run/i8577a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension (inline ctx: Macro.StrCtx) inline def unapplySeq(inline input: Int): Option[Seq[Int]] =
Some(Seq(input))

@main def Test: Unit =
val mac"$x" = 1
val y: Int = x
assert(x == 1)
15 changes: 15 additions & 0 deletions tests/run/i8577b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: U): Option[Seq[U]] =
Some(Seq(input))

@main def Test: Unit =
val mac"$x" = 1
val y: Int = x
assert(x == 1)
15 changes: 15 additions & 0 deletions tests/run/i8577c.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq(inline input: T): Option[Seq[T]] =
Some(Seq(input))

@main def Test: Unit =
val mac"$x" = 1
val y: Int = x
assert(x == 1)
15 changes: 15 additions & 0 deletions tests/run/i8577d.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: T): Option[Seq[T]] =
Some(Seq(input))

@main def Test: Unit =
val mac"$x" = 1
val y: Int = x
assert(x == 1)
19 changes: 19 additions & 0 deletions tests/run/i8577e.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension [T] (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: (T, U)): Option[Seq[(T, U)]] =
Some(Seq(input))

@main def Test: Unit =
val mac"$x" = (1, 2)
val x2: (Int, Int) = x
assert(x == (1, 2))

val mac"$y" = (1, "a")
val y2: (Int, String) = y
assert(y == (1, "a"))
15 changes: 15 additions & 0 deletions tests/run/i8577f.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import scala.quoted._

object Macro:
opaque type StrCtx = StringContext
def apply(ctx: StringContext): StrCtx = ctx
def unapply(ctx: StrCtx): Option[StringContext] = Some(ctx)

@main def Test: Unit =
extension (ctx: StringContext) def mac: Macro.StrCtx = Macro(ctx)
extension (inline ctx: Macro.StrCtx) inline def unapplySeq[U](inline input: U): Option[Seq[U]] =
Some(Seq(input))

val mac"$x" = 1
val y: Int = x
assert(x == 1)
Loading