From 545235a1273020e47ad2da3c099f0a746ea0887b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 1 Mar 2020 12:01:26 +0100 Subject: [PATCH 1/2] Refine typing of export forwarders An export forwarder is now of singleton type only if the export path does not refer to private values. Previously, this situation would lead to a type error later on, like this one: ```scala scala> class A{ | val x: Int = 1 | } | class B(a: A){ | export a.x | } 5 | export a.x | ^ | non-private method x in class B refers to private value a | in its type signature => (B.this.a.x : Int) ``` --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 5 ++++- docs/docs/reference/other-new-features/export.md | 4 +++- tests/run/exports.scala | 10 +++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 97f61c6664bd..c2e81b18c348 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1088,8 +1088,11 @@ class Namer { typer: Typer => // allow such type aliases. If we forbid them at some point (requiring the referred type to be // fully applied), we'd have to change the scheme here as well. else { + def refersToPrivate(tp: Type): Boolean = tp match + case tp: TermRef => tp.termSymbol.is(Private) || refersToPrivate(tp.prefix) + case _ => false val (maybeStable, mbrInfo) = - if (sym.isStableMember && sym.isPublic) + if sym.isStableMember && sym.isPublic && !refersToPrivate(path.tpe) then (StableRealizable, ExprType(path.tpe.select(sym))) else (EmptyFlags, mbr.info.ensureMethodic) diff --git a/docs/docs/reference/other-new-features/export.md b/docs/docs/reference/other-new-features/export.md index 11ed1c55c229..e84725ea91ef 100644 --- a/docs/docs/reference/other-new-features/export.md +++ b/docs/docs/reference/other-new-features/export.md @@ -77,7 +77,9 @@ Export aliases are always `final`. Aliases of given instances are again defined not marked `override`. - However, export aliases can implement deferred members of base classes. -Export aliases for public value definitions are marked by the compiler as "stable" and their result types are the singleton types of the aliased definitions. This means that they can be used as parts of stable identifier paths, even though they are technically methods. For instance, the following is OK: +Export aliases for public value definitions that are accessed without +referring to private values in the qualifier path +are marked by the compiler as "stable" and their result types are the singleton types of the aliased definitions. This means that they can be used as parts of stable identifier paths, even though they are technically methods. For instance, the following is OK: ```scala class C { type T } object O { val c: C = ... } diff --git a/tests/run/exports.scala b/tests/run/exports.scala index 6fb5de88322b..c796f330b9c7 100644 --- a/tests/run/exports.scala +++ b/tests/run/exports.scala @@ -39,9 +39,17 @@ object Test extends App { 1.scanned } test() + + val _: Int = B.x } final class Foo { lazy val foo : Foo = new Foo export foo._ // nothing is exported -} \ No newline at end of file +} + +class A: + val x: Int = 1 +class B(a: A): + export a.x +object B extends B(A()) \ No newline at end of file From 065b6b917de69dc61c82a4a442955f47b46496f9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 1 Mar 2020 12:56:32 +0100 Subject: [PATCH 2/2] Update check file --- tests/neg/exports.check | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/neg/exports.check b/tests/neg/exports.check index f691b66444f6..e59b11771d2b 100644 --- a/tests/neg/exports.check +++ b/tests/neg/exports.check @@ -11,7 +11,7 @@ 25 | export printUnit.bitmap // error: no eligible member | ^ | non-private method bitmap in class Copier refers to private value printUnit - | in its type signature => Copier.this.printUnit.bitmap.type + | in its type signature => Copier.this.printUnit.bitmap$ -- [E120] Duplicate Symbol Error: tests/neg/exports.scala:23:33 -------------------------------------------------------- 23 | export printUnit.{stat => _, _} // error: double definition | ^