Skip to content

Commit 545235a

Browse files
committed
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) ```
1 parent fc29643 commit 545235a

File tree

3 files changed

+16
-3
lines changed

3 files changed

+16
-3
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1088,8 +1088,11 @@ class Namer { typer: Typer =>
10881088
// allow such type aliases. If we forbid them at some point (requiring the referred type to be
10891089
// fully applied), we'd have to change the scheme here as well.
10901090
else {
1091+
def refersToPrivate(tp: Type): Boolean = tp match
1092+
case tp: TermRef => tp.termSymbol.is(Private) || refersToPrivate(tp.prefix)
1093+
case _ => false
10911094
val (maybeStable, mbrInfo) =
1092-
if (sym.isStableMember && sym.isPublic)
1095+
if sym.isStableMember && sym.isPublic && !refersToPrivate(path.tpe) then
10931096
(StableRealizable, ExprType(path.tpe.select(sym)))
10941097
else
10951098
(EmptyFlags, mbr.info.ensureMethodic)

docs/docs/reference/other-new-features/export.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ Export aliases are always `final`. Aliases of given instances are again defined
7777
not marked `override`.
7878
- However, export aliases can implement deferred members of base classes.
7979

80-
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:
80+
Export aliases for public value definitions that are accessed without
81+
referring to private values in the qualifier path
82+
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:
8183
```scala
8284
class C { type T }
8385
object O { val c: C = ... }

tests/run/exports.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,17 @@ object Test extends App {
3939
1.scanned
4040
}
4141
test()
42+
43+
val _: Int = B.x
4244
}
4345

4446
final class Foo {
4547
lazy val foo : Foo = new Foo
4648
export foo._ // nothing is exported
47-
}
49+
}
50+
51+
class A:
52+
val x: Int = 1
53+
class B(a: A):
54+
export a.x
55+
object B extends B(A())

0 commit comments

Comments
 (0)