Skip to content

Commit f6edd41

Browse files
committed
Use SELECTin by default to pickle methods with parameters.
1 parent 8c2104a commit f6edd41

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+642
-41
lines changed

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

+18-11
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ object Denotations {
477477
val jointInfo = infoMeet(info1, info2, safeIntersection)
478478
if jointInfo.exists then
479479
val sym = if symScore >= 0 then sym1 else sym2
480-
JointRefDenotation(sym, jointInfo, denot1.validFor & denot2.validFor, pre)
480+
JointRefDenotation(sym, jointInfo, denot1.validFor & denot2.validFor, pre, denot1.isRefinedMethod || denot2.isRefinedMethod)
481481
else if symScore == 2 then denot1
482482
else if symScore == -2 then denot2
483483
else
@@ -569,7 +569,7 @@ object Denotations {
569569

570570
/** A non-overloaded denotation */
571571
abstract class SingleDenotation(symbol: Symbol, initInfo: Type) extends Denotation(symbol, initInfo) {
572-
protected def newLikeThis(symbol: Symbol, info: Type, pre: Type): SingleDenotation
572+
protected def newLikeThis(symbol: Symbol, info: Type, pre: Type, isRefinedMethod: Boolean): SingleDenotation
573573

574574
final def name(using Context): Name = symbol.name
575575

@@ -582,6 +582,9 @@ object Denotations {
582582
*/
583583
def prefix: Type = NoPrefix
584584

585+
/** True if the info of this denotation comes from a refinement. */
586+
def isRefinedMethod: Boolean = false
587+
585588
/** For SymDenotations, the language-specific signature of the info, depending on
586589
* where the symbol is defined. For non-SymDenotations, the Scala 3
587590
* signature.
@@ -615,9 +618,9 @@ object Denotations {
615618
case _ => Signature.NotAMethod
616619
}
617620

618-
def derivedSingleDenotation(symbol: Symbol, info: Type, pre: Type = this.prefix)(using Context): SingleDenotation =
619-
if ((symbol eq this.symbol) && (info eq this.info) && (pre eq this.prefix)) this
620-
else newLikeThis(symbol, info, pre)
621+
def derivedSingleDenotation(symbol: Symbol, info: Type, pre: Type = this.prefix, isRefinedMethod: Boolean = this.isRefinedMethod)(using Context): SingleDenotation =
622+
if ((symbol eq this.symbol) && (info eq this.info) && (pre eq this.prefix) && (isRefinedMethod == this.isRefinedMethod)) this
623+
else newLikeThis(symbol, info, pre, isRefinedMethod)
621624

622625
def mapInfo(f: Type => Type)(using Context): SingleDenotation =
623626
derivedSingleDenotation(symbol, f(info))
@@ -1126,26 +1129,30 @@ object Denotations {
11261129
prefix: Type) extends NonSymSingleDenotation(symbol, initInfo, prefix) {
11271130
validFor = initValidFor
11281131
override def hasUniqueSym: Boolean = true
1129-
protected def newLikeThis(s: Symbol, i: Type, pre: Type): SingleDenotation =
1130-
new UniqueRefDenotation(s, i, validFor, pre)
1132+
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean): SingleDenotation =
1133+
if isRefinedMethod then
1134+
new JointRefDenotation(s, i, validFor, pre, isRefinedMethod)
1135+
else
1136+
new UniqueRefDenotation(s, i, validFor, pre)
11311137
}
11321138

11331139
class JointRefDenotation(
11341140
symbol: Symbol,
11351141
initInfo: Type,
11361142
initValidFor: Period,
1137-
prefix: Type) extends NonSymSingleDenotation(symbol, initInfo, prefix) {
1143+
prefix: Type,
1144+
override val isRefinedMethod: Boolean) extends NonSymSingleDenotation(symbol, initInfo, prefix) {
11381145
validFor = initValidFor
11391146
override def hasUniqueSym: Boolean = false
1140-
protected def newLikeThis(s: Symbol, i: Type, pre: Type): SingleDenotation =
1141-
new JointRefDenotation(s, i, validFor, pre)
1147+
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean): SingleDenotation =
1148+
new JointRefDenotation(s, i, validFor, pre, isRefinedMethod)
11421149
}
11431150

11441151
class ErrorDenotation(using Context) extends NonSymSingleDenotation(NoSymbol, NoType, NoType) {
11451152
override def exists: Boolean = false
11461153
override def hasUniqueSym: Boolean = false
11471154
validFor = Period.allInRun(ctx.runId)
1148-
protected def newLikeThis(s: Symbol, i: Type, pre: Type): SingleDenotation =
1155+
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean): SingleDenotation =
11491156
this
11501157
}
11511158

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -1504,8 +1504,11 @@ object SymDenotations {
15041504

15051505
// ----- copies and transforms ----------------------------------------
15061506

1507-
protected def newLikeThis(s: Symbol, i: Type, pre: Type): SingleDenotation =
1508-
new UniqueRefDenotation(s, i, validFor, pre)
1507+
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean): SingleDenotation =
1508+
if isRefinedMethod then
1509+
new JointRefDenotation(s, i, validFor, pre, isRefinedMethod)
1510+
else
1511+
new UniqueRefDenotation(s, i, validFor, pre)
15091512

15101513
/** Copy this denotation, overriding selective fields */
15111514
final def copySymDenotation(

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

+4-3
Original file line numberDiff line numberDiff line change
@@ -772,15 +772,16 @@ object Types {
772772
pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo)
773773
}
774774
else
775+
val isRefinedMethod = rinfo.isInstanceOf[MethodOrPoly]
775776
val joint = pdenot.meet(
776-
new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId), pre),
777+
new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId), pre, isRefinedMethod),
777778
pre,
778779
safeIntersection = ctx.base.pendingMemberSearches.contains(name))
779780
joint match
780781
case joint: SingleDenotation
781-
if rinfo.isInstanceOf[MethodOrPoly] && rinfo <:< joint.info =>
782+
if isRefinedMethod && rinfo <:< joint.info =>
782783
// use `rinfo` to keep the right parameter names for named args. See i8516.scala.
783-
joint.derivedSingleDenotation(joint.symbol, rinfo)
784+
joint.derivedSingleDenotation(joint.symbol, rinfo, pre, isRefinedMethod)
784785
case _ =>
785786
joint
786787
}

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

