Skip to content

Commit c6b681c

Browse files
authored
Merge pull request #8408 from dotty-staging/fix-export-stable
Refine typing of export forwarders
2 parents e8a0994 + 065b6b9 commit c6b681c

File tree

4 files changed

+17
-4
lines changed

4 files changed

+17
-4
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/neg/exports.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
25 | export printUnit.bitmap // error: no eligible member
1212
| ^
1313
| non-private method bitmap in class Copier refers to private value printUnit
14-
| in its type signature => Copier.this.printUnit.bitmap.type
14+
| in its type signature => Copier.this.printUnit.bitmap$
1515
-- [E120] Duplicate Symbol Error: tests/neg/exports.scala:23:33 --------------------------------------------------------
1616
23 | export printUnit.{stat => _, _} // error: double definition
1717
| ^

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)