Skip to content

Commit caef187

Browse files
committed
Re-implement with PolySig support w/ a Typed
1 parent 8cdf496 commit caef187

File tree

11 files changed

+107
-119
lines changed

11 files changed

+107
-119
lines changed

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, defn.VoidClass
4729+
defn.BoxedDoubleClass, defn.StringClass, jsdefn.JavaLangVoidClass
47304730
)
47314731
}
47324732

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ 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+
2932
@threadUnsafe lazy val ScalaJSJSPackageVal = requiredPackage("scala.scalajs.js")
3033
@threadUnsafe lazy val ScalaJSJSPackageClass = ScalaJSJSPackageVal.moduleClass.asClass
3134
@threadUnsafe lazy val JSPackage_typeOfR = ScalaJSJSPackageClass.requiredMethodRef("typeOf")

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -485,9 +485,6 @@ 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-
491488
@tu lazy val InvokerModule = requiredModule("scala.runtime.coverage.Invoker")
492489
@tu lazy val InvokedMethodRef = InvokerModule.requiredMethodRef("invoked")
493490

@@ -735,7 +732,6 @@ class Definitions {
735732
}
736733
def JavaEnumType = JavaEnumClass.typeRef
737734

738-
739735
@tu lazy val MethodHandleClass: ClassSymbol = requiredClass("java.lang.invoke.MethodHandle")
740736
@tu lazy val MethodHandlesLookupClass: ClassSymbol = requiredClass("java.lang.invoke.MethodHandles.Lookup")
741737
@tu lazy val VarHandleClass: ClassSymbol = requiredClass("java.lang.invoke.VarHandle")

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 | SELECTinPoly =>
132+
case REFINEDtype | TERMREFin | TYPEREFin | SELECTin =>
133133
printName(); printTree(); printTrees()
134134
case RETURN | HOLE =>
135135
printNat(); printTrees()

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -413,14 +413,6 @@ 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-
}
424416
else // select from owner
425417
writeByte(SELECTin)
426418
withLength {

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

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,7 @@ class TreeUnpickler(reader: TastyReader,
11301130

11311131
def makeSelect(qual: Tree, name: Name, denot: Denotation): Select =
11321132
var qualType = qual.tpe.widenIfUnstable
1133+
val owner = denot.symbol.maybeOwner
11331134
val tpe0 = name match
11341135
case name: TypeName => TypeRef(qualType, name, denot)
11351136
case name: TermName => TermRef(qualType, name, denot)
@@ -1141,43 +1142,6 @@ class TreeUnpickler(reader: TastyReader,
11411142
val denot = accessibleDenot(qual.tpe.widenIfUnstable, name, sig, target)
11421143
makeSelect(qual, name, denot)
11431144

1144-
def readSelectIn(): Select =
1145-
var sname = readName()
1146-
val qual = readTerm()
1147-
val ownerTpe = readType()
1148-
val owner = ownerTpe.typeSymbol
1149-
val SignedName(name, sig, target) = sname: @unchecked // only methods with params use SELECTin
1150-
val qualType = qual.tpe.widenIfUnstable
1151-
val prefix = ctx.typeAssigner.maybeSkolemizePrefix(qualType, name)
1152-
1153-
/** Tasty should still be able to resolve a method from another root class,
1154-
* even if it has been moved to a super type,
1155-
* or an override has been removed.
1156-
*
1157-
* This is tested in
1158-
* - sbt-test/tasty-compat/remove-override
1159-
* - sbt-test/tasty-compat/move-method
1160-
*/
1161-
def lookupInSuper =
1162-
val cls = ownerTpe.classSymbol
1163-
if cls.exists then
1164-
cls.asClass.classDenot
1165-
.findMember(name, cls.thisType, EmptyFlags, excluded=Private)
1166-
.atSignature(sig, target)
1167-
else
1168-
NoDenotation
1169-
1170-
val denot =
1171-
val d = ownerTpe.decl(name).atSignature(sig, target)
1172-
(if !d.exists then lookupInSuper else d).asSeenFrom(prefix)
1173-
1174-
makeSelect(qual, name, denot)
1175-
1176-
def readSelectInPoly(): Select =
1177-
val tree = readSelectIn()
1178-
val info = readType()
1179-
tree.withType(tree.symbol.copy(info = info).termRef)
1180-
11811145
def readQualId(): (untpd.Ident, TypeRef) =
11821146
val qual = readTerm().asInstanceOf[untpd.Ident]
11831147
(untpd.Ident(qual.name).withSpan(qual.span), qual.tpe.asInstanceOf[TypeRef])
@@ -1267,13 +1231,25 @@ class TreeUnpickler(reader: TastyReader,
12671231
val fn = readTerm()
12681232
val args = until(end)(readTerm())
12691233
if fn.symbol.isConstructor then constructorApply(fn, args)
1270-
else tpd.Apply(fn, args)
1234+
else if defn.isPolymorphicSignature(fn.symbol) then
1235+
val info = MethodType(args.map(_.tpe.widen), defn.ObjectType)
1236+
val fun2 = fn.withType(fn.symbol.copy(info = info).termRef)
1237+
tpd.Apply(fun2, args)
1238+
else
1239+
tpd.Apply(fn, args)
12711240
case TYPEAPPLY =>
12721241
tpd.TypeApply(readTerm(), until(end)(readTpt()))
12731242
case TYPED =>
12741243
val expr = readTerm()
12751244
val tpt = readTpt()
1276-
Typed(expr, tpt)
1245+
expr match
1246+
case Apply(fun, args) if defn.wasPolymorphicSignature(fun.symbol) =>
1247+
val info = MethodType(args.map(_.tpe.widen), tpt.tpe)
1248+
val fun2 = fun.withType(fun.symbol.copy(info = info).termRef)
1249+
val expr2 = tpd.cpy.Apply(expr)(fun2, args)
1250+
Typed(expr2, tpt)
1251+
case _ =>
1252+
Typed(expr, tpt)
12771253
case ASSIGN =>
12781254
Assign(readTerm(), readTerm())
12791255
case BLOCK =>
@@ -1326,8 +1302,37 @@ class TreeUnpickler(reader: TastyReader,
13261302
case SELECTouter =>
13271303
val levels = readNat()
13281304
readTerm().outerSelect(levels, SkolemType(readType()))
1329-
case SELECTin => readSelectIn()
1330-
case SELECTinPoly => readSelectInPoly()
1305+
case SELECTin =>
1306+
var sname = readName()
1307+
val qual = readTerm()
1308+
val ownerTpe = readType()
1309+
val owner = ownerTpe.typeSymbol
1310+
val SignedName(name, sig, target) = sname: @unchecked // only methods with params use SELECTin
1311+
val qualType = qual.tpe.widenIfUnstable
1312+
val prefix = ctx.typeAssigner.maybeSkolemizePrefix(qualType, name)
1313+
1314+
/** Tasty should still be able to resolve a method from another root class,
1315+
* even if it has been moved to a super type,
1316+
* or an override has been removed.
1317+
*
1318+
* This is tested in
1319+
* - sbt-test/tasty-compat/remove-override
1320+
* - sbt-test/tasty-compat/move-method
1321+
*/
1322+
def lookupInSuper =
1323+
val cls = ownerTpe.classSymbol
1324+
if cls.exists then
1325+
cls.asClass.classDenot
1326+
.findMember(name, cls.thisType, EmptyFlags, excluded=Private)
1327+
.atSignature(sig, target)
1328+
else
1329+
NoDenotation
1330+
1331+
val denot =
1332+
val d = ownerTpe.decl(name).atSignature(sig, target)
1333+
(if !d.exists then lookupInSuper else d).asSeenFrom(prefix)
1334+
1335+
makeSelect(qual, name, denot)
13311336
case REPEATED =>
13321337
val elemtpt = readTpt()
13331338
SeqLiteral(until(end)(readTerm()), elemtpt)

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,8 @@ trait Applications extends Compatibility {
953953
// being brought into existence as needed
954954
val info = MethodType(proto.typedArgs().map(_.tpe.widen), resultType)
955955
val fun2 = fun1.withType(funRef.symbol.copy(info = info).termRef)
956-
simpleApply(fun2, proto)
956+
val app = simpleApply(fun2, proto)
957+
Typed(app, TypeTree(resultType))
957958
else
958959
val app = ApplyTo(tree, fun1, funRef, proto, pt)
959960
convertNewGenericArray(

project/Build.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,10 +1813,9 @@ object Build {
18131813
settings(disableDocSetting).
18141814
settings(
18151815
versionScheme := Some("semver-spec"),
1816-
if (mode == Bootstrapped) Def.settings(
1817-
commonMiMaSettings,
1818-
mimaBinaryIssueFilters ++= MiMaFilters.TastyCore,
1819-
) else {
1816+
if (mode == Bootstrapped) {
1817+
commonMiMaSettings
1818+
} else {
18201819
Nil
18211820
}
18221821
)

project/MiMaFilters.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,4 @@ object MiMaFilters {
66
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.internal.MappedAlternative"),
77
ProblemFilters.exclude[MissingClassProblem]("scala.caps"),
88
)
9-
val TastyCore: Seq[ProblemFilter] = Seq(
10-
ProblemFilters.exclude[MissingMethodProblem]("dotty.tools.tasty.TastyFormat.SELECTinPoly"),
11-
)
129
}

tasty/src/dotty/tools/tasty/TastyFormat.scala

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ Standard-Section: "ASTs" TopLevelStat*
8686
IDENT NameRef Type -- Used when term ident’s type is not a TermRef
8787
SELECT possiblySigned_NameRef qual_Term -- qual.name
8888
SELECTin Length possiblySigned_NameRef qual_Term owner_Type -- qual.name, referring to a symbol declared in owner that has the given signature (see note below)
89-
SELECTinPoly Length possiblySigned_NameRef qual_Term owner_Type method_Type -- like SELECTin, but with the method_Type too (because signature polymorphic)
9089
QUALTHIS typeIdent_Tree -- id.this, different from THIS in that it contains a qualifier ident with position.
9190
NEW clsType_Term -- new cls
9291
THROW throwableExpr_Term -- throw throwableExpr
@@ -579,7 +578,6 @@ object TastyFormat {
579578
// final val ??? = 178
580579
// final val ??? = 179
581580
final val METHODtype = 180
582-
final val SELECTinPoly = 181
583581

584582
final val MATCHtype = 190
585583
final val MATCHtpt = 191
@@ -779,7 +777,6 @@ object TastyFormat {
779777
case TERMREFin => "TERMREFin"
780778
case TYPEREFin => "TYPEREFin"
781779
case SELECTin => "SELECTin"
782-
case SELECTinPoly => "SELECTinPoly"
783780

784781
case REFINEDtype => "REFINEDtype"
785782
case REFINEDtpt => "REFINEDtpt"
@@ -810,7 +807,7 @@ object TastyFormat {
810807
*/
811808
def numRefs(tag: Int): Int = tag match {
812809
case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | RETURN | BIND |
813-
SELFDEF | REFINEDtype | TERMREFin | TYPEREFin | SELECTin | SELECTinPoly | HOLE => 1
810+
SELFDEF | REFINEDtype | TERMREFin | TYPEREFin | SELECTin | HOLE => 1
814811
case RENAMED | PARAMtype => 2
815812
case POLYtype | TYPELAMBDAtype | METHODtype => -1
816813
case _ => 0

tests/run/i11332.scala

Lines changed: 50 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,61 +3,59 @@ import scala.language.unsafeNulls
33

44
import java.lang.invoke._, MethodType.methodType
55

6-
object Test extends O:
6+
object Test extends Foo:
77
def main(args: Array[String]): Unit = ()
88

9-
class O {
10-
def bar(x: Int): Int = -x
11-
def baz(s: String): String = s.reverse
12-
def dingo(l: Long): String = "long"
13-
def dingo(i: Int): String = "int"
14-
def putStr(s: String): Unit = ()
15-
def returnAny(s: String): Object = s
9+
class Foo {
10+
def neg(x: Int): Int = -x
11+
def rev(s: String): String = s.reverse
12+
def over(l: Long): String = "long"
13+
def over(i: Int): String = "int"
14+
def unit(s: String): Unit = ()
15+
def obj(s: String): Object = s
1616
def id[T](x: T): T = x
1717

1818
val l = MethodHandles.lookup()
19-
val mh1 = l.findVirtual(classOf[O], "bar", methodType(classOf[Int], classOf[Int]))
20-
val mh2 = l.findVirtual(classOf[O], "baz", methodType(classOf[String], classOf[String]))
21-
val mh3 = l.findVirtual(classOf[O], "dingo", methodType(classOf[String], classOf[Long]))
22-
val mh4 = l.findVirtual(classOf[O], "dingo", methodType(classOf[String], classOf[Int]))
23-
val mh6 = l.findVirtual(classOf[O], "putStr", methodType(classOf[Unit], classOf[String]))
24-
val mh7 = l.findVirtual(classOf[O], "returnAny", methodType(classOf[Any], classOf[String]))
25-
val mh8 = l.findStatic(classOf[ClassLoader], "getPlatformClassLoader", methodType(classOf[ClassLoader]))
26-
27-
val test1_1 = assert(-42 == (mh1.invokeExact(this, 42): Int))
28-
val test1_2 = assert(-33 == (mh1.invokeExact(this, 33): Int))
29-
30-
val test2_1 = assert("oof" == (mh2.invokeExact(this, "foo"): String))
31-
val test2_2 = assert("rab" == (mh2.invokeExact(this, "bar"): String))
32-
val test3 = assert("long" == (mh3.invokeExact(this, 1L): String))
33-
val test4 = assert("int" == (mh4.invokeExact(this, 1): String))
34-
val test5_1 = assert(-3 == (id(mh1.invokeExact(this, 3)): Int))
35-
val test5_2 = expectWrongMethod(mh1.invokeExact(this, 4))
36-
37-
val test6_1 = mh6.invokeExact(this, "hi"): Unit
38-
val test6_2 = { val hi2: Unit = mh6.invokeExact(this, "hi2"); assert((()) == hi2) }
39-
val test6_3 = { def hi3: Unit = mh6.invokeExact(this, "hi3"); assert((()) == hi3) }
40-
41-
val test7_statbk = { mh7.invokeExact(this, "any"); () }
42-
val test7_valdef = { val any2 = mh7.invokeExact(this, "any2"); assert("any2" == any2) }
43-
val test7_defdef = { def any3 = mh7.invokeExact(this, "any3"); assert("any3" == any3) }
44-
45-
val cl1: ClassLoader = mh8.invoke()
46-
val cl2: ClassLoader = mh8.invoke().asInstanceOf[ClassLoader]
47-
48-
val test9_1 = expectWrongMethod(assert(-1 == mh1.invokeExact(this, 1)))
49-
val test9_3 = expectWrongMethod {
50-
l.findVirtual(classOf[O], "bar", methodType(classOf[Int], classOf[Int])).invokeExact(this, 3) // testing inline
51-
}
52-
val test9_4 =
53-
val res = l
54-
.findVirtual(classOf[O], "bar", methodType(classOf[Int], classOf[Int])) // testing inline
55-
.invokeExact(this, 4): Int
56-
assert(-4 == res)
57-
58-
def expectWrongMethod(op: => Any) =
59-
try
60-
op
61-
throw new AssertionError("expected operation to fail but it didn't")
62-
catch case expected: WrongMethodTypeException => ()
19+
val mhNeg = l.findVirtual(classOf[Foo], "neg", methodType(classOf[Int], classOf[Int]))
20+
val mhRev = l.findVirtual(classOf[Foo], "rev", methodType(classOf[String], classOf[String]))
21+
val mhOverL = l.findVirtual(classOf[Foo], "over", methodType(classOf[String], classOf[Long]))
22+
val mhOverI = l.findVirtual(classOf[Foo], "over", methodType(classOf[String], classOf[Int]))
23+
val mhUnit = l.findVirtual(classOf[Foo], "unit", methodType(classOf[Unit], classOf[String]))
24+
val mhObj = l.findVirtual(classOf[Foo], "obj", methodType(classOf[Any], classOf[String]))
25+
val mhCL = l.findStatic(classOf[ClassLoader], "getPlatformClassLoader", methodType(classOf[ClassLoader]))
26+
27+
val testNeg1 = assert(-42 == (mhNeg.invokeExact(this, 42): Int))
28+
val testNeg2 = assert(-33 == (mhNeg.invokeExact(this, 33): Int))
29+
30+
val testRev1 = assert("oof" == (mhRev.invokeExact(this, "foo"): String))
31+
val testRev2 = assert("rab" == (mhRev.invokeExact(this, "bar"): String))
32+
33+
val testOverL = assert("long" == (mhOverL.invokeExact(this, 1L): String))
34+
val testOVerI = assert("int" == (mhOverI.invokeExact(this, 1): String))
35+
36+
val testNeg_tvar = assert(-3 == (id(mhNeg.invokeExact(this, 3)): Int))
37+
val testNeg_obj = expectWrongMethod(mhNeg.invokeExact(this, 4))
38+
39+
val testUnit_exp = { mhUnit.invokeExact(this, "hi"): Unit; () }
40+
val testUnit_val = { val hi2: Unit = mhUnit.invokeExact(this, "hi2"); assert((()) == hi2) }
41+
val testUnit_def = { def hi3: Unit = mhUnit.invokeExact(this, "hi3"); assert((()) == hi3) }
42+
43+
val testObj_exp = { mhObj.invokeExact(this, "any"); () }
44+
val testObj_val = { val any2 = mhObj.invokeExact(this, "any2"); assert("any2" == any2) }
45+
val testObj_def = { def any3 = mhObj.invokeExact(this, "any3"); assert("any3" == any3) }
46+
47+
val testCl1_pass = assert(null != (mhCL.invoke(): ClassLoader))
48+
val testCl2_cast = assert(null != (mhCL.invoke().asInstanceOf[ClassLoader]: ClassLoader))
49+
50+
val testNeg_inline_obj = expectWrongMethod(l
51+
.findVirtual(classOf[Foo], "neg", methodType(classOf[Int], classOf[Int]))
52+
.invokeExact(this, 3))
53+
val testNeg_inline_pass = assert(-4 == (l
54+
.findVirtual(classOf[Foo], "neg", methodType(classOf[Int], classOf[Int]))
55+
.invokeExact(this, 4): Int))
56+
57+
def expectWrongMethod(op: => Any) = try {
58+
op
59+
throw new AssertionError("expected operation to fail but it didn't")
60+
} catch case expected: WrongMethodTypeException => ()
6361
}

0 commit comments

Comments
 (0)