Skip to content

Commit

Permalink
Support implicit arguments before extractor method (#18671)
Browse files Browse the repository at this point in the history
Currently the global object initialization checker crashes when
encountering implicit arguments in extractor methods that are before
scrutinee. This PR fixes the issue and adds tests with implicit
arguments before scrutinee for `unapply` and `unapplySeq` respectively.
  • Loading branch information
olhotak authored Oct 16, 2023
2 parents 3ea7f7a + 6c34ce4 commit 69e1338
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 3 deletions.
11 changes: 8 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/init/Objects.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1305,9 +1305,14 @@ object Objects:
case select: Select =>
eval(select.qualifier, thisV, klass)

val implicitValues = evalArgs(implicits.map(Arg.apply), thisV, klass)
// TODO: implicit values may appear before and/or after the scrutinee parameter.
val unapplyRes = call(receiver, funRef.symbol, TraceValue(scrutinee, summon[Trace]) :: implicitValues, funRef.prefix, superType = NoType, needResolve = true)
def implicitArgsBeforeScrutinee(fun: Tree): Contextual[List[ArgInfo]] = fun match
case Apply(f, implicitArgs) =>
implicitArgsBeforeScrutinee(f) ++ evalArgs(implicitArgs.map(Arg.apply), thisV, klass)
case _ => List()

val implicitArgsAfterScrutinee = evalArgs(implicits.map(Arg.apply), thisV, klass)
val args = implicitArgsBeforeScrutinee(fun) ++ (TraceValue(scrutinee, summon[Trace]) :: implicitArgsAfterScrutinee)
val unapplyRes = call(receiver, funRef.symbol, args, funRef.prefix, superType = NoType, needResolve = true)

if fun.symbol.name == nme.unapplySeq then
var resultTp = unapplyResTp
Expand Down
14 changes: 14 additions & 0 deletions tests/init-global/neg/unapply-implicit-arg.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
object Bar {
class Foo {
def m1(i: Int) = i+1
def m2(i: Int) = i+2
}
def unapply(using f1: Foo)(i: Int): Option[Int] =
if i == 0 then Some(f1.m1(i)) else Some(f1.m2(i))

given Foo = new Foo
val i1: Int = 0
val i2: Int = i2 match // error
case Bar(i) => i
case _ => 0
}
14 changes: 14 additions & 0 deletions tests/init-global/neg/unapply-implicit-arg2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
object Bar {
class Foo {
def m1(i: Int) = i+1
def m2(i: Int) = i+2
}
def unapply(using f1: Foo)(i: Int): Option[Int] =
if i == 0 then Some(f1.m1(i1)) else Some(f1.m2(i2)) // error

given Foo = new Foo
val i1: Int = 0
val i2: Int = i1 match
case Bar(i) => i
case _ => 0
}
14 changes: 14 additions & 0 deletions tests/init-global/neg/unapply-implicit-arg3.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
object Bar {
class Foo {
def m1(i: Int) = i + i1
def m2(i: Int) = i + i2 // error
}
def unapply(using f1: Foo)(i: Int): Option[Int] =
if i == 0 then Some(f1.m1(i)) else Some(f1.m2(i))

given Foo = new Foo
val i1: Int = 0
val i2: Int = i1 match
case Bar(i) => i
case _ => 0
}
14 changes: 14 additions & 0 deletions tests/init-global/neg/unapplySeq-implicit-arg.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
object Bar {
class Foo {
def m1(seq: Seq[Int]) = 1 +: seq
def m2(seq: Seq[Int]) = 2 +: seq
}
def unapplySeq(using f1: Foo)(seqi: Seq[Int]): Option[Seq[Int]] =
if seqi(0) == 0 then Some(f1.m1(seqi)) else Some(f1.m2(seqi))

given Foo = new Foo
val i1: Int = 0
val i2: Int = Seq(i2) match // error
case Bar(i) => i
case _ => 0
}
10 changes: 10 additions & 0 deletions tests/init-global/neg/unapplySeq-implicit-arg2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
object Bar {
class Foo
def unapplySeq(using f1: Foo)(using f2: Foo)(seqi: Seq[Int])(using Foo): Option[Seq[Int]] =
Some(i1 +: seqi) // error
given Foo = new Foo
val i1: Int = Seq(0) match {
case Bar(i) => i
case _ => 0
}
}
12 changes: 12 additions & 0 deletions tests/init-global/neg/unapplySeq-implicit-arg3.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
object Bar {
class Foo {
def m(seq: Seq[Int]) = i1 +: seq // error
}
def unapplySeq(using f1: Foo)(seqi: Seq[Int])(using Foo): Option[Seq[Int]] =
Some(f1.m(seqi))
given Foo = new Foo
val i1: Int = Seq(0) match {
case Bar(i, _) => i
case _ => 0
}
}
14 changes: 14 additions & 0 deletions tests/init-global/pos/unapply-implicit-arg-pos.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
object Bar {
class Foo {
def m1(i: Int) = i + i1
def m2(i: Int) = i + 2
}
def unapply(using f1: Foo)(using f2: Foo)(i: Int)(using f3: Foo): Option[Int] =
if i == 0 then Some(f1.m1(i1) + f3.m1(i1)) else Some(f2.m2(i) + f3.m2(i))

given Foo = new Foo
val i1: Int = 0
val i2: Int = i1 match
case Bar(i) => i
case _ => 0
}
14 changes: 14 additions & 0 deletions tests/init-global/pos/unapplySeq-implicit-arg-pos.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
object Bar {
class Foo {
def m1(seq: Seq[Int]) = 0 +: seq
def m2(seq: Seq[Int]) = i1 +: seq
}
def unapplySeq(using f1: Foo)(using f2: Foo)(seqi: Seq[Int])(using f3: Foo): Option[Seq[Int]] =
if seqi(0) == 0 then Some(f1.m1(seqi)) else Some(f2.m2(seqi))

given Foo = new Foo
val i1: Int = 0
val i2: Int = Seq(i1) match
case Bar(i) => i
case _ => 0
}

0 comments on commit 69e1338

Please sign in to comment.