From 06eee798f581ffa21dc4a69974315a7c2bc7abe1 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 31 Jan 2017 10:59:59 -0700 Subject: Refactor implementation of lookupCompanion - Check for module class up front to use sourceModule - Consolidate most of the logic in Contexts --- .../scala/tools/nsc/typechecker/Contexts.scala | 40 +++++++++++++--------- .../scala/tools/nsc/typechecker/Namers.scala | 9 +++-- src/reflect/scala/reflect/internal/Scopes.scala | 33 ++++++++---------- 3 files changed, 43 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index d349597b14..7a71561978 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1193,27 +1193,33 @@ trait Contexts { self: Analyzer => res } - final def lookupCompanionOf(original: Symbol): Symbol = { - if (original.isModuleClass) original.sourceModule - else lookupScopeEntry(original) match { - case null => NoSymbol - case entry => entry.owner.lookupCompanion(original) + final def lookupCompanionInIncompleteOwner(original: Symbol): Symbol = { + /* Search scopes in current and enclosing contexts for the definition of `symbol` */ + def lookupScopeEntry(symbol: Symbol): ScopeEntry = { + var res: ScopeEntry = null + var ctx = this + while (res == null && ctx.outer != ctx) { + val s = ctx.scope lookupSymbolEntry symbol + if (s != null) + res = s + else + ctx = ctx.outer + } + res } - } - /** Search scopes in current and enclosing contexts for the definition of `symbol` */ - private def lookupScopeEntry(symbol: Symbol): ScopeEntry = { - var res: ScopeEntry = null - var ctx = this - while (res == null && ctx.outer != ctx) { - val s = ctx.scope lookupSymbolEntry symbol - if (s != null) - res = s - else - ctx = ctx.outer + // 1) Must be owned by the same Scope, to ensure that in + // `{ class C; { ...; object C } }`, the class is not seen as a companion of the object. + // 2) Must be a class and module symbol, so that `{ class C; def C }` or `{ type T; object T }` are not companions. + lookupScopeEntry(original) match { + case null => NoSymbol + case entry => + def isCompanion(sym: Symbol): Boolean = + (original.isModule && sym.isClass || sym.isModule && original.isClass) && sym.isCoDefinedWith(original) + entry.owner.lookupNameInSameScopeAs(original, original.name.companionName).filter(isCompanion) } - res } + } //class Context /** A `Context` focussed on an `Import` tree */ diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 395bda234b..28169c9da1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1955,9 +1955,12 @@ trait Namers extends MethodSynthesis { // Doing this generally would trigger cycles; that's what we also // use the lower-level scan through the current Context as a fall back. if (!currentRun.compiles(owner)) owner.initialize - original.companionSymbol orElse { - ctx.lookupCompanionOf(original) - } + + if (original.isModuleClass) original.sourceModule + else if (!owner.isTerm && owner.hasCompleteInfo) + original.companionSymbol + else + ctx.lookupCompanionInIncompleteOwner(original) } /** A version of `Symbol#linkedClassOfClass` that works with local companions, ala `companionSymbolOf`. */ diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index 51fb31d36d..0435a2c1cf 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -291,25 +291,6 @@ trait Scopes extends api.Scopes { self: SymbolTable => null } - final def lookupCompanion(original: Symbol): Symbol = { - lookupSymbolEntry(original) match { - case null => - case entry => - var e = lookupEntry(original.name.companionName) - while (e != null) { - // 1) Must be owned by the same Scope, to ensure that in - // `{ class C; { ...; object C } }`, the class is not seen as a companion of the object. - // 2) Must be a class and module symbol, so that `{ class C; def C }` or `{ type T; object T }` are not companions. - def isClassAndModule(sym1: Symbol, sym2: Symbol) = sym1.isClass && sym2.isModule - if ((e.owner eq entry.owner) && (isClassAndModule(original, e.sym) || isClassAndModule(e.sym, original))) { - return if (e.sym.isCoDefinedWith(original)) e.sym else NoSymbol - } - e = lookupNextEntry(e) - } - } - NoSymbol - } - /** lookup a symbol entry matching given name. * @note from Martin: I believe this is a hotspot or will be one * in future versions of the type system. I have reverted the previous @@ -345,6 +326,20 @@ trait Scopes extends api.Scopes { self: SymbolTable => e } + final def lookupNameInSameScopeAs(original: Symbol, companionName: Name): Symbol = { + lookupSymbolEntry(original) match { + case null => + case entry => + var e = lookupEntry(companionName) + while (e != null) { + if (e.owner eq entry.owner) return e.sym + e = lookupNextEntry(e) + } + } + NoSymbol + } + + /** TODO - we can test this more efficiently than checking isSubScope * in both directions. However the size test might be enough to quickly * rule out most failures. -- cgit v1.2.3