Skip to content

Commit de26e9a

Browse files
authored
Take @TargetNAME into account when resolving extension methods (#16487)
Take @TargetNAME into account when resolving extension methods of value classes Before target name we only matched on signatures. This was OK, since multiple extension methods of the same class must be different, otherwise we will get a "have the same erasure" error later at erasurePhase. But with @TargetNAME that's now a legal situation that needs to be resolved correctly. We do this by propagating the target name to the extension method and verifying that the target names of the original and extension methods match. Fixes #16464
2 parents 7b7c055 + 3a650e9 commit de26e9a

File tree

4 files changed

+56
-3
lines changed

4 files changed

+56
-3
lines changed

compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,17 @@ object ExtensionMethods {
188188
val companion = imeth.owner.companionModule
189189
val companionInfo = companion.info
190190
val candidates = companionInfo.decl(extensionName(imeth)).alternatives
191-
val matching =
192-
// See the documentation of `memberSignature` to understand why `.stripPoly.ensureMethodic` is needed here.
193-
candidates filter (c => FullParameterization.memberSignature(c.info) == imeth.info.stripPoly.ensureMethodic.signature)
191+
def matches(candidate: SingleDenotation) =
192+
FullParameterization.memberSignature(candidate.info) == imeth.info.stripPoly.ensureMethodic.signature
193+
// See the documentation of `memberSignature` to understand why `.stripPoly.ensureMethodic` is needed here.
194+
&& (if imeth.targetName == imeth.name then
195+
// imeth does not have a @targetName annotation, candidate should not have one either
196+
candidate.symbol.targetName == candidate.symbol.name
197+
else
198+
// imeth has a @targetName annotation, candidate's target name must match
199+
imeth.targetName == candidate.symbol.targetName
200+
)
201+
val matching = candidates.filter(matches)
194202
assert(matching.nonEmpty,
195203
i"""no extension method found for:
196204
|
@@ -203,6 +211,9 @@ object ExtensionMethods {
203211
| Candidates (signatures normalized):
204212
|
205213
| ${candidates.map(c => s"${c.name}:${c.info.signature}:${FullParameterization.memberSignature(c.info)}").mkString("\n")}""")
214+
if matching.tail.nonEmpty then
215+
// this case will report a "have the same erasure" error later at erasure pahse
216+
report.log(i"mutiple extension methods match $imeth: ${candidates.map(c => i"${c.name}:${c.info}")}")
206217
matching.head.symbol.asTerm
207218
}
208219
}

tests/neg/i16464.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
implicit final class SomeOps(e: Int) extends AnyVal:
3+
def -(other: Seq[Int]) = List(1)
4+
def -(other: Seq[Long]) = List(2) // error: double definition
5+
6+
def main(): Unit = 1 - Seq.empty[Int]

tests/pos/i16464.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import scala.annotation.targetName
2+
3+
object test1:
4+
implicit final class SomeOps(e: Int) extends AnyVal:
5+
@targetName("a")
6+
def -(other: Seq[Int]) = List(1)
7+
@targetName("b")
8+
def -(other: Seq[Long]) = List(2)
9+
10+
def main(): Unit = 1 - Seq.empty[Int]
11+
12+
object test2:
13+
implicit final class SomeOps(e: Int) extends AnyVal:
14+
@targetName("a")
15+
def -(other: Seq[Int]) = List(1)
16+
def -(other: Seq[Long]) = List(2)
17+
18+
def main(): Unit = 1 - Seq.empty[Int]
19+
20+
object test3:
21+
implicit final class SomeOps(e: Int) extends AnyVal:
22+
def -(other: Seq[Int]) = List(1)
23+
@targetName("b")
24+
def -(other: Seq[Long]) = List(2)
25+
26+
def main(): Unit = 1 - Seq.empty[Int]

tests/run/i16464.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import scala.annotation.targetName
2+
3+
implicit final class SomeOps(e: Int) extends AnyVal:
4+
@targetName("a")
5+
def -(other: Seq[Int]) = List(1)
6+
@targetName("b")
7+
def -(other: Seq[Long]) = List(2)
8+
9+
@main
10+
def Test(): Unit = 1 - Seq.empty[Int]

0 commit comments

Comments
 (0)