Skip to content

Commit f43f262

Browse files
committed
Allow to disambiguate overloads with @TargetNAME
Alternatives that have the same signature but different targetNames are considered to be separate.
1 parent 5d5fc3c commit f43f262

21 files changed

+266
-119
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -882,16 +882,16 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
882882
}
883883

884884
/** A select node with the given selector name and signature and a computed type */
885-
def selectWithSig(name: Name, sig: Signature)(using Context): Tree =
886-
untpd.SelectWithSig(tree, name, sig).withType(tree.tpe.select(name.asTermName, sig))
885+
def selectWithSig(name: Name, sig: Signature, target: Name)(using Context): Tree =
886+
untpd.SelectWithSig(tree, name, sig).withType(tree.tpe.select(name.asTermName, sig, target))
887887

888888
/** A select node with selector name and signature taken from `sym`.
889889
* Note: Use this method instead of select(sym) if the referenced symbol
890890
* might be overridden in the type of the qualifier prefix. See note
891891
* on select(sym: Symbol).
892892
*/
893893
def selectWithSig(sym: Symbol)(using Context): Tree =
894-
selectWithSig(sym.name, sym.signature)
894+
selectWithSig(sym.name, sym.signature, sym.erasedName)
895895

896896
/** A unary apply node with given argument: `tree(arg)` */
897897
def appliedTo(arg: Tree)(using Context): Apply =

