Skip to content

Commit cd3b1ed

Browse files
authored
Merge pull request #5754 from dotty-staging/add-toplevel
Allow toplevel definitions
2 parents 3c50954 + 537f80d commit cd3b1ed

Some content is hidden

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

65 files changed

+574
-136
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

+32
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Decorators._, transform.SymUtils._
99
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName}
1010
import typer.FrontEnd
1111
import util.{Property, SourceFile, SourcePosition}
12+
import util.NameTransformer.avoidIllegalChars
1213
import collection.mutable.ListBuffer
1314
import reporting.diagnostic.messages._
1415
import reporting.trace
@@ -1025,6 +1026,37 @@ object desugar {
10251026
else Apply(ref(tupleTypeRef.classSymbol.companionModule.termRef), ts)
10261027
}
10271028

1029+
/** Group all definitions that can't be at the toplevel in
1030+
* an object named `<source>$package` where `<source>` is the name of the source file.
1031+
* Definitions that can't be at the toplevel are:
1032+
*
1033+
* - all pattern, value and method definitions
1034+
* - non-class type definitions
1035+
* - implicit classes and objects
1036+
* - companion objects of opaque types
1037+
*/
1038+
def packageDef(pdef: PackageDef)(implicit ctx: Context): PackageDef = {
1039+
val opaqueNames = pdef.stats.collect {
1040+
case stat: TypeDef if stat.mods.is(Opaque) => stat.name
1041+
}
1042+
def needsObject(stat: Tree) = stat match {
1043+
case _: ValDef | _: PatDef | _: DefDef => true
1044+
case stat: ModuleDef =>
1045+
stat.mods.is(Implicit) || opaqueNames.contains(stat.name.stripModuleClassSuffix.toTypeName)
1046+
case stat: TypeDef => !stat.isClassDef || stat.mods.is(Implicit)
1047+
case _ => false
1048+
}
1049+
val (nestedStats, topStats) = pdef.stats.partition(needsObject)
1050+
if (nestedStats.isEmpty) pdef
1051+
else {
1052+
var fileName = ctx.source.file.name
1053+
val sourceName = fileName.take(fileName.lastIndexOf('.'))
1054+
val groupName = avoidIllegalChars((sourceName ++ str.TOPLEVEL_SUFFIX).toTermName.asSimpleName)
1055+
val grouped = ModuleDef(groupName, Template(emptyConstructor, Nil, Nil, EmptyValDef, nestedStats))
1056+
cpy.PackageDef(pdef)(pdef.pid, topStats :+ grouped)
1057+
}
1058+
}
1059+
10281060
/** Make closure corresponding to function.
10291061
* params => body
10301062
* ==>

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ object Denotations {
412412
def precedes(sym1: Symbol, sym2: Symbol) = {
413413
def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match {
414414
case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1)
415-
case Nil => true
415+
case Nil => false
416416
}
417417
(sym1 ne sym2) &&
418418
(sym1.derivesFrom(sym2) ||
@@ -1183,7 +1183,7 @@ object Denotations {
11831183
*/
11841184
def isDoubleDef(sym1: Symbol, sym2: Symbol)(implicit ctx: Context): Boolean =
11851185
(sym1.exists && sym2.exists &&
1186-
(sym1 ne sym2) && (sym1.owner eq sym2.owner) &&
1186+
(sym1 `ne` sym2) && (sym1.effectiveOwner `eq` sym2.effectiveOwner) &&
11871187
!sym1.is(Bridge) && !sym2.is(Bridge))
11881188

