Skip to content

Commit a92a209

Browse files
committed
Make isAbsent force less
After the previous commits, only completers for ModuleCompleter and SymbolLoader actually need to be completed to avoid isAbsent returning the wrong result. A version if `isAbsent` that doesn't force at all is still necessary, but instead of having a separate `unforcedIsAbsent` method we roll its functionality into `isAbsent` which gains a `canForce` parameter
1 parent 5c37a38 commit a92a209

File tree

8 files changed

+54
-27
lines changed

8 files changed

+54
-27
lines changed

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

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,21 @@ object SymDenotations {
353353
/** The completer of this denotation. @pre: Denotation is not yet completed */
354354
final def completer: LazyType = myInfo.asInstanceOf[LazyType]
355355

356-
/** Make sure this denotation is completed */
356+
/** If this denotation is not completed, run the completer.
357+
* The resulting info might be another completer.
358+
*
359+
* @see ensureCompleted
360+
*/
361+
final def completeOnce()(implicit ctx: Context): Unit = myInfo match {
362+
case myInfo: LazyType =>
363+
completeFrom(myInfo)
364+
case _ =>
365+
}
366+
367+
/** Make sure this denotation is fully completed.
368+
*
369+
* @see completeOnce
370+
*/
357371
final def ensureCompleted()(implicit ctx: Context): Unit = info
358372

359373
/** The symbols defined in this class or object.
@@ -370,7 +384,7 @@ object SymDenotations {
370384
case cinfo: LazyType =>
371385
val knownDecls = cinfo.decls
372386
if (knownDecls ne EmptyScope) knownDecls
373-
else { completeFrom(cinfo); unforcedDecls } // complete-once
387+
else { completeOnce(); unforcedDecls }
374388
case _ => info.decls
375389
}
376390

@@ -505,20 +519,33 @@ object SymDenotations {
505519
/** is this symbol the result of an erroneous definition? */
506520
def isError: Boolean = false
507521

508-
/** Make denotation not exist */
509-
final def markAbsent(): Unit =
522+
/** Make denotation not exist.
523+
* @pre `isCompleting` is false, or this is a ModuleCompleter or SymbolLoader
524+
*/
525+
final def markAbsent()(implicit ctx: Context): Unit = {
526+
if (isCompleting)
527+
assert(myInfo.isInstanceOf[ModuleCompleter | SymbolLoader],
528+
s"Illegal call to `markAbsent()` while completing $this using completer $myInfo")
510529
myInfo = NoType
530+
}
511531

512-
/** Is symbol known to not exist, or potentially not completed yet? */
513-
final def unforcedIsAbsent(implicit ctx: Context): Boolean =
514-
myInfo == NoType ||
515-
(this.is(ModuleVal, butNot = Package)) && moduleClass.unforcedIsAbsent
516-
517-
/** Is symbol known to not exist? */
518-
final def isAbsent(implicit ctx: Context): Boolean = {
519-
ensureCompleted()
520-
(myInfo `eq` NoType) ||
521-
(this.is(ModuleVal, butNot = Package)) && moduleClass.isAbsent
532+
/** Is symbol known to not exist?
533+
* @param canForce If this is true, the info may be forced to avoid a false-negative result
534+
*/
535+
@tailrec
536+
final def isAbsent(canForce: Boolean = true)(implicit ctx: Context): Boolean = myInfo match {
537+
case myInfo: ModuleCompleter =>
538+
// Instead of completing the ModuleCompleter, we can check whether
539+
// the module class is absent, which might require less completions.
540+
myInfo.moduleClass.isAbsent(canForce)
541+
case _: SymbolLoader if canForce =>
542+
// Completing a SymbolLoader might call `markAbsent()`
543+
completeOnce()
544+
isAbsent(canForce)
545+
case _ =>
546+
// Otherwise, no completion is necessary, see the preconditions of `markAbsent()`.
547+
(myInfo `eq` NoType) ||
548+
is(ModuleVal, butNot = Package) && moduleClass.isAbsent(canForce)
522549
}
523550

524551
/** Is this symbol the root class or its companion object? */
@@ -639,7 +666,7 @@ object SymDenotations {
639666
final def isCoDefinedWith(other: Symbol)(implicit ctx: Context): Boolean =
640667
(this.effectiveOwner == other.effectiveOwner) &&
641668
( !this.effectiveOwner.is(PackageClass)
642-
|| this.unforcedIsAbsent || other.unforcedIsAbsent
669+
|| this.isAbsent(canForce = false) || other.isAbsent(canForce = false)
643670
|| { // check if they are defined in the same file(or a jar)
644671
val thisFile = this.symbol.associatedFile
645672
val thatFile = other.associatedFile
@@ -792,7 +819,7 @@ object SymDenotations {
792819
}
793820

794821
if (pre eq NoPrefix) true
795-
else if (isAbsent) false
822+
else if (isAbsent()) false
796823
else {
797824
val boundary = accessBoundary(owner)
798825

@@ -1611,7 +1638,7 @@ object SymDenotations {
16111638
}
16121639

16131640
final override def derivesFrom(base: Symbol)(implicit ctx: Context): Boolean =
1614-
!isAbsent &&
1641+
!isAbsent() &&
16151642
base.isClass &&
16161643
( (symbol eq base)
16171644
|| (baseClassSet contains base)
@@ -1990,7 +2017,7 @@ object SymDenotations {
19902017

19912018
/** Register companion class */
19922019
override def registerCompanion(companion: Symbol)(implicit ctx: Context) =
1993-
if (companion.isClass && !unforcedIsAbsent && !companion.unforcedIsAbsent)
2020+
if (companion.isClass && !isAbsent(canForce = false) && !companion.isAbsent(canForce = false))
19942021
myCompanion = companion
19952022

19962023
override def registeredCompanion(implicit ctx: Context) = { ensureCompleted(); myCompanion }
@@ -2072,7 +2099,7 @@ object SymDenotations {
20722099
}
20732100
case nil =>
20742101
val directMembers = super.computeNPMembersNamed(name)
2075-
if (acc.exists) acc.union(directMembers.filterWithPredicate(!_.symbol.isAbsent))
2102+
if (acc.exists) acc.union(directMembers.filterWithPredicate(!_.symbol.isAbsent()))
20762103
else directMembers
20772104
}
20782105
if (symbol `eq` defn.ScalaPackageClass) {

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,7 @@ class ClassfileParser(
932932
staticScope.lookup(name)
933933
else {
934934
var module = sym.companionModule
935-
if (!module.exists && sym.isAbsent)
935+
if (!module.exists && sym.isAbsent())
936936
module = sym.scalacLinkedClass
937937
module.info.member(name).symbol
938938
}

compiler/src/dotty/tools/dotc/interactive/Completion.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ object Completion {
251251
*/
252252
private def include(sym: Symbol, nameInScope: Name)(implicit ctx: Context): Boolean =
253253
nameInScope.startsWith(prefix) &&
254-
!sym.isAbsent &&
254+
!sym.isAbsent() &&
255255
!sym.isPrimaryConstructor &&
256256
sym.sourceSymbol.exists &&
257257
(!sym.is(Package) || sym.is(ModuleClass)) &&

compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,8 @@ private class ExtractDependenciesCollector extends tpd.TreeTraverser { thisTreeT
326326

327327
private def ignoreDependency(sym: Symbol)(implicit ctx: Context) =
328328
!sym.exists ||
329-
sym.unforcedIsAbsent || // ignore dependencies that have a symbol but do not exist.
330-
// e.g. java.lang.Object companion object
329+
sym.isAbsent(canForce = false) || // ignore dependencies that have a symbol but do not exist.
330+
// e.g. java.lang.Object companion object
331331
sym.isEffectiveRoot ||
332332
sym.isAnonymousFunction ||
333333
sym.isAnonymousClass

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class Pickler extends Phase {
4141
/** Drop any elements of this list that are linked module classes of other elements in the list */
4242
private def dropCompanionModuleClasses(clss: List[ClassSymbol])(implicit ctx: Context): List[ClassSymbol] = {
4343
val companionModuleClasses =
44-
clss.filterNot(_.is(Module)).map(_.linkedClass).filterNot(_.unforcedIsAbsent)
44+
clss.filterNot(_.is(Module)).map(_.linkedClass).filterNot(_.isAbsent())
4545
clss.filterNot(companionModuleClasses.contains)
4646
}
4747

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class TreeChecker extends Phase with SymTransformer {
6868
def transformSym(symd: SymDenotation)(implicit ctx: Context): SymDenotation = {
6969
val sym = symd.symbol
7070

71-
if (sym.isClass && !sym.isAbsent) {
71+
if (sym.isClass && !sym.isAbsent()) {
7272
val validSuperclass = sym.isPrimitiveValueClass || defn.syntheticCoreClasses.contains(sym) ||
7373
(sym eq defn.ObjectClass) || sym.isOneOf(NoSuperClassFlags) || (sym.asClass.superClass.exists) ||
7474
sym.isRefinementClass

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@ trait Checking {
860860
typr.println(i"check no double declarations $cls")
861861

862862
def checkDecl(decl: Symbol): Unit = {
863-
for (other <- seen(decl.name) if (!decl.isAbsent && !other.isAbsent)) {
863+
for (other <- seen(decl.name) if (!decl.isAbsent() && !other.isAbsent())) {
864864
typr.println(i"conflict? $decl $other")
865865
def javaFieldMethodPair =
866866
decl.is(JavaDefined) && other.is(JavaDefined) &&

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ trait TypeAssigner {
170170
final def reallyExists(denot: Denotation)(implicit ctx: Context): Boolean = try
171171
denot match {
172172
case denot: SymDenotation =>
173-
denot.exists && !denot.isAbsent
173+
denot.exists && !denot.isAbsent()
174174
case denot: SingleDenotation =>
175175
val sym = denot.symbol
176176
(sym eq NoSymbol) || reallyExists(sym.denot)

0 commit comments

Comments
 (0)