@@ -16,6 +16,7 @@ import annotation.tailrec
16
16
import util .SimpleIdentityMap
17
17
import util .Stats
18
18
import java .util .WeakHashMap
19
+ import scala .util .control .NonFatal
19
20
import config .Config
20
21
import reporting .diagnostic .Message
21
22
import reporting .diagnostic .messages .BadSymbolicReference
@@ -625,6 +626,10 @@ object SymDenotations {
625
626
def isPackageObject (implicit ctx : Context ): Boolean =
626
627
name.isPackageObjectName && owner.is(Package ) && this .is(Module )
627
628
629
+ /** Is this symbol a toplevel definition in a package object? */
630
+ def isWrappedToplevelDef (given Context ): Boolean =
631
+ ! isConstructor && owner.isPackageObject
632
+
628
633
/** Is this symbol an abstract type? */
629
634
final def isAbstractType (implicit ctx : Context ): Boolean = this .is(DeferredType )
630
635
@@ -1527,6 +1532,14 @@ object SymDenotations {
1527
1532
myBaseTypeCachePeriod = Nowhere
1528
1533
}
1529
1534
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
+
1530
1543
override def copyCaches (from : SymDenotation , phase : Phase )(implicit ctx : Context ): this .type = {
1531
1544
from match {
1532
1545
case from : ClassDenotation =>
@@ -1726,11 +1739,9 @@ object SymDenotations {
1726
1739
}
1727
1740
1728
1741
/** 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 =
1730
1743
scope.enter(sym)
1731
- if (myMemberCache != null ) myMemberCache.invalidate(sym.name)
1732
- if (! sym.flagsUNSAFE.is(Private )) invalidateMemberNamesCache()
1733
- }
1744
+ invalidateMemberCaches(sym)
1734
1745
1735
1746
/** Replace symbol `prev` (if defined in current class) by symbol `replacement`.
1736
1747
* If `prev` is not defined in current class, do nothing.
@@ -2071,6 +2082,7 @@ object SymDenotations {
2071
2082
2072
2083
private var packageObjsCache : List [ClassDenotation ] = _
2073
2084
private var packageObjsRunId : RunId = NoRunId
2085
+ private var ambiguityWarningIssued : Boolean = false
2074
2086
2075
2087
/** The package objects in this class */
2076
2088
def packageObjs (implicit ctx : Context ): List [ClassDenotation ] = {
@@ -2122,19 +2134,61 @@ object SymDenotations {
2122
2134
case pcls :: pobjs1 =>
2123
2135
if (pcls.isCompleting) recur(pobjs1, acc)
2124
2136
else {
2125
- // A package object inherits members from `Any` and `Object` which
2126
- // should not be accessible from the package prefix.
2127
2137
val pmembers = pcls.computeNPMembersNamed(name).filterWithPredicate { d =>
2138
+ // Drop members of `Any` and `Object`
2128
2139
val owner = d.symbol.maybeOwner
2129
2140
(owner ne defn.AnyClass ) && (owner ne defn.ObjectClass )
2130
2141
}
2131
2142
recur(pobjs1, acc.union(pmembers))
2132
2143
}
2133
2144
case nil =>
2134
2145
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
2137
2150
}
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
+
2138
2192
if (symbol `eq` defn.ScalaPackageClass ) {
2139
2193
val denots = super .computeNPMembersNamed(name)
2140
2194
if (denots.exists) denots
@@ -2154,17 +2208,17 @@ object SymDenotations {
2154
2208
recur(packageObjs, super .memberNames(keepOnly))
2155
2209
}
2156
2210
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.
2159
2213
* @return `sym` is not already entered
2160
2214
*/
2161
2215
override def proceedWithEnter (sym : Symbol , mscope : MutableScope )(implicit ctx : Context ): Boolean = {
2162
2216
val entry = mscope.lookupEntry(sym.name)
2163
2217
if (entry != null ) {
2164
2218
if (entry.sym == sym) return false
2165
2219
mscope.unlink(entry)
2166
- if (sym.name.isPackageObjectName) packageObjsRunId = NoRunId
2167
2220
}
2221
+ if (sym.name.isPackageObjectName) packageObjsRunId = NoRunId
2168
2222
true
2169
2223
}
2170
2224
0 commit comments