8000 Unify scope lookup for companions and default getters · retronym/scala@da14e9c · GitHub
[go: up one dir, main page]

Skip to content

Commit da14e9c

Browse files
committed
Unify scope lookup for companions and default getters
In scala#5700, I fixed a bug in the companion lookup, which ensured they were defined in the same scope. The same approach applies well to the lookup of default getters. You may ask, we can't just use: ``` context.lookupSymbol(name, _.owner == expectedOwner) ``` That doesn't individually lookup the entry in each enclosing nested scopes, but rather relies on the outer scope delegation in `Scope.lookupEntry` itself. This in turn relies on the way that nested scopes share the `elems` table with the enclosing scope: ``` final def newNestedScope(outer: Scope): Scope = { val nested = newScope nested.elems = outer.elems nested.nestinglevel = outer.nestinglevel + 1 ... } ``` If the outer scope is later mutated, in our case by lazily adding the default getter, the inner scope won't see the new elems. Context.lookupSymbol will jump immediately jump to search of the enclosing prefix. Perhaps a better design would be for the inner scope to retain a reference to the outer one, rather than just to the head of its elems linked list at the time the nested scope was created.
1 parent 86f2028 commit da14e9c

File tree

3 files changed

+18
-36
lines changed

3 files changed

+18
-36
lines changed

src/compiler/scala/tools/nsc/typechecker/Contexts.scala

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,8 +1065,11 @@ trait Contexts { self: Analyzer =>
10651065
found1
10661066
}
10671067

1068-
def lookupInScope(scope: Scope) =
1069-
(scope lookupUnshadowedEntries name filter (e => qualifies(e.sym))).toList
1068+
def lookupInScope(scope: Scope) = {
1069+
val entries = scope lookupUnshadowedEntries name
1070+
val result = entries.filter(e => qualifies(e.sym)).toList
1071+
result
1072+
}
10701073

10711074
def newOverloaded(owner: Symbol, pre: Type, entries: List[ScopeEntry]) =
10721075
logResult(s"overloaded symbol in $pre")(owner.newOverloaded(pre, entries map (_.sym)))
@@ -1198,32 +1201,14 @@ trait Contexts { self: Analyzer =>
11981201
else finish(EmptyTree, NoSymbol)
11991202
}
12001203

1201-
/**
1202-
* Find a symbol in this context or one of its outers.
1203-
*
1204-
* Used to find symbols are owned by methods (or fields), they can't be
1205-
* found in some scope.
1206-
*
1207-
* Examples: companion module of classes owned by a method, default getter
1208-
* methods of nested methods. See NamesDefaults.scala
1209-
*/
1210-
def lookup(name: Name, expectedOwner: Symbol) = {
1211-
var res: Symbol = NoSymbol
1212-
var ctx = this
1213-
while (res == NoSymbol && ctx.outer != ctx) {
1214-
ctx.scope.lookupUnshadowedEntries(name).filter(s => s.sym != NoSymbol && s.sym.owner == expectedOwner).toList match {
1215-
case Nil =>
1216-
ctx = ctx.outer
1217-
case found :: Nil =>
1218-
res = found.sym
1219-
case alts =>
1220-
res = expectedOwner.newOverloaded(NoPrefix, alts.map(_.sym))
1221-
}
1222-
}
1223-
res
1204+
final def lookupCompanionInIncompleteOwner(original: Symbol): Symbol = {
1205+
// Must have both a class and module symbol, so that `{ class C; def C }` or `{ type T; object T }` are not companions.
1206+
def isCompanion(sym: Symbol): Boolean =
1207+
(original.isModule && sym.isClass || sym.isModule && original.isClass) && sym.isCoDefinedWith(original)
1208+
lookupSibling(original, original.name.companionName).filter(isCompanion)
12241209
}
12251210

1226-
final def lookupCompanionInIncompleteOwner(original: Symbol): Symbol = {
1211+
final def lookupSibling(original: Symbol, name: Name): Symbol = {
12271212
/* Search scopes in current and enclosing contexts for the definition of `symbol` */
12281213
def lookupScopeEntry(symbol: Symbol): ScopeEntry = {
12291214
var res: ScopeEntry = null
@@ -1238,15 +1223,12 @@ trait Contexts { self: Analyzer =>
12381223
res
12391224
}
12401225

1241-
// 1) Must be owned by the same Scope, to ensure that in
1242-
// `{ class C; { ...; object C } }`, the class is not seen as a companion of the object.
1243-
// 2) Must be a class and module symbol, so that `{ class C; def C }` or `{ type T; object T }` are not companions.
1226+
// Must be owned by the same Scope, to ensure that in
1227+
// `{ class C; { ...; object C } }`, the class is not seen as a companion of the object.
12441228
lookupScopeEntry(original) match {
12451229
case null => NoSymbol
12461230
case entry =>
1247-
def isCompanion(sym: Symbol): Boolean =
1248-
(original.isModule && sym.isClass || sym.isModule && original.isClass) && sym.isCoDefinedWith(original)
1249-
entry.owner.lookupNameInSameScopeAs(original, original.name.companionName).filter(isCompanion)
1231+
entry.owner.lookupNameInSameScopeAs(original, name)
12501232
}
12511233
}
12521234

src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -479,9 +479,8 @@ trait NamesDefaults { self: Analyzer =>
479479
if (param.owner.owner.isClass) {
480480
param.owner.owner.info.member(defGetterName)
481481
} else {
482-
// the owner of the method is another method. find the default
483-
// getter in the context.
484-
context.lookup(defGetterName, param.owner.owner)
482+
// the owner of the method is another method. find the default getter in the context.
483+
context.lookupSibling(param.owner, defGetterName)
485484
}
486485
}
487< 9BD9 code>486
} else NoSymbol

test/files/run/names-defaults-nest.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
object Test {
22
def multinest = {
3-
def baz = bar();
3+
def baz = {bar()}
44
def bar(x: String = "a"): Any = {
55
def bar(x: String = "b") = x
66
bar() + x
77
};
8+
bar$default$1(0)
89
assert(baz == "ba", baz)
910
}
1011
def main(args: Array[String]) {

0 commit comments

Comments
 (0)
0