diff options
-rw-r--r-- | compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 4 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/typer/Checking.scala | 23 | ||||
-rw-r--r-- | tests/neg/leak-type.scala | 13 | ||||
-rw-r--r-- | tests/pos/i1130.scala | 4 | ||||
-rw-r--r-- | tests/pos/leak-inferred.scala | 12 |
5 files changed, 48 insertions, 8 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index a9ea49ad1..88b6eef7a 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -736,7 +736,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle // no longer necessary. goto(end) setPos(start, tree) - sym.info = ta.avoidPrivateLeaks(sym, tree.pos) + if (!sym.isType) { // Only terms might have leaky aliases, see the documentation of `checkNoPrivateLeaks` + sym.info = ta.avoidPrivateLeaks(sym, tree.pos) + } tree } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index b43391592..1dc35f507 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -348,12 +348,23 @@ object Checking { /** Check the type signature of the symbol `M` defined by `tree` does not refer * to a private type or value which is invisible at a point where `M` is still - * visible. As an exception, we allow references to type aliases if the underlying - * type of the alias is not a leak. So type aliases are transparent as far as - * leak testing is concerned. + * visible. + * + * As an exception, we allow references to type aliases if the underlying + * type of the alias is not a leak, and if `sym` is not a type. The rationale + * for this is that the inferred type of a term symbol might contain leaky + * aliases which should be removed (see leak-inferred.scala for an example), + * but a type symbol definition will not contain leaky aliases unless the + * user wrote them, so we can ask the user to change his definition. The more + * practical reason for not transforming types is that `checkNoPrivateLeaks` + * can force a lot of denotations, and this restriction means that we never + * need to run `TypeAssigner#avoidPrivateLeaks` on type symbols when + * unpickling, which avoids some issues related to forcing order. + * + * See i997.scala for negative tests, and i1130.scala for a case where it + * matters that we transform leaky aliases away. + * * @return The `info` of `sym`, with problematic aliases expanded away. - * See i997.scala for tests, i1130.scala for a case where it matters that we - * transform leaky aliases away. */ def checkNoPrivateLeaks(sym: Symbol, pos: Position)(implicit ctx: Context): Type = { class NotPrivate extends TypeMap { @@ -388,7 +399,7 @@ object Checking { tp } else mapOver(tp) - if ((errors ne prevErrors) && tp.info.isAlias) { + if ((errors ne prevErrors) && !sym.isType && tp.info.isAlias) { // try to dealias to avoid a leak error val savedErrors = errors errors = prevErrors diff --git a/tests/neg/leak-type.scala b/tests/neg/leak-type.scala new file mode 100644 index 000000000..30ecab70b --- /dev/null +++ b/tests/neg/leak-type.scala @@ -0,0 +1,13 @@ +trait A { + private type Foo = Int + + + class Inner[T <: Foo] { // error: non-private type T refers to private type Foo in its type signature + def get: T = ??? + } +} +class B extends A { + def foo(x: Inner[_]): Unit = { + val a = x.get // error: cannot resolve reference to type B(B.this).Foo + } +} diff --git a/tests/pos/i1130.scala b/tests/pos/i1130.scala index 8d71de5e8..c28eaa169 100644 --- a/tests/pos/i1130.scala +++ b/tests/pos/i1130.scala @@ -3,4 +3,6 @@ trait A { def foo: Foo = 1 } -class B extends A +class B extends A { + foo +} diff --git a/tests/pos/leak-inferred.scala b/tests/pos/leak-inferred.scala new file mode 100644 index 000000000..5d8a7e3bc --- /dev/null +++ b/tests/pos/leak-inferred.scala @@ -0,0 +1,12 @@ +class A { + private val x = List(1,2) + + val elem = x.head +} + +class B extends A { + val a: Int = elem + // Without `checkNoPrivateLeaks`, we get: + // found: B.this.x.scala$collection$immutable$List$$A(B.this.elem) + // required: Int +} |