Skip to content

Commit 6ada020

Browse files
committed
Merge pull request #42 from odersky/fix/#39-checkAccessible
Fix of #39
2 parents 092456b + f196e07 commit 6ada020

File tree

5 files changed

+75
-34
lines changed

5 files changed

+75
-34
lines changed

src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ object Denotations {
393393
exists && p(this)
394394

395395
def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation =
396-
if (symbol isAccessibleFrom (pre, superAccess)) this else NoDenotation
396+
if (!symbol.exists || symbol.isAccessibleFrom(pre, superAccess)) this else NoDenotation
397397

398398
def atSignature(sig: Signature)(implicit ctx: Context): SingleDenotation =
399399
if (sig matches signature) this else NoDenotation

src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -116,40 +116,43 @@ class Typer extends Namer with Applications with Implicits {
116116
* current context. Return the type with those alternatives as denotations
117117
* which are accessible.
118118
*/
119-
def checkAccessible(tpe: Type, superAccess: Boolean, pos: Position)(implicit ctx: Context): Type = tpe match {
120-
case tpe: NamedType =>
121-
val pre = tpe.prefix
122-
val name = tpe.name
123-
val d = tpe.denot.accessibleFrom(pre, superAccess)
124-
if (!d.exists) {
125-
val d2 = pre.nonPrivateMember(name)
126-
if (reallyExists(d2) && (d2 ne tpe.denot))
127-
checkAccessible(pre.select(name, d2), superAccess, pos)
128-
else {
129-
val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists)
130-
val what = alts match {
131-
case Nil =>
132-
name.toString
133-
case sym :: Nil =>
134-
if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated
135-
case _ =>
136-
i"none of the overloaded alternatives named $name"
119+
def checkAccessible(tpe: Type, superAccess: Boolean, pos: Position)(implicit ctx: Context): Type = {
120+
def test(tpe: Type, firstTry: Boolean): Type = tpe match {
121+
case tpe: NamedType =>
122+
val pre = tpe.prefix
123+
val name = tpe.name
124+
val d = tpe.denot.accessibleFrom(pre, superAccess)
125+
if (!d.exists) {
126+
// it could be that we found an inaccessbile private member, but there is
127+
// an inherited non-private member with the same name and signature.
128+
val d2 = pre.nonPrivateMember(name)
129+
if (reallyExists(d2) && firstTry) test(pre.select(name, d2), false)
130+
else {
131+
val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists)
132+
val what = alts match {
133+
case Nil =>
134+
name.toString
135+
case sym :: Nil =>
136+
if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated
137+
case _ =>
138+
i"none of the overloaded alternatives named $name"
139+
}
140+
val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else ""
141+
val whyNot = new StringBuffer
142+
val addendum =
143+
alts foreach (_.isAccessibleFrom(pre, superAccess, whyNot))
144+
if (!tpe.isError)
145+
ctx.error(i"$what cannot be accessed as a member of $pre$where.$whyNot", pos)
146+
ErrorType
137147
}
138-
val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else ""
139-
val whyNot = new StringBuffer
140-
val addendum =
141-
alts foreach (_.isAccessibleFrom(pre, superAccess, whyNot))
142-
if (!tpe.isError)
143-
ctx.error(i"$what cannot be accessed as a member of $pre$where.$whyNot", pos)
144-
ErrorType
145-
}
146-
}
147-
else if (d.symbol is TypeParamAccessor) // always dereference type param accessors
148-
checkAccessible(d.info.bounds.hi, superAccess, pos)
149-
else
150-
tpe withDenot d
151-
case _ =>
152-
tpe
148+
} else if (d.symbol is TypeParamAccessor) // always dereference type param accessors
149+
checkAccessible(d.info.bounds.hi, superAccess, pos)
150+
else
151+
tpe withDenot d
152+
case _ =>
153+
tpe
154+
}
155+
test(tpe, true)
153156
}
154157

155158
/** The enclosing class, except if we are in a super call, in which case

test/dotc/tests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class tests extends CompilerTest {
4040
@Test def pos_overloaded() = compileFile(posDir, "overloaded")
4141
@Test def pos_templateParents() = compileFile(posDir, "templateParents")
4242
@Test def pos_structural() = compileFile(posDir, "structural")
43+
@Test def pos_i39 = compileFile(posDir, "i39")
4344

4445
@Test def neg_blockescapes() = compileFile(negDir, "blockescapesNeg", xerrors = 1)
4546
@Test def neg_typedapply() = compileFile(negDir, "typedapply", xerrors = 4)
@@ -49,6 +50,7 @@ class tests extends CompilerTest {
4950
@Test def neg_privates() = compileFile(negDir, "privates", xerrors = 2)
5051
@Test def neg_rootImports = compileFile(negDir, "rootImplicits", xerrors = 2)
5152
@Test def neg_templateParents() = compileFile(negDir, "templateParents", xerrors = 3)
53+
@Test def neg_i39 = compileFile(negDir, "i39", xerrors = 1)
5254

5355
@Test def dotc = compileDir(dotcDir + "tools/dotc")
5456
@Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast")

tests/neg/i39.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
object i39neg {
2+
3+
trait B {
4+
type D <: { type T }
5+
def d: D
6+
}
7+
8+
val bc: B = new B {
9+
def d: D = ???
10+
private def pd: D = ???
11+
}
12+
13+
val d: bc.D = bc.d
14+
val pd: bc.D = bc.pd
15+
16+
// infinite loop in Typer
17+
val asT: d.T = ???
18+
19+
}

tests/pos/i39.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
object i39 {
2+
3+
trait B {
4+
type D <: { type T }
5+
def d: D
6+
}
7+
8+
val bc: B = new B {
9+
def d: D = ???
10+
}
11+
12+
val d: bc.D = bc.d
13+
14+
// infinite loop in Typer
15+
val asT: d.T = ???
16+
17+
}

0 commit comments

Comments
 (0)