Skip to content

Fix usage of outer type argument in extension inline unapply #15189

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

Closed
wants to merge 15 commits into from
Closed
18 changes: 12 additions & 6 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ object Inliner {
// 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
// An inline unapply `P.unapply` in a pattern `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.
// This serves as a placeholder for the inlined body until the `patternMatcher` phase. After pattern matcher
Expand All @@ -210,18 +210,24 @@ object Inliner {

val targs = fun match
case TypeApply(_, targs) => targs
case Apply(TypeApply(_, targs), _) => targs
case _ => Nil
val unapplyInfo = sym.info match
case info: PolyType => info.instantiate(targs.map(_.tpe))
case info: PolyType => info.instantiate(targs.map(_.tpe)) match
case MethodTpe(_, _, rt: PolyType) => rt.instantiate(targs.map(_.tpe))
case MethodTpe(_, _, rt) if sym.flags.is(ExtensionMethod) => rt
case info => info
case MethodTpe(_, _, rt: PolyType) => rt.instantiate(targs.map(_.tpe))
case MethodTpe(_, _, rt) if sym.flags.is(ExtensionMethod) => rt
case info => info

val unappplySym = newSymbol(cls, sym.name.toTermName, Synthetic | Method, unapplyInfo, coord = sym.coord).entered
val unapply = DefDef(unappplySym, argss =>
inlineCall(fun.appliedToArgss(argss).withSpan(unapp.span))(using ctx.withOwner(unappplySym))
val unapplySym = newSymbol(cls, sym.name.toTermName, Synthetic | Method, unapplyInfo, coord = sym.coord).entered
val unapply = DefDef(unapplySym, argss =>
inlineCall(fun.appliedToArgss(argss).withSpan(unapp.span))(using ctx.withOwner(unapplySym))
)
val cdef = ClassDef(cls, DefDef(constr), List(unapply))
val newUnapply = Block(cdef :: Nil, New(cls.typeRef, Nil))
val newFun = newUnapply.select(unappplySym).withSpan(unapp.span)
val newFun = newUnapply.select(unapplySym).withSpan(unapp.span)
cpy.UnApply(unapp)(newFun, implicits, patterns)
}

Expand Down
12 changes: 12 additions & 0 deletions tests/pos/i8577/MacroA_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package i8577

import scala.quoted._

object MacroA:
opaque type StringContext = scala.StringContext
def apply(ctx: scala.StringContext): StringContext = ctx
def unapply(ctx: StringContext): Option[scala.StringContext] = Some(ctx)

def implUnapplyA(sc: Expr[MacroB.StringContext], input: Expr[Int])
(using Quotes): Expr[Option[Seq[Int]]] =
'{ Some(Seq(${input})) }
12 changes: 12 additions & 0 deletions tests/pos/i8577/MacroB_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package i8577

import scala.quoted._

object MacroB:
opaque type StringContext = scala.StringContext
def apply(ctx: scala.StringContext): StringContext = ctx
def unapply(ctx: StringContext): Option[scala.StringContext] = Some(ctx)

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

import scala.quoted._

object MacroC:
opaque type StringContext = scala.StringContext
def apply(ctx: scala.StringContext): StringContext = ctx
def unapply(ctx: StringContext): Option[scala.StringContext] = Some(ctx)

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

import scala.quoted._

object MacroD:
opaque type StringContext = scala.StringContext
def apply(ctx: scala.StringContext): StringContext = ctx
def unapply(ctx: StringContext): Option[scala.StringContext] = Some(ctx)

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

import scala.quoted._

object MacroE:
opaque type StringContext = scala.StringContext
def apply(ctx: scala.StringContext): StringContext = ctx
def unapply(ctx: StringContext): Option[scala.StringContext] = Some(ctx)

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

import scala.quoted._

object MacroF:
opaque type StringContext = scala.StringContext
def apply(ctx: scala.StringContext): StringContext = ctx
def unapply(ctx: StringContext): Option[scala.StringContext] = Some(ctx)

def implUnapplyF[T, U](sc: Expr[MacroF.StringContext], 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/i8577/MacroG_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package i8577

import scala.quoted._

object MacroG:
opaque type StringContext = scala.StringContext
def apply(ctx: scala.StringContext): StringContext = ctx
def unapply(ctx: StringContext): Option[scala.StringContext] = Some(ctx)

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

def main: Unit = {
{
extension (ctx: StringContext) def macroA: MacroB.StringContext = MacroB(ctx)
extension (inline ctx: MacroB.StringContext) inline def unapplySeq(inline input: Int): Option[Seq[Int]] =
${implUnapplyA('ctx, 'input)}

val macroA"$xA" = 1
}

{
extension (ctx: StringContext) def macroB: MacroB.StringContext = MacroB(ctx)
extension (inline ctx: MacroB.StringContext) inline def unapplySeq[U](inline input: U): Option[Seq[U]] =
${ implUnapplyB('ctx, 'input) }

val macroB"$xB" = 2
}

{
extension (ctx: StringContext) def macroC: MacroC.StringContext = MacroC(ctx)
extension [T] (inline ctx: MacroC.StringContext) inline def unapplySeq(inline input: T): Option[Seq[T]] =
${ implUnapplyC('ctx, 'input) }

val macroC"$xC" = 3
}

{
extension (ctx: StringContext) def macroD: MacroD.StringContext = MacroD(ctx)
extension [T] (inline ctx: MacroD.StringContext) inline def unapplySeq[U](inline input: T): Option[Seq[T]] =
${ implUnapplyD('ctx, 'input) }

// miscompilation
// val macroD"$xD" = 4
Copy link
Contributor

Choose a reason for hiding this comment

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

If the intentional that this should not compile?
If so, you could create a separate netagive test in tests/neg/

}

{
extension (ctx: StringContext) def macroE: MacroE.StringContext = MacroE(ctx)
extension [T] (inline ctx: MacroE.StringContext) inline def unapplySeq[U](inline input: U): Option[Seq[U]] =
${ implUnapplyE('ctx, 'input) }

val macroE"$xE" = 5
}

{
extension (ctx: StringContext) def macroF: MacroF.StringContext = MacroF(ctx)
extension [T] (inline ctx: MacroF.StringContext) inline def unapplySeq[U](inline input: (T, U)): Option[Seq[(T, U)]] =
${ implUnapplyF('ctx, 'input) }

val macroF"$xF" = (6, 7)

// miscompilation
// val macroF"$xF" = (6, "7")
}

{
extension (ctx: StringContext) def macroG: MacroG.StringContext = MacroG(ctx)
extension [T] (inline ctx: MacroG.StringContext) inline def unapplySeq[U](inline input: T | U): Option[Seq[T | U]] =
${ implUnapplyG('ctx, 'input) }

// compiler error
// val macroG"$xG" = 8
}
}

// {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this section commented out?

// // B
// object F2
// extension (o: F2.type) inline def unapplySeq[T](inline x: T) = Some(Seq(x))
//
// val F2(x) = 16
// println(s"F2: $x")
// }
//
// {
// // C
// object F1
// extension [T] (o: F1.type) inline def unapplySeq(inline x: T) = Some(Seq(x))
//
// val F1(x) = 15
// println(s"F1: $x")
// }
//
// {
// // D
// object F4b
// extension [T] (o: F4b.type) inline def unapplySeq[U](inline x: T) = Some(Seq(x))
//
// val F4b(x) = 18.2
// println(s"F4b: $x")
// }
//
// {
// // E
// object F4b
// extension [T] (o: F4b.type) inline def unapplySeq[U](inline x: U) = Some(Seq(x))
//
// val F4b(x) = 18.2
// println(s"F4b: $x")
// }
//
// {
// // F
// object F4d
// extension [T] (o: F4d.type) inline def unapplySeq[U](inline x: (T, U)) = Some(Seq(x))
//
// val F4d(x) = (18.4, 18.5)
// println(s"F4d: $x")
// }
//
// {
// // G
// object H1
// extension (inline o: H1.type) inline def unapplySeq(inline x: Int) = Some(Seq(x))
//
// val H1(x) = 23
// println(s"H1: $x")
// }
//
// {
// // H
// object H2
// extension (inline o: H2.type) inline def unapplySeq[T](inline x: T) = Some(Seq(x))
//
// val H2(x) = 24
// println(s"H2: $x")
// }
//
// {
// // I
// object H2
// extension [T] (inline o: H2.type) inline def unapplySeq(inline x: T) = Some(Seq(x))
//
// val H2(x) = 24
// println(s"H2: $x")
// }
//
// {
// // J
// object H2
// extension [T] (inline o: H2.type) inline def unapplySeq[U](inline x: T) = Some(Seq(x))
//
// val H2(x) = 24
// println(s"H2: $x")
// }
//
// {
// // K
// object H2
// extension [T] (inline o: H2.type) inline def unapplySeq[U](inline x: U) = Some(Seq(x))
//
// val H2(x) = 24
// println(s"H2: $x")
// }
//
// {
// // L
// object H2
// extension [T] (inline o: H2.type) inline def unapplySeq[U](inline x: (T, U)) = Some(Seq(x))
//
// val H2(x) = (24, "a")
// println(s"H2: $x")
// }
//}