Skip to content

Commit f6a7900

Browse files
committed
Support polymorphic signatures
1 parent e587a81 commit f6a7900

File tree

16 files changed

+202
-53
lines changed

16 files changed

+202
-53
lines changed

compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTyp
134134

135135
private lazy val jliCallSiteRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.CallSite])
136136
private lazy val jliLambdaMetafactoryRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.LambdaMetafactory])
137-
private lazy val jliMethodHandleRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodHandle])
138-
private lazy val jliMethodHandlesLookupRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodHandles.Lookup])
137+
private lazy val jliMethodHandleRef : ClassBType = classBTypeFromSymbol(defn.MethodHandleClass)
138+
private lazy val jliMethodHandlesLookupRef : ClassBType = classBTypeFromSymbol(defn.MethodHandlesLookupClass)
139139
private lazy val jliMethodTypeRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodType])
140140
private lazy val jliStringConcatFactoryRef : ClassBType = classBTypeFromSymbol(requiredClass("java.lang.invoke.StringConcatFactory")) // since JDK 9
141141
private lazy val srLambdaDeserialize : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.LambdaDeserialize])

compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4726,7 +4726,7 @@ class JSCodeGen()(using genCtx: Context) {
47264726
Set[Symbol](
47274727
defn.BoxedUnitClass, defn.BoxedBooleanClass, defn.BoxedCharClass, defn.BoxedByteClass,
47284728
defn.BoxedShortClass, defn.BoxedIntClass, defn.BoxedLongClass, defn.BoxedFloatClass,
4729-
defn.BoxedDoubleClass, defn.StringClass, jsdefn.JavaLangVoidClass
4729+
defn.BoxedDoubleClass, defn.StringClass, defn.VoidClass
47304730
)
47314731
}
47324732

compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ final class JSDefinitions()(using Context) {
2626
@threadUnsafe lazy val NoinlineAnnotType: TypeRef = requiredClassRef("scala.noinline")
2727
def NoinlineAnnot(using Context) = NoinlineAnnotType.symbol.asClass
2828

29-
@threadUnsafe lazy val JavaLangVoidType: TypeRef = requiredClassRef("java.lang.Void")
30-
def JavaLangVoidClass(using Context) = JavaLangVoidType.symbol.asClass
31-
3229
@threadUnsafe lazy val ScalaJSJSPackageVal = requiredPackage("scala.scalajs.js")
3330
@threadUnsafe lazy val ScalaJSJSPackageClass = ScalaJSJSPackageVal.moduleClass.asClass
3431
@threadUnsafe lazy val JSPackage_typeOfR = ScalaJSJSPackageClass.requiredMethodRef("typeOf")

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,9 @@ class Definitions {
485485
}
486486
def NullType: TypeRef = NullClass.typeRef
487487

488+
@tu lazy val VoidType: TypeRef = requiredClassRef("java.lang.Void")
489+
def VoidClass(using Context) = VoidType.symbol.asClass
490+
488491
@tu lazy val InvokerModule = requiredModule("scala.runtime.coverage.Invoker")
489492
@tu lazy val InvokedMethodRef = InvokerModule.requiredMethodRef("invoked")
490493

@@ -732,6 +735,26 @@ class Definitions {
732735
}
733736
def JavaEnumType = JavaEnumClass.typeRef
734737

738+
739+
@tu lazy val MethodHandleClass: ClassSymbol = requiredClass("java.lang.invoke.MethodHandle")
740+
@tu lazy val MethodHandlesLookupClass: ClassSymbol = requiredClass("java.lang.invoke.MethodHandles.Lookup")
741+
@tu lazy val VarHandleClass: ClassSymbol = requiredClass("java.lang.invoke.VarHandle")
742+
743+
// from the Java language spec: https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.3
744+
def isPolymorphicSignature(sym: Symbol) = sym.is(JavaDefined) && {
745+
val owner = sym.maybeOwner
746+
(owner == MethodHandleClass || owner == VarHandleClass)
747+
&& sym.hasAnnotation(NativeAnnot)
748+
&& sym.paramSymss.match
749+
case List(List(p)) => p.info.isRepeatedParam
750+
case _ => false
751+
}
752+
753+
def wasPolymorphicSignature(sym: Symbol) =
754+
val owner = sym.maybeOwner
755+
(owner == MethodHandleClass || owner == VarHandleClass)
756+
&& isPolymorphicSignature(owner.info.member(sym.name).symbol)
757+
735758
@tu lazy val StringBuilderClass: ClassSymbol = requiredClass("scala.collection.mutable.StringBuilder")
736759
@tu lazy val MatchErrorClass : ClassSymbol = requiredClass("scala.MatchError")
737760
@tu lazy val ConversionClass : ClassSymbol = requiredClass("scala.Conversion").typeRef.symbol.asClass

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class TastyPrinter(bytes: Array[Byte]) {
129129
printName(); printName()
130130
case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | BIND =>
131131
printName(); printTrees()
132-
case REFINEDtype | TERMREFin | TYPEREFin | SELECTin =>
132+
case REFINEDtype | TERMREFin | TYPEREFin | SELECTin | SELECTinPoly =>
133133
printName(); printTree(); printTrees()
134134
case RETURN | HOLE =>
135135
printNat(); printTrees()

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,14 @@ class TreePickler(pickler: TastyPickler) {
413413
writeByte(if name.isTypeName then SELECTtpt else SELECT)
414414
pickleNameAndSig(name, sig, ename)
415415
pickleTree(qual)
416+
else if defn.wasPolymorphicSignature(tree.symbol) then
417+
writeByte(SELECTinPoly)
418+
withLength {
419+
pickleNameAndSig(name, tree.symbol.signature, ename)
420+
pickleTree(qual)
421+
pickleType(tree.symbol.owner.typeRef)
422+
pickleType(tree.tpe.widenSingleton, richTypes = true) // this widens to a MethodType, so need richTypes
423+
}
416424
else // select from owner
417425
writeByte(SELECTin)
418426
withLength {

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

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,7 +1131,6 @@ class TreeUnpickler(reader: TastyReader,
11311131

11321132
def makeSelect(qual: Tree, name: Name, denot: Denotation): Select =
11331133
var qualType = qual.tpe.widenIfUnstable
1134-
val owner = denot.symbol.maybeOwner
11351134
val tpe0 = name match
11361135
case name: TypeName => TypeRef(qualType, name, denot)
11371136
case name: TermName => TermRef(qualType, name, denot)
@@ -1143,6 +1142,43 @@ class TreeUnpickler(reader: TastyReader,
11431142
val denot = accessibleDenot(qual.tpe.widenIfUnstable, name, sig, target)
11441143
makeSelect(qual, name, denot)
11451144

1145+
def readSelectIn(): Select =
1146+
var sname = readName()
1147+
val qual = readTerm()
1148+
val ownerTpe = readType()
1149+
val owner = ownerTpe.typeSymbol
1150+
val SignedName(name, sig, target) = sname: @unchecked // only methods with params use SELECTin
1151+
val qualType = qual.tpe.widenIfUnstable
1152+
val prefix = ctx.typeAssigner.maybeSkolemizePrefix(qualType, name)
1153+
1154+
/** Tasty should still be able to resolve a method from another root class,
1155+
* even if it has been moved to a super type,
1156+
* or an override has been removed.
1157+
*
1158+
* This is tested in
1159+
* - sbt-test/tasty-compat/remove-override
1160+
* - sbt-test/tasty-compat/move-method
1161+
*/
1162+
def lookupInSuper =
1163+
val cls = ownerTpe.classSymbol
1164+
if cls.exists then
1165+
cls.asClass.classDenot
1166+
.findMember(name, cls.thisType, EmptyFlags, excluded=Private)
1167+
.atSignature(sig, target)
1168+
else
1169+
NoDenotation
1170+
1171+
val denot =
1172+
val d = ownerTpe.decl(name).atSignature(sig, target)
1173+
(if !d.exists then lookupInSuper else d).asSeenFrom(prefix)
1174+
1175+
makeSelect(qual, name, denot)
1176+
1177+
def readSelectInPoly(): Select =
1178+
val tree = readSelectIn()
1179+
val info = readType()
1180+
tree.withType(tree.symbol.copy(info = info).termRef)
1181+
11461182
def readQualId(): (untpd.Ident, TypeRef) =
11471183
val qual = readTerm().asInstanceOf[untpd.Ident]
11481184
(untpd.Ident(qual.name).withSpan(qual.span), qual.tpe.asInstanceOf[TypeRef])
@@ -1292,37 +1328,8 @@ class TreeUnpickler(reader: TastyReader,
12921328
case SELECTouter =>
12931329
val levels = readNat()
12941330
readTerm().outerSelect(levels, SkolemType(readType()))
1295-
case SELECTin =>
1296-
var sname = readName()
1297-
val qual = readTerm()
1298-
val ownerTpe = readType()
1299-
val owner = ownerTpe.typeSymbol
1300-
val SignedName(name, sig, target) = sname: @unchecked // only methods with params use SELECTin
1301-
val qualType = qual.tpe.widenIfUnstable
1302-
val prefix = ctx.typeAssigner.maybeSkolemizePrefix(qualType, name)
1303-
1304-
/** Tasty should still be able to resolve a method from another root class,
1305-
* even if it has been moved to a super type,
1306-
* or an override has been removed.
1307-
*
1308-
* This is tested in
1309-
* - sbt-test/tasty-compat/remove-override
1310-
* - sbt-test/tasty-compat/move-method
1311-
*/
1312-
def lookupInSuper =
1313-
val cls = ownerTpe.classSymbol
1314-
if cls.exists then
1315-
cls.asClass.classDenot
1316-
.findMember(name, cls.thisType, EmptyFlags, excluded=Private)
1317-
.atSignature(sig, target)
1318-
else
1319-
NoDenotation
1320-
1321-
val denot =
1322-
val d = ownerTpe.decl(name).atSignature(sig, target)
1323-
(if !d.exists then lookupInSuper else d).asSeenFrom(prefix)
1324-
1325-
makeSelect(qual, name, denot)
1331+
case SELECTin => readSelectIn()
1332+
case SELECTinPoly => readSelectInPoly()
13261333
case REPEATED =>
13271334
val elemtpt = readTpt()
13281335
SeqLiteral(until(end)(readTerm()), elemtpt)

compiler/src/dotty/tools/dotc/semanticdb/SemanticSymbolBuilder.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ class SemanticSymbolBuilder:
9090
case _ =>
9191
end find
9292
val sig = sym.signature
93-
find(_.signature == sig)
93+
// the polymorphic signature methods (invoke/invokeExact) are never overloaded
94+
// we changed the signature at the call site, which means they won't match,
95+
// but we still shouldn't add any overload index in this case.
96+
find(_.signature == sig || defn.wasPolymorphicSignature(sym))
9497

9598
def addDescriptor(sym: Symbol): Unit =
9699
if sym.is(ModuleClass) then

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
204204
return transformSelect(cpy.Select(tree)(qual.select(pobj).withSpan(qual.span), tree.name), targs)
205205
case _ =>
206206
}
207-
val tree1 = super.transform(tree)
207+
val tree0 = super.transform(tree)
208+
val tree1 = if defn.wasPolymorphicSignature(tree.symbol) then tree0.withType(tree.tpe) else tree0
208209
constToLiteral(tree1) match {
209210
case _: Literal => tree1
210211
case _ => superAcc.transformSelect(tree1, targs)

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,13 @@ abstract class Recheck extends Phase, SymTransformer:
261261
mt.instantiate(argTypes)
262262

263263
def recheckApply(tree: Apply, pt: Type)(using Context): Type =
264-
val funtpe = recheck(tree.fun)
265-
funtpe.widen match
264+
val wasPolymorphicSignature = tree.fun.tpe match
265+
case funRef: TermRef => defn.wasPolymorphicSignature(funRef.symbol)
266+
case _ => false
267+
if wasPolymorphicSignature then tree.fun.tpe.widenTermRefExpr.finalResultType
268+
else
269+
val funtpe = recheck(tree.fun)
270+
funtpe.widen match
266271
case fntpe: MethodType =>
267272
assert(fntpe.paramInfos.hasSameLengthAs(tree.args))
268273
val formals =

0 commit comments

Comments
 (0)