From 6574b32690121eda182587d7f1e58e06fcee79b0 Mon Sep 17 00:00:00 2001 From: EnzeXing Date: Mon, 9 Oct 2023 16:42:47 -0400 Subject: [PATCH 1/5] Support implicit arguments before extractor method --- .../src/dotty/tools/dotc/transform/init/Objects.scala | 10 +++++++--- tests/init-global/neg/unapply-implicit-arg.scala | 11 +++++++++++ tests/init-global/neg/unapplySeq-implicit-arg.scala | 11 +++++++++++ tests/init-global/pos/unapply-implicit-arg-pos.scala | 11 +++++++++++ .../init-global/pos/unapplySeq-implicit-arg-pos.scala | 11 +++++++++++ 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 tests/init-global/neg/unapply-implicit-arg.scala create mode 100644 tests/init-global/neg/unapplySeq-implicit-arg.scala create mode 100644 tests/init-global/pos/unapply-implicit-arg-pos.scala create mode 100644 tests/init-global/pos/unapplySeq-implicit-arg-pos.scala diff --git a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala index 3d01b6a93b08..076bff9d67d5 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala @@ -1298,9 +1298,13 @@ 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 implicitValuesBeforeScrutinee(fun: Tree): Contextual[List[ArgInfo]] = fun match + case Apply(f, implicitArgs) => + implicitValuesBeforeScrutinee(f) ++ evalArgs(implicitArgs.map(Arg.apply), thisV, klass) + case _ => List() + + val implicitValuesAfterScrtinee = evalArgs(implicits.map(Arg.apply), thisV, klass) + val unapplyRes = call(receiver, funRef.symbol, TraceValue(scrutinee, summon[Trace]) :: (implicitValuesBeforeScrutinee(fun) ++ implicitValuesAfterScrtinee), funRef.prefix, superType = NoType, needResolve = true) if fun.symbol.name == nme.unapplySeq then var resultTp = unapplyResTp diff --git a/tests/init-global/neg/unapply-implicit-arg.scala b/tests/init-global/neg/unapply-implicit-arg.scala new file mode 100644 index 000000000000..5f2545c59e0b --- /dev/null +++ b/tests/init-global/neg/unapply-implicit-arg.scala @@ -0,0 +1,11 @@ +class Foo + +object Bar { + def unapply(using Foo)(pair: (Int, Int))(using Foo): Option[Int] = + if pair._1 == 0 then Some(pair._1) else Some(pair._2) + given Foo = new Foo + val i1: Int = 0 + val i2: Int = (i1, i2) match // error + case Bar(i) => i + case _ => 0 +} \ No newline at end of file diff --git a/tests/init-global/neg/unapplySeq-implicit-arg.scala b/tests/init-global/neg/unapplySeq-implicit-arg.scala new file mode 100644 index 000000000000..600323b114b6 --- /dev/null +++ b/tests/init-global/neg/unapplySeq-implicit-arg.scala @@ -0,0 +1,11 @@ +class Foo + +object Bar { + def unapplySeq(using Foo)(using Foo)(pair: (Int, Int))(using Foo): Option[Seq[Int]] = + if pair._1 == 0 then Some(Seq(pair._1)) else Some(Seq(pair._2)) + given Foo = new Foo + val i1: Int = 0 + val i2: Int = (i1, i2) match // error + case Bar(i) => i + case _ => 0 +} diff --git a/tests/init-global/pos/unapply-implicit-arg-pos.scala b/tests/init-global/pos/unapply-implicit-arg-pos.scala new file mode 100644 index 000000000000..2b954256ab1a --- /dev/null +++ b/tests/init-global/pos/unapply-implicit-arg-pos.scala @@ -0,0 +1,11 @@ +class Foo + +object Bar { + def unapply(using Foo)(using Foo)(pair: (Int, Int))(using Foo): Option[Int] = + if pair._1 == 0 then Some(pair._1) else Some(pair._2) + given Foo = new Foo + val i1: Int = 0 + val i2: Int = (i1, i1) match + case Bar(i) => i + case _ => 0 +} \ No newline at end of file diff --git a/tests/init-global/pos/unapplySeq-implicit-arg-pos.scala b/tests/init-global/pos/unapplySeq-implicit-arg-pos.scala new file mode 100644 index 000000000000..76b5502caf09 --- /dev/null +++ b/tests/init-global/pos/unapplySeq-implicit-arg-pos.scala @@ -0,0 +1,11 @@ +class Foo + +object Bar { + def unapplySeq(using Foo)(using Foo)(pair: (Int, Int))(using Foo): Option[Seq[Int]] = + if pair._1 == 0 then Some(Seq(pair._1)) else Some(Seq(pair._2)) + given Foo = new Foo + val i1: Int = 0 + val i2: Int = (i1, i1) match + case Bar(i) => i + case _ => 0 +} From e99c982cb1cec5c9ad171232a8034bcb61f92ada Mon Sep 17 00:00:00 2001 From: EnzeXing Date: Fri, 13 Oct 2023 16:54:52 -0400 Subject: [PATCH 2/5] Correct argument order in evalPattern and add test --- .../dotty/tools/dotc/transform/init/Objects.scala | 2 +- tests/init-global/neg/unapply-implicit-arg.scala | 13 ++++++++----- tests/init-global/neg/unapply-implicit-arg2.scala | 14 ++++++++++++++ tests/init-global/neg/unapply-implicit-arg3.scala | 14 ++++++++++++++ .../init-global/neg/unapplySeq-implicit-arg.scala | 13 ++++++++----- .../init-global/neg/unapplySeq-implicit-arg2.scala | 10 ++++++++++ .../init-global/neg/unapplySeq-implicit-arg3.scala | 12 ++++++++++++ .../init-global/pos/unapply-implicit-arg-pos.scala | 13 ++++++++----- .../pos/unapplySeq-implicit-arg-pos.scala | 13 ++++++++----- 9 files changed, 83 insertions(+), 21 deletions(-) create mode 100644 tests/init-global/neg/unapply-implicit-arg2.scala create mode 100644 tests/init-global/neg/unapply-implicit-arg3.scala create mode 100644 tests/init-global/neg/unapplySeq-implicit-arg2.scala create mode 100644 tests/init-global/neg/unapplySeq-implicit-arg3.scala diff --git a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala index 076bff9d67d5..4274cab41cd9 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala @@ -1304,7 +1304,7 @@ object Objects: case _ => List() val implicitValuesAfterScrtinee = evalArgs(implicits.map(Arg.apply), thisV, klass) - val unapplyRes = call(receiver, funRef.symbol, TraceValue(scrutinee, summon[Trace]) :: (implicitValuesBeforeScrutinee(fun) ++ implicitValuesAfterScrtinee), funRef.prefix, superType = NoType, needResolve = true) + val unapplyRes = call(receiver, funRef.symbol, implicitValuesBeforeScrutinee(fun) ++ (TraceValue(scrutinee, summon[Trace]) :: implicitValuesAfterScrtinee), funRef.prefix, superType = NoType, needResolve = true) if fun.symbol.name == nme.unapplySeq then var resultTp = unapplyResTp diff --git a/tests/init-global/neg/unapply-implicit-arg.scala b/tests/init-global/neg/unapply-implicit-arg.scala index 5f2545c59e0b..bf41fbbf9412 100644 --- a/tests/init-global/neg/unapply-implicit-arg.scala +++ b/tests/init-global/neg/unapply-implicit-arg.scala @@ -1,11 +1,14 @@ -class Foo - object Bar { - def unapply(using Foo)(pair: (Int, Int))(using Foo): Option[Int] = - if pair._1 == 0 then Some(pair._1) else Some(pair._2) + 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 = (i1, i2) match // error + val i2: Int = i2 match // error case Bar(i) => i case _ => 0 } \ No newline at end of file diff --git a/tests/init-global/neg/unapply-implicit-arg2.scala b/tests/init-global/neg/unapply-implicit-arg2.scala new file mode 100644 index 000000000000..c0a16faac377 --- /dev/null +++ b/tests/init-global/neg/unapply-implicit-arg2.scala @@ -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 +} diff --git a/tests/init-global/neg/unapply-implicit-arg3.scala b/tests/init-global/neg/unapply-implicit-arg3.scala new file mode 100644 index 000000000000..efa348f6cfdb --- /dev/null +++ b/tests/init-global/neg/unapply-implicit-arg3.scala @@ -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 +} diff --git a/tests/init-global/neg/unapplySeq-implicit-arg.scala b/tests/init-global/neg/unapplySeq-implicit-arg.scala index 600323b114b6..e58635a3090f 100644 --- a/tests/init-global/neg/unapplySeq-implicit-arg.scala +++ b/tests/init-global/neg/unapplySeq-implicit-arg.scala @@ -1,11 +1,14 @@ -class Foo - object Bar { - def unapplySeq(using Foo)(using Foo)(pair: (Int, Int))(using Foo): Option[Seq[Int]] = - if pair._1 == 0 then Some(Seq(pair._1)) else Some(Seq(pair._2)) + 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 = (i1, i2) match // error + val i2: Int = Seq(i2) match // error case Bar(i) => i case _ => 0 } diff --git a/tests/init-global/neg/unapplySeq-implicit-arg2.scala b/tests/init-global/neg/unapplySeq-implicit-arg2.scala new file mode 100644 index 000000000000..35f5105b84d2 --- /dev/null +++ b/tests/init-global/neg/unapplySeq-implicit-arg2.scala @@ -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 + } +} diff --git a/tests/init-global/neg/unapplySeq-implicit-arg3.scala b/tests/init-global/neg/unapplySeq-implicit-arg3.scala new file mode 100644 index 000000000000..2b5cdd327e57 --- /dev/null +++ b/tests/init-global/neg/unapplySeq-implicit-arg3.scala @@ -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 + } +} diff --git a/tests/init-global/pos/unapply-implicit-arg-pos.scala b/tests/init-global/pos/unapply-implicit-arg-pos.scala index 2b954256ab1a..5573a2210160 100644 --- a/tests/init-global/pos/unapply-implicit-arg-pos.scala +++ b/tests/init-global/pos/unapply-implicit-arg-pos.scala @@ -1,11 +1,14 @@ -class Foo - object Bar { - def unapply(using Foo)(using Foo)(pair: (Int, Int))(using Foo): Option[Int] = - if pair._1 == 0 then Some(pair._1) else Some(pair._2) + 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, i1) match + val i2: Int = i1 match case Bar(i) => i case _ => 0 } \ No newline at end of file diff --git a/tests/init-global/pos/unapplySeq-implicit-arg-pos.scala b/tests/init-global/pos/unapplySeq-implicit-arg-pos.scala index 76b5502caf09..08e69d4ff3bc 100644 --- a/tests/init-global/pos/unapplySeq-implicit-arg-pos.scala +++ b/tests/init-global/pos/unapplySeq-implicit-arg-pos.scala @@ -1,11 +1,14 @@ -class Foo - object Bar { - def unapplySeq(using Foo)(using Foo)(pair: (Int, Int))(using Foo): Option[Seq[Int]] = - if pair._1 == 0 then Some(Seq(pair._1)) else Some(Seq(pair._2)) + 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 = (i1, i1) match + val i2: Int = Seq(i1) match case Bar(i) => i case _ => 0 } From 508c2d27d8a705c540a3a7b133c30b39fed2effb Mon Sep 17 00:00:00 2001 From: EnzeXing Date: Sat, 14 Oct 2023 19:08:00 -0400 Subject: [PATCH 3/5] Address comments --- .../src/dotty/tools/dotc/transform/init/Objects.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala index 4274cab41cd9..a2a9c84a782b 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala @@ -1298,13 +1298,14 @@ object Objects: case select: Select => eval(select.qualifier, thisV, klass) - def implicitValuesBeforeScrutinee(fun: Tree): Contextual[List[ArgInfo]] = fun match + def implicitArgsBeforeScrutinee(fun: Tree): Contextual[List[ArgInfo]] = fun match case Apply(f, implicitArgs) => - implicitValuesBeforeScrutinee(f) ++ evalArgs(implicitArgs.map(Arg.apply), thisV, klass) + implicitArgsBeforeScrutinee(f) ++ evalArgs(implicitArgs.map(Arg.apply), thisV, klass) case _ => List() - val implicitValuesAfterScrtinee = evalArgs(implicits.map(Arg.apply), thisV, klass) - val unapplyRes = call(receiver, funRef.symbol, implicitValuesBeforeScrutinee(fun) ++ (TraceValue(scrutinee, summon[Trace]) :: implicitValuesAfterScrtinee), funRef.prefix, superType = NoType, needResolve = true) + val implicitArgsAfterScrtinee = evalArgs(implicits.map(Arg.apply), thisV, klass) + val args = implicitArgsBeforeScrutinee(fun) ++ (TraceValue(scrutinee, summon[Trace]) :: implicitArgsAfterScrtinee) + val unapplyRes = call(receiver, funRef.symbol, args, funRef.prefix, superType = NoType, needResolve = true) if fun.symbol.name == nme.unapplySeq then var resultTp = unapplyResTp From 231a4a7ef71be9be756ccd5040912c9bd19f58f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Lhot=C3=A1k?= Date: Mon, 16 Oct 2023 14:52:40 -0400 Subject: [PATCH 4/5] Update compiler/src/dotty/tools/dotc/transform/init/Objects.scala --- compiler/src/dotty/tools/dotc/transform/init/Objects.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala index a2a9c84a782b..5071fc920651 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala @@ -1303,7 +1303,7 @@ object Objects: implicitArgsBeforeScrutinee(f) ++ evalArgs(implicitArgs.map(Arg.apply), thisV, klass) case _ => List() - val implicitArgsAfterScrtinee = evalArgs(implicits.map(Arg.apply), thisV, klass) + val implicitArgsAfterScrutinee = evalArgs(implicits.map(Arg.apply), thisV, klass) val args = implicitArgsBeforeScrutinee(fun) ++ (TraceValue(scrutinee, summon[Trace]) :: implicitArgsAfterScrtinee) val unapplyRes = call(receiver, funRef.symbol, args, funRef.prefix, superType = NoType, needResolve = true) From 6c34ce4a780e6a738cb96d6e07b2744f205c51fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Lhot=C3=A1k?= Date: Mon, 16 Oct 2023 14:52:46 -0400 Subject: [PATCH 5/5] Update compiler/src/dotty/tools/dotc/transform/init/Objects.scala --- compiler/src/dotty/tools/dotc/transform/init/Objects.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala index 5071fc920651..db0066401621 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala @@ -1304,7 +1304,7 @@ object Objects: case _ => List() val implicitArgsAfterScrutinee = evalArgs(implicits.map(Arg.apply), thisV, klass) - val args = implicitArgsBeforeScrutinee(fun) ++ (TraceValue(scrutinee, summon[Trace]) :: implicitArgsAfterScrtinee) + 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