compiler/src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ object Denotations {
222222
* when seen from prefix `site`.
223223
* @param relaxed When true, consider only parameter signatures for a match.
224224
*/
225-
def atSignature(sig: Signature, site: Type = NoPrefix, relaxed: Boolean = false)(using Context): Denotation
225+
def atSignature(sig: Signature, targetName: Name, site: Type = NoPrefix, relaxed: Boolean = false)(using Context): Denotation
226226

227227
/** The variant of this denotation that's current in the given context.
228228
* If no such denotation exists, returns the denotation with each alternative
@@ -347,13 +347,15 @@ object Denotations {
347347
}
348348

349349
/** The alternative of this denotation that has a type matching `targetType` when seen
350-
* as a member of type `site`, `NoDenotation` if none exists.
350+
* as a member of type `site` and that has an erased name matching `targetName`, or
351+
* `NoDenotation` if none exists.
351352
*/
352-
def matchingDenotation(site: Type, targetType: Type)(using Context): SingleDenotation = {
353-
def qualifies(sym: Symbol) = site.memberInfo(sym).matchesLoosely(targetType)
353+
def matchingDenotation(site: Type, targetType: Type, targetName: Name)(using Context): SingleDenotation = {
354+
def qualifies(sym: Symbol) =
355+
site.memberInfo(sym).matchesLoosely(targetType) && targetNamesMatch(sym.erasedName, targetName)
354356
if (isOverloaded)
355-
atSignature(targetType.signature, site, relaxed = true) match {
356-
case sd: SingleDenotation => sd.matchingDenotation(site, targetType)
357+
atSignature(targetType.signature, targetName, site, relaxed = true) match {
358+
case sd: SingleDenotation => sd.matchingDenotation(site, targetType, targetName)
357359
case md => md.suchThat(qualifies(_))
358360
}
359361
else if (exists && !qualifies(symbol)) NoDenotation
@@ -610,9 +612,9 @@ object Denotations {
610612
def accessibleFrom(pre: Type, superAccess: Boolean)(using Context): Denotation =
611613
if (!symbol.exists || symbol.isAccessibleFrom(pre, superAccess)) this else NoDenotation
612614

613-
def atSignature(sig: Signature, site: Type, relaxed: Boolean)(using Context): SingleDenotation =
615+
def atSignature(sig: Signature, targetName: Name, site: Type, relaxed: Boolean)(using Context): SingleDenotation =
614616
val situated = if site == NoPrefix then this else asSeenFrom(site)
615-
val matches = sig.matchDegree(situated.signature) match
617+
val sigMatches = sig.matchDegree(situated.signature) match
616618
case FullMatch =>
617619
true
618620
case MethodNotAMethodMatch =>
@@ -622,7 +624,8 @@ object Denotations {
622624
relaxed
623625
case noMatch =>
624626
false
625-
if matches then this else NoDenotation
627+
if sigMatches && targetNamesMatch(symbol.erasedName, targetName) then this
628+
else NoDenotation
626629

627630
def matchesImportBound(bound: Type)(using Context): Boolean =
628631
if bound.isRef(defn.NothingClass) then false
@@ -983,33 +986,35 @@ object Denotations {
983986
final def last: SingleDenotation = this
984987

985988
def matches(other: SingleDenotation)(using Context): Boolean =
986-
val d = signature.matchDegree(other.signature)
987-
988-
d match
989-
case FullMatch =>
990-
true
991-
case MethodNotAMethodMatch =>
992-
!ctx.erasedTypes && {
993-
val isJava = symbol.is(JavaDefined)
994-
val otherIsJava = other.symbol.is(JavaDefined)
995-
// A Scala zero-parameter method and a Scala non-method always match.
996-
if !isJava && !otherIsJava then
997-
true
998-
// Java allows defining both a field and a zero-parameter method with the same name,
999-
// so they must not match.
1000-
else if isJava && otherIsJava then
1001-
false
1002-
// A Java field never matches a Scala method.
1003-
else if isJava then
1004-
symbol.is(Method)
1005-
else // otherIsJava
1006-
other.symbol.is(Method)
1007-
}
1008-
case ParamMatch =>
1009-
// The signatures do not tell us enough to be sure about matching
1010-
!ctx.erasedTypes && info.matches(other.info)
1011-
case noMatch =>
1012-
false
989+
targetNamesMatch(symbol.erasedName, other.symbol.erasedName)
990+
&& {
991+
val d = signature.matchDegree(other.signature)
992+
d match
993+
case FullMatch =>
994+
true
995+
case MethodNotAMethodMatch =>
996+
!ctx.erasedTypes && {
997+
val isJava = symbol.is(JavaDefined)
998+
val otherIsJava = other.symbol.is(JavaDefined)
999+
// A Scala zero-parameter method and a Scala non-method always match.
1000+
if !isJava && !otherIsJava then
1001+
true
1002+
// Java allows defining both a field and a zero-parameter method with the same name,
1003+
// so they must not match.
1004+
else if isJava && otherIsJava then
1005+
false
1006+
// A Java field never matches a Scala method.
1007+
else if isJava then
1008+
symbol.is(Method)
1009+
else // otherIsJava
1010+
other.symbol.is(Method)
1011+
}
1012+
case ParamMatch =>
1013+
// The signatures do not tell us enough to be sure about matching
1014+
!ctx.erasedTypes && info.matches(other.info)
1015+
case noMatch =>
1016+
false
1017+
}
10131018
end matches
10141019

10151020
def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(using Context): SingleDenotation =
@@ -1164,9 +1169,11 @@ object Denotations {
11641169
final def hasUniqueSym: Boolean = false
11651170
final def name(using Context): Name = denot1.name
11661171
final def signature(using Context): Signature = Signature.OverloadedSignature
1167-
def atSignature(sig: Signature, site: Type, relaxed: Boolean)(using Context): Denotation =
1172+
def atSignature(sig: Signature, targetName: Name, site: Type, relaxed: Boolean)(using Context): Denotation =
11681173
if (sig eq Signature.OverloadedSignature) this
1169-
else derivedUnionDenotation(denot1.atSignature(sig, site, relaxed), denot2.atSignature(sig, site, relaxed))
1174+
else derivedUnionDenotation(
1175+
denot1.atSignature(sig, targetName, site, relaxed),
1176+
denot2.atSignature(sig, targetName, site, relaxed))
11701177
def current(using Context): Denotation =
11711178
derivedUnionDenotation(denot1.current, denot2.current)
11721179
def altsWith(p: Symbol => Boolean): List[SingleDenotation] =
@@ -1263,7 +1270,6 @@ object Denotations {
12631270
else ctx.run.staticRefs.getOrElseUpdate(path, recur(path))
12641271
}
12651272

1266-
12671273
/** If we are looking for a non-existing term name in a package,
12681274
* assume it is a package for which we do not have a directory and
12691275
* enter it.
@@ -1279,4 +1285,7 @@ object Denotations {
12791285
util.Stats.record("stale symbol")
12801286
override def getMessage(): String = msg
12811287
}
1288+
1289+
def targetNamesMatch(name1: Name, name2: Name): Boolean =
1290+
name1 == name2 || name1.isEmpty || name2.isEmpty
12821291
}

compiler/src/dotty/tools/dotc/core/NameKinds.scala

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -371,17 +371,19 @@ object NameKinds {
371371
/** A name together with a signature. Used in Tasty trees. */
372372
object SignedName extends NameKind(SIGNED) {
373373

374-
case class SignedInfo(sig: Signature) extends Info {
374+
case class SignedInfo(sig: Signature, target: TermName) extends Info {
375375
assert(sig ne Signature.NotAMethod)
376-
override def toString: String = s"$infoString $sig"
376+
override def toString: String =
377+
val targetStr = if target.isEmpty then "" else s" @$target"
378+
s"$infoString $sig$targetStr"
377379
override def hashCode = scala.runtime.ScalaRunTime._hashCode(this) * 31 + kind.hashCode
378380
}
379381
type ThisInfo = SignedInfo
380382

381-
def apply(qual: TermName, sig: Signature): TermName =
382-
qual.derived(new SignedInfo(sig))
383-
def unapply(name: DerivedName): Option[(TermName, Signature)] = name match {
384-
case DerivedName(underlying, info: SignedInfo) => Some((underlying, info.sig))
383+
def apply(qual: TermName, sig: Signature, target: TermName): TermName =
384+
qual.derived(new SignedInfo(sig, target))
385+
def unapply(name: DerivedName): Option[(TermName, Signature, TermName)] = name match {
386+
case DerivedName(underlying, info: SignedInfo) => Some((underlying, info.sig, info.target))
385387
case _ => None
386388
}
387389

compiler/src/dotty/tools/dotc/core/NameTags.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,6 @@ object NameTags extends TastyFormat.NameTags {
5252
case OBJECTCLASS => "OBJECTCLASS"
5353

5454
case SIGNED => "SIGNED"
55+
case TARGETSIGNED => "TARGETSIGNED"
5556
}
5657
}

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -493,11 +493,9 @@ object SymDenotations {
493493
/** `fullName` where `.' is the separator character */
494494
def fullName(using Context): Name = fullNameSeparated(QualifiedName)
495495

496-
/** The name given in a `@targetName` annotation if one is present, `name` otherwise */
497-
def erasedName(using Context): Name =
498-
val targetNameAnnot =
499-
if isAllOf(ModuleClass | Synthetic) then companionClass.getAnnotation(defn.TargetNameAnnot)
500-
else getAnnotation(defn.TargetNameAnnot)
496+
private var myTargetName: Name = null
497+
498+
private def computeTargetName(targetNameAnnot: Option[Annotation])(using Context): Name =
501499
targetNameAnnot match
502500
case Some(ann) =>
503501
ann.arguments match
@@ -509,6 +507,21 @@ object SymDenotations {
509507
case _ => name
510508
case _ => name
511509

510+
def setTargetName(name: Name): Unit =
511+
myTargetName = name
512+
513+
/** The name given in a `@targetName` annotation if one is present, `name` otherwise */
514+
def erasedName(using Context): Name =
515+
if myTargetName == null then
516+
val carrier: SymDenotation =
517+
if isAllOf(ModuleClass | Synthetic) then companionClass else this
518+
val targetNameAnnot =
519+
if carrier.isCompleting // annotations have been set already in this case
520+
then carrier.unforcedAnnotation(defn.TargetNameAnnot)
521+
else carrier.getAnnotation(defn.TargetNameAnnot)
522+
myTargetName = computeTargetName(targetNameAnnot)
523+
myTargetName
524+
512525
// ----- Tests -------------------------------------------------
513526

514527
/** Is this denotation a type? */
@@ -1243,7 +1256,7 @@ object SymDenotations {
12431256
final def matchingDecl(inClass: Symbol, site: Type)(using Context): Symbol = {
12441257
var denot = inClass.info.nonPrivateDecl(name)
12451258
if (denot.isTerm) // types of the same name always match
1246-
denot = denot.matchingDenotation(site, site.memberInfo(symbol))
1259+
denot = denot.matchingDenotation(site, site.memberInfo(symbol), symbol.erasedName)
12471260
denot.symbol
12481261
}
12491262

@@ -1252,7 +1265,7 @@ object SymDenotations {
12521265
final def matchingMember(site: Type)(using Context): Symbol = {
12531266
var denot = site.nonPrivateMember(name)
12541267
if (denot.isTerm) // types of the same name always match
1255-
denot = denot.matchingDenotation(site, site.memberInfo(symbol))
1268+
denot = denot.matchingDenotation(site, site.memberInfo(symbol), symbol.erasedName)
12561269
denot.symbol
12571270
}
12581271

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,8 +1457,8 @@ object Types {
14571457
def select(name: TermName)(using Context): TermRef =
14581458
TermRef(this, name, member(name))
14591459

1460-
def select(name: TermName, sig: Signature)(using Context): TermRef =
1461-
TermRef(this, name, member(name).atSignature(sig, relaxed = !ctx.erasedTypes))
1460+
def select(name: TermName, sig: Signature, target: Name)(using Context): TermRef =
1461+
TermRef(this, name, member(name).atSignature(sig, target, relaxed = !ctx.erasedTypes))
14621462

14631463
// ----- Access to parts --------------------------------------------
14641464

@@ -2084,14 +2084,14 @@ object Types {
20842084
}
20852085

20862086
private def disambiguate(d: Denotation)(using Context): Denotation =
2087-
disambiguate(d, currentSignature)
2087+
disambiguate(d, currentSignature, currentSymbol.erasedName)
20882088

2089-
private def disambiguate(d: Denotation, sig: Signature)(using Context): Denotation =
2089+
private def disambiguate(d: Denotation, sig: Signature, target: Name)(using Context): Denotation =
20902090
if (sig != null)
2091-
d.atSignature(sig, relaxed = !ctx.erasedTypes) match {
2091+
d.atSignature(sig, target, relaxed = !ctx.erasedTypes) match {
20922092
case d1: SingleDenotation => d1
20932093
case d1 =>
2094-
d1.atSignature(sig, relaxed = false) match {
2094+
d1.atSignature(sig, target, relaxed = false) match {
20952095
case d2: SingleDenotation => d2
20962096
case d2 => d2.suchThat(currentSymbol.eq).orElse(d2)
20972097
}
@@ -2395,7 +2395,8 @@ object Types {
23952395
if (d.isOverloaded && lastSymbol.exists)
23962396
d = disambiguate(d,
23972397
if (lastSymbol.signature == Signature.NotAMethod) Signature.NotAMethod
2398-
else lastSymbol.asSeenFrom(prefix).signature)
2398+
else lastSymbol.asSeenFrom(prefix).signature,
2399+
lastSymbol.erasedName)
23992400
NamedType(prefix, name, d)
24002401
}
24012402
if (prefix eq this.prefix) this

compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import Names.{Name, chrs, SimpleName, DerivedName, TypeName}
1111
import NameKinds._
1212
import Decorators._
1313
import scala.io.Codec
14+
import Denotations.targetNamesMatch
15+
import NameTags.{SIGNED, TARGETSIGNED}
1416

1517
class NameBuffer extends TastyBuffer(10000) {
1618
import NameBuffer._
@@ -24,8 +26,9 @@ class NameBuffer extends TastyBuffer(10000) {
2426
ref
2527
case None =>
2628
name1 match {
27-
case SignedName(original, Signature(params, result)) =>
29+
case SignedName(original, Signature(params, result), target) =>
2830
nameIndex(original)
31+
if !targetNamesMatch(original, target) then nameIndex(target)
2932
nameIndex(result)
3033
params.foreach {
3134
case param: TypeName =>
@@ -70,29 +73,39 @@ class NameBuffer extends TastyBuffer(10000) {
7073

7174
def pickleNameContents(name: Name): Unit = {
7275
val tag = name.toTermName.info.kind.tag
73-
writeByte(tag)
7476
name.toTermName match {
7577
case name: SimpleName =>
78+
writeByte(tag)
7679
val bytes =
7780
if (name.length == 0) new Array[Byte](0)
7881
else Codec.toUTF8(chrs, name.start, name.length)
7982
writeNat(bytes.length)
8083
writeBytes(bytes, bytes.length)
8184
case AnyQualifiedName(prefix, name) =>
85+
writeByte(tag)
8286
withLength { writeNameRef(prefix); writeNameRef(name) }
8387
case AnyUniqueName(original, separator, num) =>
88+
writeByte(tag)
8489
withLength {
8590
writeNameRef(separator)
8691
writeNat(num)
8792
if (!original.isEmpty) writeNameRef(original)
8893
}
8994
case AnyNumberedName(original, num) =>
95+
writeByte(tag)
9096
withLength { writeNameRef(original); writeNat(num) }
91-
case SignedName(original, Signature(paramsSig, result)) =>
97+
case SignedName(original, Signature(paramsSig, result), target) =>
98+
val needsTarget = !targetNamesMatch(original, target)
99+
writeByte(if needsTarget then TARGETSIGNED else SIGNED)
92100
withLength(
93-
{ writeNameRef(original); writeNameRef(result); paramsSig.foreach(writeParamSig) },
94-
if ((paramsSig.length + 2) * maxIndexWidth <= maxNumInByte) 1 else 2)
101+
{ writeNameRef(original)
102+
if needsTarget then writeNameRef(target)
103+
writeNameRef(result)
104+
paramsSig.foreach(writeParamSig)
105+
},
106+
if ((paramsSig.length + 3) * maxIndexWidth <= maxNumInByte) 1 else 2)
95107
case DerivedName(original, _) =>
108+
writeByte(tag)
96109
withLength { writeNameRef(original) }
97110
}
98111
}

compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ class TastyUnpickler(reader: TastyReader) {
5050
val length = readNat()
5151
val start = currentAddr
5252
val end = start + length
53+
def readSignedRest(original: TermName, target: TermName): TermName =
54+
val result = readName().toTypeName
55+
// DOTTY: we shouldn't have to give an explicit type to paramsSig,
56+
// see https://github.com/lampepfl/dotty/issues/4867
57+
val paramsSig: List[Signature.ParamSig] = until(end)(readParamSig())
58+
val sig = Signature(paramsSig, result)
59+
SignedName(original, sig, target)
60+
5361
val result = tag match {
5462
case UTF8 =>
5563
goto(end)
@@ -66,12 +74,11 @@ class TastyUnpickler(reader: TastyReader) {
6674
numberedNameKindOfTag(tag)(readName(), readNat())
6775
case SIGNED =>
6876
val original = readName()
69-
val result = readName().toTypeName
70-
// DOTTY: we shouldn't have to give an explicit type to paramsSig,
71-
// see https://github.com/lampepfl/dotty/issues/4867
72-
val paramsSig: List[Signature.ParamSig] = until(end)(readParamSig())
73-
val sig = Signature(paramsSig, result)
74-
SignedName(original, sig)
77+
readSignedRest(original, original)
78+
case TARGETSIGNED =>
79+
val original = readName()
80+
val target = readName()
81+
readSignedRest(original, target)
7582
case _ =>
7683
simpleNameKindOfTag(tag)(readName())
7784
}

0 commit comments

Comments
 (0)