8000 SI-3772 Fix detection of term-owned companions · scala/scala@f1a4d59 · GitHub
[go: up one dir, main page]

Skip to content

Commit f1a4d59

Browse files
committed
SI-3772 Fix detection of term-owned companions
Companion detection consults the scopes of enclosing Contexts during typechecking to avoid the cycles that would ensue if we had to look at into the info of enclosing class symbols. For example, this used to typecheck: object CC { val outer = 42 } if ("".isEmpty) { case class CC(c: Int) CC.outer } This logic was not suitably hardened to find companions in exactly the same nesting level. After fixing this problem, a similar problem in `Namer::inCurrentScope` could be solved to be more selective about synthesizing a companion object. In particular, if a manually defined companion trails after the case class, don't create an addiotional synthetic comanpanion object.
1 parent 3107532 commit f1a4d59

File tree

8 files changed

+85
-25
lines changed

8 files changed

+85
-25
lines changed

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,12 @@ trait Namers extends MethodSynthesis {
220220

221221
private def inCurrentScope(m: Symbol): Boolean = {
222222
if (owner.isClass) owner == m.owner
223-
else m.owner.isClass && context.scope == m.owner.info.decls
223+
else {
224+
context.scope.lookupSymbolEntry(m) match {
225+
case null => false
226+
case entry => entry.owner eq context.scope
227+
}
228+
}
224229
}
225230

226231
/** Enter symbol int 8000 o context's scope and return symbol itself */
@@ -1923,10 +1928,13 @@ trait Namers extends MethodSynthesis {
19231928
// use the lower-level scan through the current Context as a fall back.
19241929
if (!currentRun.compiles(owner)) owner.initialize
19251930
original.companionSymbol orElse {
1926-
ctx.lookup(original.name.companionName, owner).suchThat(sym =>
1927-
(original.isTerm || sym.hasModuleFlag) &&
1928-
(sym isCoDefinedWith original)
1929-
)
1931+
var c = ctx
1932+
var companion: Symbol = NoSymbol
1933+
while (c != NoContext && companion == NoSymbol) {
1934+
companion = c.scope.lookupCompanion(original)
1935+
c = c.outer
1936+
}
1937+
companion
19301938
}
19311939
}
19321940

src/reflect/scala/reflect/internal/Scopes.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,30 @@ trait Scopes extends api.Scopes { self: SymbolTable =>
282282
}
283283
}
284284

285+
final def lookupSymbolEntry(sym: Symbol): ScopeEntry = {
286+
var e = lookupEntry(sym.name)
287+
while (e ne null) {
288+
if (e.sym == sym) return e
289+
e = lookupNextEntry(e)
290+
}
291+
null
292+
}
293+
294+
final def lookupCompanion(original: Symbol): Symbol = {
295+
lookupSymbolEntry(original) match {
296+
case null =>
297+
case entry if entry.sym == original =>
298+
entry.owner.lookupAllEntries(original.name.companionName)
299+
.filter(_.owner eq entry.owner)
300+
.filter(x => original.isTerm || x.sym.hasModuleFlag).toList match {
301+
case only :: Nil => return only.sym
302+
case _ =>
303+
}
304+
case _ =>
305+
}
306+
NoSymbol
307+
}
308+
285309
/** lookup a symbol entry matching given name.
286310
* @note from Martin: I believe this is a hotspot or will be one
287311
* in future versions of the type system. I have reverted the previous

test/files/neg/t3772.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
t3772.scala:7: error: value inner is not a member of object CC
2+
CC.inner
3+
^
4+
t3772.scala:14: error: value outer is not a member of object CC
5+
CC.outer
6+
^
7+
two errors found

test/files/neg/t3772.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class Test {
2+
def m = {
3+
case class CC(c: Int)
4+
if ("".isEmpty) {
5+
object CC { def inner = 42}
6+
}
7+
CC.inner
8+
}
9+
def n = {
10+
object CC { val outer = 42 }
11+
if ("".isEmpty) {
12+
case class CC(c: Int)
13+
CC(0).c
14+
CC.outer
15+
}
16+
}
17+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
t8002-nested-scope.scala:8: error: method x in class C cannot be accessed in C
2+
new C().x
3+
^
4+
one error found
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class C {
2+
def foo = {
3+
class C { private def x = 0 }
4+
5+
{
6+
val a = 0
7+
object C {
8+
new C().x
9+
}
10+
}
11+
}
12+
}

test/files/pos/t3772.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class Test {
2+
def m = {
3+
case class C(c: Int)
4+
object C { def xxx = true}
5+
C(42).c
6+
C.xxx
7+
}
8+
}

test/files/pos/t8002-nested-scope.scala

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)
0