+11-10
Original file line numberDiff line numberDiff line change
@@ -409,22 +409,23 @@ class TreePickler(pickler: TastyPickler) {
409409
case _ =>
410410
val sig = tree.tpe.signature
411411
var ename = tree.symbol.targetName
412-
val isAmbiguous =
413-
sig != Signature.NotAMethod
414-
&& qual.tpe.nonPrivateMember(name).match
415-
case d: MultiDenotation => d.atSignature(sig, ename).isInstanceOf[MultiDenotation]
416-
case _ => false
417-
if isAmbiguous then
412+
val selectFromQualifier =
413+
name.isTypeName
414+
|| qual.isInstanceOf[TreePickler.Hole] // holes have no symbol
415+
|| sig == Signature.NotAMethod // no overload resolution necessary
416+
|| !tree.denot.symbol.exists // polymorphic function type
417+
|| tree.denot.asSingleDenotation.isRefinedMethod // refined methods have no defining class symbol
418+
if selectFromQualifier then
419+
writeByte(if name.isTypeName then SELECTtpt else SELECT)
420+
pickleNameAndSig(name, sig, ename)
421+
pickleTree(qual)
422+
else // select from owner
418423
writeByte(SELECTin)
419424
withLength {
420425
pickleNameAndSig(name, tree.symbol.signature, ename)
421426
pickleTree(qual)
422427
pickleType(tree.symbol.owner.typeRef)
423428
}
424-
else
425-
writeByte(if (name.isTypeName) SELECTtpt else SELECT)
426-
pickleNameAndSig(name, sig, ename)
427-
pickleTree(qual)
428429
}
429430
case Apply(fun, args) =>
430431
if (fun.symbol eq defn.throwMethod) {

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

+29-10
Original file line numberDiff line numberDiff line change
@@ -1186,15 +1186,34 @@ class TreeUnpickler(reader: TastyReader,
11861186
case SELECTin =>
11871187
var sname = readName()
11881188
val qual = readTerm()
1189-
val owner = readType()
1190-
def select(name: Name, denot: Denotation) =
1191-
val prefix = ctx.typeAssigner.maybeSkolemizePrefix(qual.tpe.widenIfUnstable, name)
1192-
makeSelect(qual, name, denot.asSeenFrom(prefix))
1193-
sname match
1194-
case SignedName(name, sig, target) =>
1195-
select(name, owner.decl(name).atSignature(sig, target))
1196-
case name =>
1197-
select(name, owner.decl(name))
1189+
val ownerTpe = readType()
1190+
val owner = ownerTpe.typeSymbol
1191+
val SignedName(name, sig, target) = sname: @unchecked // only methods with params use SELECTin
1192+
val qualType = qual.tpe.widenIfUnstable
1193+
val prefix = ctx.typeAssigner.maybeSkolemizePrefix(qualType, name)
1194+
1195+
/** Tasty should still be able to resolve a method from another root class,
1196+
* even if it has been moved to a super type,
1197+
* or an override has been removed.
1198+
*
1199+
* This is tested in
1200+
* - sbt-dotty/sbt-test/tasty-compat/remove-override
1201+
* - sbt-dotty/sbt-test/tasty-compat/move-method
1202+
*/
1203+
def lookupInSuper =
1204+
val cls = ownerTpe.classSymbol
1205+
if cls.exists then
1206+
cls.asClass.classDenot
1207+
.findMember(name, cls.thisType, EmptyFlags, excluded=Private)
1208+
.atSignature(sig, target)
1209+
else
1210+
NoDenotation
1211+
1212+
val denot =
1213+
val d = ownerTpe.decl(name).atSignature(sig, target)
1214+
(if !d.exists then lookupInSuper else d).asSeenFrom(prefix)
1215+
1216+
makeSelect(qual, name, denot)
11981217
case REPEATED =>
11991218
val elemtpt = readTpt()
12001219
SeqLiteral(until(end)(readTerm()), elemtpt)
@@ -1494,4 +1513,4 @@ object TreeUnpickler {
14941513
final val AllDefs = 2 // add everything
14951514

14961515
class TreeWithoutOwner extends Exception
1497-
}
1516+
}

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,10 @@ object Scala3:
114114

115115
/** Is symbol global? Non-global symbols get localN names */
116116
def isGlobal(using Context): Boolean =
117-
sym.is(Package)
118-
|| !sym.isSelfSym && (sym.is(Param) || sym.owner.isClass) && sym.owner.isGlobal
117+
sym.exists && (
118+
sym.is(Package)
119+
|| !sym.isSelfSym && (sym.is(Param) || sym.owner.isClass) && sym.owner.isGlobal
120+
)
119121

120122
def isLocalWithinSameName(using Context): Boolean =
121123
sym.exists && !sym.isGlobal && sym.name == sym.owner.name
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package a
2+
3+
object A {
4+
5+
class Buf[A] {
6+
def append(a: A): this.type = this
7+
def append(a: A*): this.type = this
8+
}
9+
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package a
2+
3+
object A {
4+
5+
class Buf[A] {
6+
def append(a: A): this.type = this
7+
}
8+
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import a.*
2+
3+
object B {
4+
val foo = new A.Buf[Seq[Double]]
5+
val bar = Seq.empty[Double]
6+
foo.append(bar)
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
lazy val a = project.in(file("a"))
2+
.settings(
3+
Compile / classDirectory := (ThisBuild / baseDirectory).value / "b-input"
4+
)
5+
6+
lazy val b = project.in(file("b"))
7+
.settings(
8+
Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "b-input",
9+
Compile / classDirectory := (ThisBuild / baseDirectory).value / "c-input"
10+
)
11+
12+
lazy val `a-changes` = project.in(file("a-changes"))
13+
.settings(
14+
Compile / classDirectory := (ThisBuild / baseDirectory).value / "c-input"
15+
)
16+
17+
lazy val c = project.in(file("."))
18+
.settings(
19+
scalacOptions ++= Seq("-from-tasty", "-Ycheck:readTasty"),
20+
Compile / sources := Seq(new java.io.File("c-input/B.tasty")),
21+
Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "c-input",
22+
Compile / classDirectory := (ThisBuild / baseDirectory).value / "c-output"
23+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import sbt._
2+
import Keys._
3+
4+
object DottyInjectedPlugin extends AutoPlugin {
5+
override def requires = plugins.JvmPlugin
6+
override def trigger = allRequirements
7+
8+
override val projectSettings = Seq(
9+
scalaVersion := sys.props("plugin.scalaVersion")
10+
)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version"))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# compile library A
2+
> a/compile
3+
# compile library B, from source, against A
4+
> b/compile
5+
# add a new overload to library A'
6+
> a-changes/compile
7+
# compile B, from tasty, against A', it should still compile: the overload does not conflict
8+
> c/compile
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package a
2+
3+
object A {
4+
5+
trait Box0[A] {
6+
def append(a: A): this.type = this
7+
}
8+
9+
trait BoxInt extends Box0[Int] {
10+
override def append(a: Int): this.type = this
11+
}
12+
13+
val box: BoxInt = new BoxInt {}
14+
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package a
2+
3+
object A {
4+
5+
trait Box0[A] {
6+
def append(a: A): this.type = this
7+
}
8+
9+
trait BoxInt extends Box0[Int]
10+
11+
val box: BoxInt = new BoxInt {}
12+
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import a.*
2+
3+
object B extends App {
4+
A.box.append(0)
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
lazy val a = project.in(file("a"))
2+
.settings(
3+
Compile / classDirectory := (ThisBuild / baseDirectory).value / "b-input"
4+
)
5+
6+
lazy val b = project.in(file("b"))
7+
.settings(
8+
Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "b-input",
9+
Compile / classDirectory := (ThisBuild / baseDirectory).value / "c-input"
10+
)
11+
12+
lazy val `a-changes` = project.in(file("a-changes"))
13+
.settings(
14+
Compile / classDirectory := (ThisBuild / baseDirectory).value / "c-input"
15+
)
16+
17+
lazy val c = project.in(file("."))
18+
.settings(
19+
scalacOptions ++= Seq("-from-tasty", "-Ycheck:readTasty"),
20+
Compile / sources := Seq(new java.io.File("c-input/B.tasty")),
21+
Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "c-input",
22+
Compile / classDirectory := (ThisBuild / baseDirectory).value / "c-output"
23+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import sbt._
2+
import Keys._
3+
4+
object DottyInjectedPlugin extends AutoPlugin {
5+
override def requires = plugins.JvmPlugin
6+
override def trigger = allRequirements
7+
8+
override val projectSettings = Seq(
9+
scalaVersion := sys.props("plugin.scalaVersion")
10+
)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version"))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# compile library A
2+
> a/compile
3+
# compile library B, from source, against A
4+
> b/compile
5+
# add a new override to library A'
6+
> a-changes/compile
7+
# compile B, from tasty, against A', it should still compile: the override is forward compatible
8+
> c/compile
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package a
2+
3+
object A {
4+
5+
trait Fn[-T1, +R] { def apply(v1: T1): R }
6+
7+
val fn0: Fn[Int, Int] { def apply(arg: Int): Int } = x => x
8+
9+
val fn = fn0
10+
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package a
2+
3+
object A {
4+
5+
trait Fn[-T1, +R] { def apply(v1: T1): R }
6+
7+
val fn0: Fn[Int, Int] = x => x
8+
9+
val fn = fn0
10+
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import a.*
2+
3+
object B extends App {
4+
A.fn(v1 = 0)
5+
}

0 commit comments

Comments
 (0)