Skip to content

Commit 34afd70

Browse files
authored
Merge pull request #2189 from dotty-staging/fix/privateleak-forcing
checkNoPrivateLeaks: Do not allow types to refer to leaky aliases
2 parents 42c2a6f + 6ae376a commit 34afd70

File tree

5 files changed

+48
-8
lines changed

5 files changed

+48
-8
lines changed

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle
736736
// no longer necessary.
737737
goto(end)
738738
setPos(start, tree)
739-
sym.info = ta.avoidPrivateLeaks(sym, tree.pos)
739+
if (!sym.isType) { // Only terms might have leaky aliases, see the documentation of `checkNoPrivateLeaks`
740+
sym.info = ta.avoidPrivateLeaks(sym, tree.pos)
741+
}
740742
tree
741743
}
742744

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

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -348,12 +348,23 @@ object Checking {
348348

349349
/** Check the type signature of the symbol `M` defined by `tree` does not refer
350350
* to a private type or value which is invisible at a point where `M` is still
351-
* visible. As an exception, we allow references to type aliases if the underlying
352-
* type of the alias is not a leak. So type aliases are transparent as far as
353-
* leak testing is concerned.
351+
* visible.
352+
*
353+
* As an exception, we allow references to type aliases if the underlying
354+
* type of the alias is not a leak, and if `sym` is not a type. The rationale
355+
* for this is that the inferred type of a term symbol might contain leaky
356+
* aliases which should be removed (see leak-inferred.scala for an example),
357+
* but a type symbol definition will not contain leaky aliases unless the
358+
* user wrote them, so we can ask the user to change his definition. The more
359+
* practical reason for not transforming types is that `checkNoPrivateLeaks`
360+
* can force a lot of denotations, and this restriction means that we never
361+
* need to run `TypeAssigner#avoidPrivateLeaks` on type symbols when
362+
* unpickling, which avoids some issues related to forcing order.
363+
*
364+
* See i997.scala for negative tests, and i1130.scala for a case where it
365+
* matters that we transform leaky aliases away.
366+
*
354367
* @return The `info` of `sym`, with problematic aliases expanded away.
355-
* See i997.scala for tests, i1130.scala for a case where it matters that we
356-
* transform leaky aliases away.
357368
*/
358369
def checkNoPrivateLeaks(sym: Symbol, pos: Position)(implicit ctx: Context): Type = {
359370
class NotPrivate extends TypeMap {
@@ -388,7 +399,7 @@ object Checking {
388399
tp
389400
}
390401
else mapOver(tp)
391-
if ((errors ne prevErrors) && tp.info.isAlias) {
402+
if ((errors ne prevErrors) && !sym.isType && tp.info.isAlias) {
392403
// try to dealias to avoid a leak error
393404
val savedErrors = errors
394405
errors = prevErrors

tests/neg/leak-type.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
trait A {
2+
private type Foo = Int
3+
4+
5+
class Inner[T <: Foo] { // error: non-private type T refers to private type Foo in its type signature
6+
def get: T = ???
7+
}
8+
}
9+
class B extends A {
10+
def foo(x: Inner[_]): Unit = {
11+
val a = x.get // error: cannot resolve reference to type B(B.this).Foo
12+
}
13+
}

tests/pos/i1130.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ trait A {
33

44
def foo: Foo = 1
55
}
6-
class B extends A
6+
class B extends A {
7+
foo
8+
}

tests/pos/leak-inferred.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class A {
2+
private val x = List(1,2)
3+
4+
val elem = x.head
5+
}
6+
7+
class B extends A {
8+
val a: Int = elem
9+
// Without `checkNoPrivateLeaks`, we get:
10+
// found: B.this.x.scala$collection$immutable$List$$A(B.this.elem)
11+
// required: Int
12+
}

0 commit comments

Comments
 (0)