Skip to content

Commit 5e82581

Browse files
committed
Refine export types
We export values under their singleton types only if the original value was already public. Otherwise this would provoke a leak.
1 parent 19d96eb commit 5e82581

File tree

4 files changed

+9
-13
lines changed

4 files changed

+9
-13
lines changed

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,9 @@ object SymDenotations {
12511251
else defn.RootClass
12521252
}
12531253

1254+
final def isPublic(implicit ctx: Context): Boolean =
1255+
accessBoundary(owner) == defn.RootClass
1256+
12541257
/** The primary constructor of a class or trait, NoSymbol if not applicable. */
12551258
def primaryConstructor(implicit ctx: Context): Symbol = NoSymbol
12561259

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -977,8 +977,10 @@ class Namer { typer: Typer =>
977977
coord = span)
978978
else {
979979
val (maybeStable, mbrInfo) =
980-
if (mbr.symbol.isStableMember) (StableRealizable, ExprType(path.tpe.select(mbr.symbol)))
981-
else (EmptyFlags, mbr.info.ensureMethodic)
980+
if (mbr.symbol.isStableMember && mbr.symbol.isPublic)
981+
(StableRealizable, ExprType(path.tpe.select(mbr.symbol)))
982+
else
983+
(EmptyFlags, mbr.info.ensureMethodic)
982984
val mbrFlags = Exported | Method | Final | maybeStable | mbr.symbol.flags & RetainedExportFlags
983985
ctx.newSymbol(cls, alias, mbrFlags, mbrInfo, coord = span)
984986
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ Export aliases are always `final`. Aliases of delegates are again defines as del
7575
not marked `override`.
7676
- However, export aliases can implement deferred members of base classes.
7777

78-
Export aliases for value definitions are marked by the compiler as "stable" and their result types are the singleton types of the aliased definitions. This means
79-
that they can be used as parts of stable identifier paths, even though they are technically methods. For instance, the following is OK:
78+
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:
8079
```scala
8180
class C { type T }
8281
object O { val c: C = ... }

tests/neg/export-leaks.scala

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,4 @@ object Signature {
1111
object O2 {
1212
private[Signature] val foo: T = ???
1313
}
14-
export O2._ // OK
15-
// The reason this works is that private escape checking only looks at real private members,
16-
// not at private[C] members. So, by itself the expansion of the export
17-
// <stable> def foo: O2.foo.type = O2.foo
18-
// is legal. The underlying type of `O2.foo.type` does violate no-escape rules, but we do not
19-
// check for it. Maybe we should. But then the question comes up whether we should
20-
// also check all possible supertypes of a type for privacy violations. These are more
21-
// general questions that are not related to exports.
22-
}
14+
export O2._ // error: non-private method foo refers to private type T

0 commit comments

Comments
 (0)