@@ -16,6 +16,7 @@ import annotation.tailrec
1616import util .SimpleIdentityMap
1717import util .Stats
1818import java .util .WeakHashMap
19+ import scala .util .control .NonFatal
1920import config .Config
2021import reporting .diagnostic .Message
2122import reporting .diagnostic .messages .BadSymbolicReference
@@ -625,6 +626,10 @@ object SymDenotations {
625626 def isPackageObject (implicit ctx : Context ): Boolean =
626627 name.isPackageObjectName && owner.is(Package ) && this .is(Module )
627628
629+ /** Is this symbol a toplevel definition in a package object? */
630+ def isWrappedToplevelDef (given Context ): Boolean =
631+ ! isConstructor && owner.isPackageObject
632+
628633 /** Is this symbol an abstract type? */
629634 final def isAbstractType (implicit ctx : Context ): Boolean = this .is(DeferredType )
630635
@@ -1527,6 +1532,14 @@ object SymDenotations {
15271532 myBaseTypeCachePeriod = Nowhere
15281533 }
15291534
1535+ def invalidateMemberCaches (sym : Symbol )(given Context ): Unit =
1536+ if myMemberCache != null then myMemberCache.invalidate(sym.name)
1537+ if ! sym.flagsUNSAFE.is(Private ) then
1538+ invalidateMemberNamesCache()
1539+ if sym.isWrappedToplevelDef then
1540+ val outerCache = sym.owner.owner.asClass.classDenot.myMemberCache
1541+ if outerCache != null then outerCache.invalidate(sym.name)
1542+
15301543 override def copyCaches (from : SymDenotation , phase : Phase )(implicit ctx : Context ): this .type = {
15311544 from match {
15321545 case from : ClassDenotation =>
@@ -1726,11 +1739,9 @@ object SymDenotations {
17261739 }
17271740
17281741 /** Enter a symbol in given `scope` without potentially replacing the old copy. */
1729- def enterNoReplace (sym : Symbol , scope : MutableScope )(implicit ctx : Context ): Unit = {
1742+ def enterNoReplace (sym : Symbol , scope : MutableScope )(given Context ): Unit =
17301743 scope.enter(sym)
1731- if (myMemberCache != null ) myMemberCache.invalidate(sym.name)
1732- if (! sym.flagsUNSAFE.is(Private )) invalidateMemberNamesCache()
1733- }
1744+ invalidateMemberCaches(sym)
17341745
17351746 /** Replace symbol `prev` (if defined in current class) by symbol `replacement`.
17361747 * If `prev` is not defined in current class, do nothing.
@@ -2071,6 +2082,7 @@ object SymDenotations {
20712082
20722083 private var packageObjsCache : List [ClassDenotation ] = _
20732084 private var packageObjsRunId : RunId = NoRunId
2085+ private var ambiguityWarningIssued : Boolean = false
20742086
20752087 /** The package objects in this class */
20762088 def packageObjs (implicit ctx : Context ): List [ClassDenotation ] = {
@@ -2122,19 +2134,61 @@ object SymDenotations {
21222134 case pcls :: pobjs1 =>
21232135 if (pcls.isCompleting) recur(pobjs1, acc)
21242136 else {
2125- // A package object inherits members from `Any` and `Object` which
2126- // should not be accessible from the package prefix.
21272137 val pmembers = pcls.computeNPMembersNamed(name).filterWithPredicate { d =>
2138+ // Drop members of `Any` and `Object`
21282139 val owner = d.symbol.maybeOwner
21292140 (owner ne defn.AnyClass ) && (owner ne defn.ObjectClass )
21302141 }
21312142 recur(pobjs1, acc.union(pmembers))
21322143 }
21332144 case nil =>
21342145 val directMembers = super .computeNPMembersNamed(name)
2135- if (acc.exists) acc.union(directMembers.filterWithPredicate(! _.symbol.isAbsent()))
2136- else directMembers
2146+ if ! acc.exists then directMembers
2147+ else acc.union(directMembers.filterWithPredicate(! _.symbol.isAbsent())) match
2148+ case d : DenotUnion => dropStale(d)
2149+ case d => d
21372150 }
2151+
2152+ def dropStale (multi : DenotUnion ): PreDenotation =
2153+ val compiledNow = multi.filterWithPredicate(d =>
2154+ d.symbol.isDefinedInCurrentRun || d.symbol.associatedFile == null
2155+ // if a symbol does not have an associated file, assume it is defined
2156+ // in the current run anyway. This is true for packages, and also can happen for pickling and
2157+ // from-tasty tests that generate a fresh symbol and then re-use it in the next run.
2158+ )
2159+ if compiledNow.exists then compiledNow
2160+ else
2161+ val assocFiles = multi.aggregate(d => Set (d.symbol.associatedFile), _ union _)
2162+ if assocFiles.size == 1 then
2163+ multi // they are all overloaded variants from the same file
2164+ else
2165+ // pick the variant(s) from the youngest class file
2166+ val lastModDate = assocFiles.map(_.lastModified).max
2167+ val youngest = assocFiles.filter(_.lastModified == lastModDate)
2168+ val chosen = youngest.head
2169+ def ambiguousFilesMsg (f : AbstractFile ) =
2170+ em """ Toplevel definition $name is defined in
2171+ | $chosen
2172+ |and also in
2173+ | $f"""
2174+ if youngest.size > 1 then
2175+ throw TypeError (i """ ${ambiguousFilesMsg(youngest.tail.head)}
2176+ |One of these files should be removed from the classpath. """ )
2177+
2178+ // Warn if one of the older files comes from a different container.
2179+ // In that case picking the youngest file is not necessarily what we want,
2180+ // since the older file might have been loaded from a jar earlier in the
2181+ // classpath.
2182+ def sameContainer (f : AbstractFile ): Boolean =
2183+ try f.container == chosen.container catch case NonFatal (ex) => true
2184+ if ! ambiguityWarningIssued then
2185+ for conflicting <- assocFiles.find(! sameContainer(_)) do
2186+ ctx.warning(i """ ${ambiguousFilesMsg(conflicting)}
2187+ |Keeping only the definition in $chosen""" )
2188+ ambiguityWarningIssued = true
2189+ multi.filterWithPredicate(_.symbol.associatedFile == chosen)
2190+ end dropStale
2191+
21382192 if (symbol `eq` defn.ScalaPackageClass ) {
21392193 val denots = super .computeNPMembersNamed(name)
21402194 if (denots.exists) denots
@@ -2154,17 +2208,17 @@ object SymDenotations {
21542208 recur(packageObjs, super .memberNames(keepOnly))
21552209 }
21562210
2157- /** If another symbol with the same name is entered, unlink it,
2158- * and, if symbol is a package object, invalidate the packageObj cache.
2211+ /** If another symbol with the same name is entered, unlink it.
2212+ * If symbol is a package object, invalidate the packageObj cache.
21592213 * @return `sym` is not already entered
21602214 */
21612215 override def proceedWithEnter (sym : Symbol , mscope : MutableScope )(implicit ctx : Context ): Boolean = {
21622216 val entry = mscope.lookupEntry(sym.name)
21632217 if (entry != null ) {
21642218 if (entry.sym == sym) return false
21652219 mscope.unlink(entry)
2166- if (sym.name.isPackageObjectName) packageObjsRunId = NoRunId
21672220 }
2221+ if (sym.name.isPackageObjectName) packageObjsRunId = NoRunId
21682222 true
21692223 }
21702224
0 commit comments