Skip to content

Commit e510418

Browse files
committed
Treat toplevel objects as package objects
1 parent 1f947a8 commit e510418

File tree

14 files changed

+117
-49
lines changed

14 files changed

+117
-49
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,15 @@ object NameOps {
8787
false
8888
}
8989

90+
def isPackageObjectName: Boolean = name match {
91+
case name: TermName => name == nme.PACKAGE || name.endsWith(str.TOPLEVEL_SUFFIX)
92+
case name: TypeName =>
93+
name.toTermName match {
94+
case ModuleClassName(original) => original.isPackageObjectName
95+
case _ => false
96+
}
97+
}
98+
9099
/** Convert this module name to corresponding module class name */
91100
def moduleClassName: TypeName = name.derived(ModuleClassName).toTypeName
92101

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ object StdNames {
2020
final val INITIALIZER_PREFIX = "initial$"
2121
final val AVOID_CLASH_SUFFIX = "$_avoid_name_clash_$"
2222
final val MODULE_SUFFIX = "$"
23-
final val TOPLEVEL_SUFFIX = "$object"
23+
final val TOPLEVEL_SUFFIX = "__object"
2424
final val NAME_JOIN = "$"
2525
final val DEFAULT_GETTER = "$default$"
2626
final val LOCALDUMMY_PREFIX = "<local " // owner of local blocks

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

Lines changed: 60 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import config.Config
1818
import reporting.diagnostic.Message
1919
import reporting.diagnostic.messages.BadSymbolicReference
2020
import reporting.trace
21+
import collection.mutable
2122

2223
import scala.annotation.internal.sharable
2324

@@ -518,12 +519,8 @@ object SymDenotations {
518519
name == tpnme.REFINE_CLASS
519520

520521
/** Is this symbol a package object or its module class? */
521-
def isPackageObject(implicit ctx: Context): Boolean = {
522-
val nameMatches =
523-
if (isType) name == tpnme.PACKAGE.moduleClassName
524-
else name == nme.PACKAGE
525-
nameMatches && (owner is Package) && (this is Module)
526-
}
522+
def isPackageObject(implicit ctx: Context): Boolean =
523+
name.isPackageObjectName && (owner is Package) && (this is Module)
527524

528525
/** Is this symbol an abstract type? */
529526
final def isAbstractType(implicit ctx: Context): Boolean = this is DeferredType
@@ -1665,9 +1662,9 @@ object SymDenotations {
16651662
val denots1 = collect(denots, ps)
16661663
p.classSymbol.denot match {
16671664
case parentd: ClassDenotation =>
1668-
denots1 union
1665+
denots1.union(
16691666
parentd.nonPrivateMembersNamed(name)
1670-
.mapInherited(ownDenots, denots1, thisType)
1667+
.mapInherited(ownDenots, denots1, thisType))
16711668
case _ =>
16721669
denots1
16731670
}
@@ -1925,17 +1922,39 @@ object SymDenotations {
19251922
initPrivateWithin: Symbol)
19261923
extends ClassDenotation(symbol, ownerIfExists, name, initFlags, initInfo, initPrivateWithin) {
19271924

1928-
private[this] var packageObjCache: SymDenotation = _
1929-
private[this] var packageObjRunId: RunId = NoRunId
1930-
1931-
/** The package object in this class, of one exists */
1932-
def packageObj(implicit ctx: Context): SymDenotation = {
1933-
if (packageObjRunId != ctx.runId) {
1934-
packageObjRunId = ctx.runId
1935-
packageObjCache = NoDenotation // break cycle in case we are looking for package object itself
1936-
packageObjCache = findMember(nme.PACKAGE, thisType, EmptyFlagConjunction, EmptyFlags).asSymDenotation
1925+
private[this] var packageObjsCache: List[ClassDenotation] = _
1926+
private[this] var packageObjsRunId: RunId = NoRunId
1927+
1928+
/** The package objects in this class */
1929+
def packageObjs(implicit ctx: Context): List[ClassDenotation] = {
1930+
if (packageObjsRunId != ctx.runId) {
1931+
packageObjsRunId = ctx.runId
1932+
packageObjsCache = Nil // break cycle in case we are looking for package object itself
1933+
packageObjsCache = {
1934+
val pkgObjBuf = new mutable.ListBuffer[ClassDenotation]
1935+
for (sym <- info.decls) { // don't use filter, since that loads classes with `$`s in their name
1936+
val denot = sym.lastKnownDenotation // don't use `sym.denot`, as this brings forward classes too early
1937+
if (denot.isType && denot.name.isPackageObjectName)
1938+
pkgObjBuf += sym.asClass.classDenot
1939+
}
1940+
pkgObjBuf.toList
1941+
}
19371942
}
1938-
packageObjCache
1943+
packageObjsCache
1944+
}
1945+
1946+
/** The package object (as a term symbol) in this package that might contain
1947+
* `sym` as a member.
1948+
*/
1949+
def packageObjFor(sym: Symbol)(implicit ctx: Context): Symbol = {
1950+
val owner = sym.maybeOwner
1951+
if (owner.is(Package)) NoSymbol
1952+
else if (owner.isPackageObject) owner.sourceModule
1953+
else // owner could be class inherited by package object (until package object inheritance is removed)
1954+
packageObjs.find(_.name == packageTypeName) match {
1955+
case Some(pobj) => pobj.sourceModule
1956+
case _ => NoSymbol
1957+
}
19391958
}
19401959

19411960
/** Looks in both the package object and the package for members. The precise algorithm
@@ -1951,28 +1970,31 @@ object SymDenotations {
19511970
* object that hides a class or object in the scala package of the same name, because
19521971
* the behavior would then be unintuitive for such members.
19531972
*/
1954-
override def computeNPMembersNamed(name: Name)(implicit ctx: Context): PreDenotation =
1955-
packageObj.moduleClass.denot match {
1956-
case pcls: ClassDenotation if !pcls.isCompleting =>
1957-
if (symbol eq defn.ScalaPackageClass) {
1958-
val denots = super.computeNPMembersNamed(name)
1959-
if (denots.exists) denots else pcls.computeNPMembersNamed(name)
1960-
}
1961-
else {
1962-
val denots = pcls.computeNPMembersNamed(name)
1963-
if (denots.exists) denots else super.computeNPMembersNamed(name)
1964-
}
1965-
case _ =>
1966-
super.computeNPMembersNamed(name)
1973+
override def computeNPMembersNamed(name: Name)(implicit ctx: Context): PreDenotation = {
1974+
def recur(pobjs: List[ClassDenotation], acc: PreDenotation): PreDenotation = pobjs match {
1975+
case pcls :: pobjs1 =>
1976+
if (pcls.isCompleting) recur(pobjs1, acc)
1977+
else recur(pobjs1, acc.union(pcls.computeNPMembersNamed(name)))
1978+
case nil =>
1979+
if (acc.exists) acc else super.computeNPMembersNamed(name)
19671980
}
1981+
if (symbol `eq` defn.ScalaPackageClass) {
1982+
val denots = super.computeNPMembersNamed(name)
1983+
if (denots.exists) denots
1984+
else recur(packageObjs, NoDenotation)
1985+
}
1986+
else recur(packageObjs, NoDenotation)
1987+
}
19681988

19691989
/** The union of the member names of the package and the package object */
19701990
override def memberNames(keepOnly: NameFilter)(implicit onBehalf: MemberNames, ctx: Context): Set[Name] = {
1971-
val ownNames = super.memberNames(keepOnly)
1972-
packageObj.moduleClass.denot match {
1973-
case pcls: ClassDenotation => ownNames union pcls.memberNames(keepOnly)
1974-
case _ => ownNames
1991+
def recur(pobjs: List[ClassDenotation], acc: Set[Name]): Set[Name] = pobjs match {
1992+
case pcls :: pobjs1 =>
1993+
recur(pobjs1, acc.union(pcls.memberNames(keepOnly)))
1994+
case nil =>
1995+
acc
19751996
}
1997+
recur(packageObjs, super.memberNames(keepOnly))
19761998
}
19771999

19782000
/** If another symbol with the same name is entered, unlink it,
@@ -1984,7 +2006,7 @@ object SymDenotations {
19842006
if (entry != null) {
19852007
if (entry.sym == sym) return false
19862008
mscope.unlink(entry)
1987-
if (sym.name == nme.PACKAGE) packageObjRunId = NoRunId
2009+
if (sym.name.isPackageObjectName) packageObjsRunId = NoRunId
19882010
}
19892011
true
19902012
}
@@ -2334,5 +2356,7 @@ object SymDenotations {
23342356
def baseClasses: List[ClassSymbol] = classes
23352357
}
23362358

2359+
private val packageTypeName = ModuleClassName(nme.PACKAGE).toTypeName
2360+
23372361
@sharable private[this] var indent = 0 // for completions printing
23382362
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,10 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
223223
*/
224224
def makePackageObjPrefixExplicit(tpe: NamedType): Type = {
225225
def tryInsert(pkgClass: SymDenotation): Type = pkgClass match {
226-
case pkgCls: PackageClassDenotation
227-
if !tpe.symbol.maybeOwner.is(Package) && pkgCls.packageObj.exists =>
228-
tpe.derivedSelect(pkgCls.packageObj.termRef)
226+
case pkg: PackageClassDenotation =>
227+
val pobj = pkg.packageObjFor(tpe.symbol)
228+
if (pobj.exists) tpe.derivedSelect(pobj.termRef)
229+
else tpe
229230
case _ =>
230231
tpe
231232
}

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,16 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
113113
private def transformSelect(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = {
114114
val qual = tree.qualifier
115115
qual.symbol.moduleClass.denot match {
116-
case pkg: PackageClassDenotation if !tree.symbol.maybeOwner.is(Package) =>
117-
transformSelect(cpy.Select(tree)(qual select pkg.packageObj.symbol, tree.name), targs)
116+
case pkg: PackageClassDenotation =>
117+
val pobj = pkg.packageObjFor(tree.symbol)
118+
if (pobj.exists)
119+
return transformSelect(cpy.Select(tree)(qual.select(pobj), tree.name), targs)
118120
case _ =>
119-
val tree1 = super.transform(tree)
120-
constToLiteral(tree1) match {
121-
case _: Literal => tree1
122-
case _ => superAcc.transformSelect(tree1, targs)
123-
}
121+
}
122+
val tree1 = super.transform(tree)
123+
constToLiteral(tree1) match {
124+
case _: Literal => tree1
125+
case _ => superAcc.transformSelect(tree1, targs)
124126
}
125127
}
126128

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ class Typer extends Namer
256256
// the opposite: the last context before the package changes. This distinction
257257
// is made so that top-level imports following a package clause are
258258
// logically nested in that package clause. Finally, for the root package
259-
// we switch back to the original test. This means that rop-level packages in
259+
// we switch back to the original test. This means that top-level packages in
260260
// the root package take priority over root imports. For instance,
261261
// a top-level io package takes priority over scala.io.
262262
// It would be nice if we could drop all this complication, and

tests/run/toplevel-defs.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
hello, Bill
2+
hello, Bob

tests/run/toplevel-defs/Test_2.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object Test extends App {
2+
println(hello("Bill"))
3+
println(O.hi)
4+
}

tests/run/toplevel-defs/defs.scala

Lines changed: 0 additions & 1 deletion
This file was deleted.

tests/run/toplevel-defs/defs_1.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def hello(name: String) = s"hello, $name"
2+
3+
object O {
4+
def hi = hello("Bob")
5+
}

tests/run/toplevel-overloads.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
hello, Bill
2+
3
3+
true
4+
hello, Bob
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import top._
2+
object Test extends App {
3+
println(hello("Bill"))
4+
println(hello(3))
5+
println(hello(true))
6+
println(O.hi)
7+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package top
2+
3+
def hello(name: String) = s"hello, $name"
4+
def hello(x: Int) = x.toString
5+
6+
object O {
7+
def hi = hello("Bob")
8+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package top
2+
3+
def hello(b: Boolean) = b.toString

0 commit comments

Comments
 (0)