Skip to content

Commit 61564ba

Browse files
committed
Treat toplevel objects as package objects
1 parent 031532d commit 61564ba

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
@@ -1680,9 +1677,9 @@ object SymDenotations {
16801677
val denots1 = collect(denots, ps)
16811678
p.classSymbol.denot match {
16821679
case parentd: ClassDenotation =>
1683-
denots1 union
1680+
denots1.union(
16841681
parentd.nonPrivateMembersNamed(name)
1685-
.mapInherited(ownDenots, denots1, thisType)
1682+
.mapInherited(ownDenots, denots1, thisType))
16861683
case _ =>
16871684
denots1
16881685
}
@@ -1940,17 +1937,39 @@ object SymDenotations {
19401937
initPrivateWithin: Symbol)
19411938
extends ClassDenotation(symbol, ownerIfExists, name, initFlags, initInfo, initPrivateWithin) {
19421939

1943-
private[this] var packageObjCache: SymDenotation = _
1944-
private[this] var packageObjRunId: RunId = NoRunId
1945-
1946-
/** The package object in this class, of one exists */
1947-
def packageObj(implicit ctx: Context): SymDenotation = {
1948-
if (packageObjRunId != ctx.runId) {
1949-
packageObjRunId = ctx.runId
1950-
packageObjCache = NoDenotation // break cycle in case we are looking for package object itself
1951-
packageObjCache = findMember(nme.PACKAGE, thisType, EmptyFlagConjunction, EmptyFlags).asSymDenotation
1940+
private[this] var packageObjsCache: List[ClassDenotation] = _
1941+
private[this] var packageObjsRunId: RunId = NoRunId
1942+
1943+
/** The package objects in this class */
1944+
def packageObjs(implicit ctx: Context): List[ClassDenotation] = {
1945+
if (packageObjsRunId != ctx.runId) {
1946+
packageObjsRunId = ctx.runId
1947+
packageObjsCache = Nil // break cycle in case we are looking for package object itself
1948+
packageObjsCache = {
1949+
val pkgObjBuf = new mutable.ListBuffer[ClassDenotation]
1950+
for (sym <- info.decls) { // don't use filter, since that loads classes with `$`s in their name
1951+
val denot = sym.lastKnownDenotation // don't use `sym.denot`, as this brings forward classes too early
1952+
if (denot.isType && denot.name.isPackageObjectName)
1953+
pkgObjBuf += sym.asClass.classDenot
1954+
}
1955+
pkgObjBuf.toList
1956+
}
19521957
}
1953-
packageObjCache
1958+
packageObjsCache
1959+
}
1960+
1961+
/** The package object (as a term symbol) in this package that might contain
1962+
* `sym` as a member.
1963+
*/
1964+
def packageObjFor(sym: Symbol)(implicit ctx: Context): Symbol = {
1965+
val owner = sym.maybeOwner
1966+
if (owner.is(Package)) NoSymbol
1967+
else if (owner.isPackageObject) owner.sourceModule
1968+
else // owner could be class inherited by package object (until package object inheritance is removed)
1969+
packageObjs.find(_.name == packageTypeName) match {
1970+
case Some(pobj) => pobj.sourceModule
1971+
case _ => NoSymbol
1972+
}
19541973
}
19551974

19561975
/** Looks in both the package object and the package for members. The precise algorithm
@@ -1966,28 +1985,31 @@ object SymDenotations {
19661985
* object that hides a class or object in the scala package of the same name, because
19671986
* the behavior would then be unintuitive for such members.
19681987
*/
1969-
override def computeNPMembersNamed(name: Name)(implicit ctx: Context): PreDenotation =
1970-
packageObj.moduleClass.denot match {
1971-
case pcls: ClassDenotation if !pcls.isCompleting =>
1972-
if (symbol eq defn.ScalaPackageClass) {
1973-
val denots = super.computeNPMembersNamed(name)
1974-
if (denots.exists) denots else pcls.computeNPMembersNamed(name)
1975-
}
1976-
else {
1977-
val denots = pcls.computeNPMembersNamed(name)
1978-
if (denots.exists) denots else super.computeNPMembersNamed(name)
1979-
}
1980-
case _ =>
1981-
super.computeNPMembersNamed(name)
1988+
override def computeNPMembersNamed(name: Name)(implicit ctx: Context): PreDenotation = {
1989+
def recur(pobjs: List[ClassDenotation], acc: PreDenotation): PreDenotation = pobjs match {
1990+
case pcls :: pobjs1 =>
1991+
if (pcls.isCompleting) recur(pobjs1, acc)
1992+
else recur(pobjs1, acc.union(pcls.computeNPMembersNamed(name)))
1993+
case nil =>
1994+
if (acc.exists) acc else super.computeNPMembersNamed(name)
19821995
}
1996+
if (symbol `eq` defn.ScalaPackageClass) {
1997+
val denots = super.computeNPMembersNamed(name)
1998+
if (denots.exists) denots
1999+
else recur(packageObjs, NoDenotation)
2000+
}
2001+
else recur(packageObjs, NoDenotation)
2002+
}
19832003

19842004
/** The union of the member names of the package and the package object */
19852005
override def memberNames(keepOnly: NameFilter)(implicit onBehalf: MemberNames, ctx: Context): Set[Name] = {
1986-
val ownNames = super.memberNames(keepOnly)
1987-
packageObj.moduleClass.denot match {
1988-
case pcls: ClassDenotation => ownNames union pcls.memberNames(keepOnly)
1989-
case _ => ownNames
2006+
def recur(pobjs: List[ClassDenotation], acc: Set[Name]): Set[Name] = pobjs match {
2007+
case pcls :: pobjs1 =>
2008+
recur(pobjs1, acc.union(pcls.memberNames(keepOnly)))
2009+
case nil =>
2010+
acc
19902011
}
2012+
recur(packageObjs, super.memberNames(keepOnly))
19912013
}
19922014

19932015
/** If another symbol with the same name is entered, unlink it,
@@ -1999,7 +2021,7 @@ object SymDenotations {
19992021
if (entry != null) {
20002022
if (entry.sym == sym) return false
20012023
mscope.unlink(entry)
2002-
if (sym.name == nme.PACKAGE) packageObjRunId = NoRunId
2024+
if (sym.name.isPackageObjectName) packageObjsRunId = NoRunId
20032025
}
20042026
true
20052027
}
@@ -2349,5 +2371,7 @@ object SymDenotations {
23492371
def baseClasses: List[ClassSymbol] = classes
23502372
}
23512373

2374+
private val packageTypeName = ModuleClassName(nme.PACKAGE).toTypeName
2375+
23522376
@sharable private[this] var indent = 0 // for completions printing
23532377
}

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
@@ -119,14 +119,16 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
119119
private def transformSelect(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = {
120120
val qual = tree.qualifier
121121
qual.symbol.moduleClass.denot match {
122-
case pkg: PackageClassDenotation if !tree.symbol.maybeOwner.is(Package) =>
123-
transformSelect(cpy.Select(tree)(qual select pkg.packageObj.symbol, tree.name), targs)
122+
case pkg: PackageClassDenotation =>
123+
val pobj = pkg.packageObjFor(tree.symbol)
124+
if (pobj.exists)
125+
return transformSelect(cpy.Select(tree)(qual.select(pobj), tree.name), targs)
124126
case _ =>
125-
val tree1 = super.transform(tree)
126-
constToLiteral(tree1) match {
127-
case _: Literal => tree1
128-
case _ => superAcc.transformSelect(tree1, targs)
129-
}
127+
}
128+
val tree1 = super.transform(tree)
129+
constToLiteral(tree1) match {
130+
case _: Literal => tree1
131+
case _ => superAcc.transformSelect(tree1, targs)
130132
}
131133
}
132134

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+
}

0 commit comments

Comments
 (0)