Skip to content

Commit 736dd85

Browse files
authored
Merge pull request #11210 from dotty-staging/tasty/remove-select
use SELECTin by default in TASTy
2 parents ecceb15 + e48aeed commit 736dd85

Some content is hidden

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

66 files changed

+681
-50
lines changed

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

+23-12
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))
@@ -1107,7 +1110,11 @@ object Denotations {
11071110
case sd: SymDenotation => true
11081111
case _ => info eq symbol.info
11091112

1110-
if !owner.membersNeedAsSeenFrom(pre) && ((pre ne owner.thisType) || hasOriginalInfo)
1113+
def ownerIsPrefix = pre match
1114+
case pre: ThisType => pre.sameThis(owner.thisType)
1115+
case _ => false
1116+
1117+
if !owner.membersNeedAsSeenFrom(pre) && (!ownerIsPrefix || hasOriginalInfo)
11111118
|| symbol.is(NonMember)
11121119
then this
11131120
else derived(symbol.info)
@@ -1126,26 +1133,30 @@ object Denotations {
11261133
prefix: Type) extends NonSymSingleDenotation(symbol, initInfo, prefix) {
11271134
validFor = initValidFor
11281135
override def hasUniqueSym: Boolean = true
1129-
protected def newLikeThis(s: Symbol, i: Type, pre: Type): SingleDenotation =
1130-
new UniqueRefDenotation(s, i, validFor, pre)
1136+
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean): SingleDenotation =
1137+
if isRefinedMethod then
1138+
new JointRefDenotation(s, i, validFor, pre, isRefinedMethod)
1139+
else
1140+
new UniqueRefDenotation(s, i, validFor, pre)
11311141
}
11321142

11331143
class JointRefDenotation(
11341144
symbol: Symbol,
11351145
initInfo: Type,
11361146
initValidFor: Period,
1137-
prefix: Type) extends NonSymSingleDenotation(symbol, initInfo, prefix) {
1147+
prefix: Type,
1148+
override val isRefinedMethod: Boolean) extends NonSymSingleDenotation(symbol, initInfo, prefix) {
11381149
validFor = initValidFor
11391150
override def hasUniqueSym: Boolean = false
1140-
protected def newLikeThis(s: Symbol, i: Type, pre: Type): SingleDenotation =
1141-
new JointRefDenotation(s, i, validFor, pre)
1151+
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean): SingleDenotation =
1152+
new JointRefDenotation(s, i, validFor, pre, isRefinedMethod)
11421153
}
11431154

11441155
class ErrorDenotation(using Context) extends NonSymSingleDenotation(NoSymbol, NoType, NoType) {
11451156
override def exists: Boolean = false
11461157
override def hasUniqueSym: Boolean = false
11471158
validFor = Period.allInRun(ctx.runId)
1148-
protected def newLikeThis(s: Symbol, i: Type, pre: Type): SingleDenotation =
1159+
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean): SingleDenotation =
11491160
this
11501161
}
11511162

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

+9-3
Original file line numberDiff line numberDiff line change
@@ -896,11 +896,14 @@ object SymDenotations {
896896
* accessed via prefix `pre`?
897897
*/
898898
def membersNeedAsSeenFrom(pre: Type)(using Context): Boolean =
899+
def preIsThis = pre match
900+
case pre: ThisType => pre.sameThis(thisType)
901+
case _ => false
899902
!( this.isTerm
900903
|| this.isStaticOwner && !this.seesOpaques
901904
|| ctx.erasedTypes
902905
|| (pre eq NoPrefix)
903-
|| (pre eq thisType)
906+
|| preIsThis
904907
)
905908

906909
/** Is this symbol concrete, or that symbol deferred? */
@@ -1504,8 +1507,11 @@ object SymDenotations {
15041507

15051508
// ----- copies and transforms ----------------------------------------
15061509

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

15101516
/** Copy this denotation, overriding selective fields */
15111517
final def copySymDenotation(

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

+14-4
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
}
@@ -2662,8 +2663,11 @@ object Types {
26622663
* the future. See also NamedType#withDenot. Test case is neg/opaque-self-encoding.scala.
26632664
*/
26642665
private def designatorFor(prefix: Type, name: Name, denot: Denotation)(using Context): Designator = {
2666+
def ownerIsPrefix(owner: Symbol) = prefix match
2667+
case prefix: ThisType => prefix.sameThis(owner.thisType)
2668+
case _ => false
26652669
val sym = denot.symbol
2666-
if (sym.exists && (prefix.eq(NoPrefix) || prefix.ne(sym.owner.thisType)))
2670+
if (sym.exists && (prefix.eq(NoPrefix) || !ownerIsPrefix(sym.owner)))
26672671
sym
26682672
else
26692673
name
@@ -2735,6 +2739,12 @@ object Types {
27352739
case that: ThisType => tref.eq(that.tref)
27362740
case _ => false
27372741
}
2742+
2743+
/** Check that the rhs is a ThisType that refers to the same class.
2744+
*/
2745+
def sameThis(that: Type)(using Context): Boolean = (that eq this) || that.match
2746+
case that: ThisType => this.cls eq that.cls
2747+
case _ => false
27382748
}
27392749

27402750
final class CachedThisType(tref: TypeRef) extends ThisType(tref)

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/reporting/MessageRendering.scala

+7-4
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,13 @@ trait MessageRendering {
149149
if (posString.nonEmpty) sb.append(posString).append(EOL)
150150
if (pos.exists) {
151151
val pos1 = pos.nonInlined
152-
val (srcBefore, srcAfter, offset) = sourceLines(pos1, diagnosticLevel)
153-
val marker = columnMarker(pos1, offset, diagnosticLevel)
154-
val err = errorMsg(pos1, msg.message, offset)
155-
sb.append((srcBefore ::: marker :: err :: outer(pos, " " * (offset - 1)) ::: srcAfter).mkString(EOL))
152+
if (pos1.exists && pos1.source.file.exists) {
153+
val (srcBefore, srcAfter, offset) = sourceLines(pos1, diagnosticLevel)
154+
val marker = columnMarker(pos1, offset, diagnosticLevel)
155+
val err = errorMsg(pos1, msg.message, offset)
156+
sb.append((srcBefore ::: marker :: err :: outer(pos, " " * (offset - 1)) ::: srcAfter).mkString(EOL))
157+
}
158+
else sb.append(msg.message)
156159
}
157160
else sb.append(msg.message)
158161
sb.toString

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

compiler/src/dotty/tools/dotc/util/SourceFile.scala

+1-2
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends
196196
var idx = startOfLine(offset)
197197
var col = 0
198198
while (idx != offset) {
199-
col += (if (idx < length && content()(idx) == '\t') (tabInc - col) % tabInc else 1)
199+
col += (if (idx < content().length && content()(idx) == '\t') (tabInc - col) % tabInc else 1)
200200
idx += 1
201201
}
202202
col
@@ -285,4 +285,3 @@ object SourceFile {
285285
override def exists: Boolean = false
286286
override def atSpan(span: Span): SourcePosition = NoSourcePosition
287287
}
288-
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+
}

0 commit comments

Comments
 (0)