11891189
def doubleDefError(denot1: Denotation, denot2: Denotation, pre: Type = NoPrefix)(implicit ctx: Context): Nothing = {
@@ -1193,7 +1193,7 @@ object Denotations {
11931193
throw new MergeError(sym1, sym2, sym1.info, sym2.info, pre) {
11941194
override def addendum(implicit ctx: Context) =
11951195
i"""
1196-
|they are both defined in ${sym1.owner} but have matching signatures
1196+
|they are both defined in ${sym1.effectiveOwner} but have matching signatures
11971197
| ${denot1.info} and
11981198
| ${denot2.info}${super.addendum}"""
11991199
}
@@ -1227,7 +1227,7 @@ object Denotations {
12271227
final case class DenotUnion(denot1: PreDenotation, denot2: PreDenotation) extends MultiPreDenotation {
12281228
def exists: Boolean = true
12291229
def toDenot(pre: Type)(implicit ctx: Context): Denotation =
1230-
(denot1 toDenot pre) & (denot2 toDenot pre, pre)
1230+
denot1.toDenot(pre).&(denot2.toDenot(pre), pre)
12311231
def containsSym(sym: Symbol): Boolean =
12321232
(denot1 containsSym sym) || (denot2 containsSym sym)
12331233
type AsSeenFromResult = PreDenotation

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

+10
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ object NameOps {
8787
false
8888
}
8989

90+
/** is this the name of an object enclosing packagel-level definitions? */
91+
def isPackageObjectName: Boolean = name match {
92+
case name: TermName => name == nme.PACKAGE || name.endsWith(str.TOPLEVEL_SUFFIX)
93+
case name: TypeName =>
94+
name.toTermName match {
95+
case ModuleClassName(original) => original.isPackageObjectName
96+
case _ => false
97+
}
98+
}
99+
90100
/** Convert this module name to corresponding module class name */
91101
def moduleClassName: TypeName = name.derived(ModuleClassName).toTypeName
92102

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

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +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 = "$package"
2324
final val NAME_JOIN = "$"
2425
final val DEFAULT_GETTER = "$default$"
2526
final val LOCALDUMMY_PREFIX = "<local " // owner of local blocks

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

+65-41
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
@@ -762,9 +759,7 @@ object SymDenotations {
762759
( !(this is Local)
763760
|| (owner is ImplClass) // allow private local accesses to impl class members
764761
|| isCorrectThisType(pre)
765-
) &&
766-
(!(this.is(Private) && owner.is(Package)) ||
767-
owner == ctx.owner.enclosingPackageClass)
762+
)
768763
|| (this is Protected) &&
769764
( superAccess
770765
|| pre.isInstanceOf[ThisType]
@@ -1684,9 +1679,9 @@ object SymDenotations {
16841679
val denots1 = collect(denots, ps)
16851680
p.classSymbol.denot match {
16861681
case parentd: ClassDenotation =>
1687-
denots1 union
1682+
denots1.union(
16881683
parentd.nonPrivateMembersNamed(name)
1689-
.mapInherited(ownDenots, denots1, thisType)
1684+
.mapInherited(ownDenots, denots1, thisType))
16901685
case _ =>
16911686
denots1
16921687
}
@@ -1944,54 +1939,81 @@ object SymDenotations {
19441939
initPrivateWithin: Symbol)
19451940
extends ClassDenotation(symbol, ownerIfExists, name, initFlags, initInfo, initPrivateWithin) {
19461941

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

19601977
/** Looks in both the package object and the package for members. The precise algorithm
19611978
* is as follows:
19621979
*
19631980
* If this is the scala package look in the package first, and if nothing is found
1964-
* there, look in the package object second. Otherwise, look in the package object
1965-
* first, and if nothing is found there, in the package second.
1981+
* there, look in the package object second. Otherwise, look in the both the package object
1982+
* and the package and form a union of the results.
19661983
*
19671984
* The reason for the special treatment of the scala package is that if we
19681985
* complete it too early, we freeze its superclass Any, so that no members can
19691986
* be entered in it. As a consequence, there should be no entry in the scala package
19701987
* object that hides a class or object in the scala package of the same name, because
19711988
* the behavior would then be unintuitive for such members.
19721989
*/
1973-
override def computeNPMembersNamed(name: Name)(implicit ctx: Context): PreDenotation =
1974-
packageObj.moduleClass.denot match {
1975-
case pcls: ClassDenotation if !pcls.isCompleting =>
1976-
if (symbol eq defn.ScalaPackageClass) {
1977-
val denots = super.computeNPMembersNamed(name)
1978-
if (denots.exists) denots else pcls.computeNPMembersNamed(name)
1979-
}
1980-
else {
1981-
val denots = pcls.computeNPMembersNamed(name)
1982-
if (denots.exists) denots else super.computeNPMembersNamed(name)
1983-
}
1984-
case _ =>
1985-
super.computeNPMembersNamed(name)
1990+
override def computeNPMembersNamed(name: Name)(implicit ctx: Context): PreDenotation = {
1991+
def recur(pobjs: List[ClassDenotation], acc: PreDenotation): PreDenotation = pobjs match {
1992+
case pcls :: pobjs1 =>
1993+
if (pcls.isCompleting) recur(pobjs1, acc)
1994+
else recur(pobjs1, acc.union(pcls.computeNPMembersNamed(name)))
1995+
case nil =>
1996+
val directMembers = super.computeNPMembersNamed(name)
1997+
if (acc.exists) acc.union(directMembers.filterWithPredicate(!_.symbol.isAbsent))
1998+
else directMembers
19861999
}
2000+
if (symbol `eq` defn.ScalaPackageClass) {
2001+
val denots = super.computeNPMembersNamed(name)
2002+
if (denots.exists) denots
2003+
else recur(packageObjs, NoDenotation)
2004+
}
2005+
else recur(packageObjs, NoDenotation)
2006+
}
19872007

19882008
/** The union of the member names of the package and the package object */
19892009
override def memberNames(keepOnly: NameFilter)(implicit onBehalf: MemberNames, ctx: Context): Set[Name] = {
1990-
val ownNames = super.memberNames(keepOnly)
1991-
packageObj.moduleClass.denot match {
1992-
case pcls: ClassDenotation => ownNames union pcls.memberNames(keepOnly)
1993-
case _ => ownNames
2010+
def recur(pobjs: List[ClassDenotation], acc: Set[Name]): Set[Name] = pobjs match {
2011+
case pcls :: pobjs1 =>
2012+
recur(pobjs1, acc.union(pcls.memberNames(keepOnly)))
2013+
case nil =>
2014+
acc
19942015
}
2016+
recur(packageObjs, super.memberNames(keepOnly))
19952017
}
19962018

19972019
/** If another symbol with the same name is entered, unlink it,
@@ -2003,7 +2025,7 @@ object SymDenotations {
20032025
if (entry != null) {
20042026
if (entry.sym == sym) return false
20052027
mscope.unlink(entry)
2006-
if (sym.name == nme.PACKAGE) packageObjRunId = NoRunId
2028+
if (sym.name.isPackageObjectName) packageObjsRunId = NoRunId
20072029
}
20082030
true
20092031
}
@@ -2353,5 +2375,7 @@ object SymDenotations {
23532375
def baseClasses: List[ClassSymbol] = classes
23542376
}
23552377

2378+
private val packageTypeName = ModuleClassName(nme.PACKAGE).toTypeName
2379+
23562380
@sharable private[this] var indent = 0 // for completions printing
23572381
}

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

+14-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import dotty.tools.io.{ ClassPath, ClassRepresentation, AbstractFile }
88
import config.Config
99
import Contexts._, Symbols._, Flags._, SymDenotations._, Types._, Scopes._, Names._
1010
import NameOps._
11+
import StdNames.str
1112
import Decorators.{PreNamedString, StringInterpolators}
1213
import classfile.ClassfileParser
1314
import util.Stats
@@ -236,11 +237,18 @@ object SymbolLoaders {
236237

237238
private[core] val currentDecls: MutableScope = new PackageScope()
238239

239-
def isFlatName(name: SimpleName): Boolean = name.lastIndexOf('$', name.length - 2) >= 0
240+
private def isFlatName(name: SimpleName): Boolean = {
241+
val idx = name.lastIndexOf('$', name.length - 2)
242+
idx >= 0 &&
243+
(idx + str.TOPLEVEL_SUFFIX.length + 1 != name.length || !name.endsWith(str.TOPLEVEL_SUFFIX))
244+
}
240245

241-
def isFlatName(classRep: ClassRepresentation): Boolean = {
242-
val idx = classRep.name.indexOf('$')
243-
idx >= 0 && idx < classRep.name.length - 1
246+
/** Name of class contains `$`, excepted names ending in `$package` */
247+
def hasFlatName(classRep: ClassRepresentation): Boolean = {
248+
val name = classRep.name
249+
val idx = name.lastIndexOf('$', name.length - 2)
250+
idx >= 0 &&
251+
(idx + str.TOPLEVEL_SUFFIX.length + 1 != name.length || !name.endsWith(str.TOPLEVEL_SUFFIX))
244252
}
245253

246254
def maybeModuleClass(classRep: ClassRepresentation): Boolean = classRep.name.last == '$'
@@ -253,11 +261,11 @@ object SymbolLoaders {
253261
val classReps = classPath.list(packageName).classesAndSources
254262

255263
for (classRep <- classReps)
256-
if (!maybeModuleClass(classRep) && isFlatName(classRep) == flat &&
264+
if (!maybeModuleClass(classRep) && hasFlatName(classRep) == flat &&
257265
(!flat || isAbsent(classRep))) // on 2nd enter of flat names, check that the name has not been entered before
258266
initializeFromClassPath(root.symbol, classRep)
259267
for (classRep <- classReps)
260-
if (maybeModuleClass(classRep) && isFlatName(classRep) == flat &&
268+
if (maybeModuleClass(classRep) && hasFlatName(classRep) == flat &&
261269
isAbsent(classRep))
262270
initializeFromClassPath(root.symbol, classRep)
263271
}

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

+9-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,15 @@ class MergeError(val sym1: Symbol, val sym2: Symbol, val tp1: Type, val tp2: Typ
170170
}
171171

172172
protected def addendum(implicit ctx: Context): String =
173-
if (prefix `eq` NoPrefix) "" else i"\nas members of type $prefix"
173+
if (prefix `eq` NoPrefix) ""
174+
else {
175+
val owner = prefix match {
176+
case prefix: ThisType => prefix.cls.show
177+
case prefix: TermRef => prefix.symbol.show
178+
case _ => i"type $prefix"
179+
}
180+
s"\nas members of $owner"
181+
}
174182

175183
override def toMessage(implicit ctx: Context): Message = {
176184
if (ctx.debug) printStackTrace()

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

+4-3
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/core/tasty/TreeUnpickler.scala

+14-7
Original file line numberDiff line numberDiff line change
@@ -1013,11 +1013,20 @@ class TreeUnpickler(reader: TastyReader,
10131013
}
10141014
}
10151015

1016-
def completeSelect(name: Name, tpf: Type => NamedType): Select = {
1016+
def completeSelect(name: Name, sig: Signature): Select = {
10171017
val localCtx =
10181018
if (name == nme.CONSTRUCTOR) ctx.addMode(Mode.InSuperCall) else ctx
10191019
val qual = readTerm()(localCtx)
1020-
ConstFold(untpd.Select(qual, name).withType(tpf(qual.tpe.widenIfUnstable)))
1020+
var pre = qual.tpe.widenIfUnstable
1021+
val denot = accessibleDenot(pre, name, sig)
1022+
val owner = denot.symbol.maybeOwner
1023+
if (owner.isPackageObject && pre.termSymbol.is(Package))
1024+
pre = pre.select(owner.sourceModule)
1025+
val tpe = name match {
1026+
case name: TypeName => TypeRef(pre, name, denot)
1027+
case name: TermName => TermRef(pre, name, denot)
1028+
}
1029+
ConstFold(untpd.Select(qual, name).withType(tpe))
10211030
}
10221031

10231032
def readQualId(): (untpd.Ident, TypeRef) = {
@@ -1039,15 +1048,13 @@ class TreeUnpickler(reader: TastyReader,
10391048
case IDENTtpt =>
10401049
untpd.Ident(readName().toTypeName).withType(readType())
10411050
case SELECT =>
1042-
def readRest(name: TermName, sig: Signature): Tree =
1043-
completeSelect(name, pre => TermRef(pre, name, accessibleDenot(pre, name, sig)))
10441051
readName() match {
1045-
case SignedName(name, sig) => readRest(name, sig)
1046-
case name => readRest(name, Signature.NotAMethod)
1052+
case SignedName(name, sig) => completeSelect(name, sig)
1053+
case name => completeSelect(name, Signature.NotAMethod)
10471054
}
10481055
case SELECTtpt =>
10491056
val name = readName().toTypeName
1050-
completeSelect(name, pre => TypeRef(pre, name, accessibleDenot(pre, name, Signature.NotAMethod)))
1057+
completeSelect(name, Signature.NotAMethod)
10511058
case QUALTHIS =>
10521059
val (qual, tref) = readQualId()
10531060
untpd.This(qual).withType(ThisType.raw(tref))

0 commit comments

Comments
 (0)