aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Martres <smarter@ubuntu.com>2017-04-04 19:17:42 +0200
committerGuillaume Martres <smarter@ubuntu.com>2017-04-04 20:24:35 +0200
commit6ae376a4544cbf93b94dc0a6ba4a78224e0477df (patch)
tree444b619989a686f66b74a963d238c318ff847aa2
parent42c2a6fbbddf73ef2faeb6204c2b7521a76d7345 (diff)
downloaddotty-6ae376a4544cbf93b94dc0a6ba4a78224e0477df.tar.gz
dotty-6ae376a4544cbf93b94dc0a6ba4a78224e0477df.tar.bz2
dotty-6ae376a4544cbf93b94dc0a6ba4a78224e0477df.zip
checkNoPrivateLeaks: Do not allow types to refer to leaky aliases
`checkNoPrivateLeaks` can force a lot of things, this lead to hard-to-reproduce issues in unpickling because we called `checkNoPrivateLeaks` on the type parameters of a class before anything in the class was indexed. We fix this by making sure that `checkNoPrivateLeaks` never transforms type symbols, only term symbols, therefore we can unpickle type parameters without forcing too many things. tests/neg/leak-type.scala illustrates the new restriction that this necessitates.
-rw-r--r--compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala4
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Checking.scala23
-rw-r--r--tests/neg/leak-type.scala13
-rw-r--r--tests/pos/i1130.scala4
-rw-r--r--tests/pos/leak-inferred.scala12
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
+}