Skip to content

Commit f2edc11

Browse files
committed
Make private term members Local if accessed only from this
Make private term members Local if they are accessed only from their owner's this-type.
1 parent 31228c0 commit f2edc11

File tree

4 files changed

+42
-14
lines changed

4 files changed

+42
-14
lines changed

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,9 @@ object SymDenotations {
795795
* @param superAccess Access is via super
796796
* Everything is accessible if `pre` is `NoPrefix`.
797797
* A symbol with type `NoType` is not accessible for any other prefix.
798+
*
799+
* As a side effect, drop Local flags of members that are not accessed via the ThisType
800+
* of their owner.
798801
*/
799802
final def isAccessibleFrom(pre: Type, superAccess: Boolean = false, whyNot: StringBuffer = null)(implicit ctx: Context): Boolean = {
800803

@@ -855,6 +858,11 @@ object SymDenotations {
855858
|| (accessWithin(boundary) || accessWithinLinked(boundary)) &&
856859
( !this.is(Local)
857860
|| isCorrectThisType(pre)
861+
|| canBeLocal(name, flags)
862+
&& {
863+
resetFlag(Local)
864+
true
865+
}
858866
)
859867
|| this.is(Protected) &&
860868
( superAccess
@@ -2184,6 +2192,20 @@ object SymDenotations {
21842192
validFor = Period.allInRun(NoRunId)
21852193
}
21862194

2195+
/** Can a pruvate symbol with given name and flags be inferred to be local,
2196+
* if all references to such symbols are via `this`?
2197+
* This holds for all term symbols, except
2198+
* - constructors, since they can never be referred to as members of their
2199+
* own, fully elaborated `this`.
2200+
* - parameters and parameter accessors, since their Local status is already
2201+
* determined by whether they have a `val` or `var` or not.
2202+
*/
2203+
def canBeLocal(name: Name, flags: FlagSet)(given Context) =
2204+
name.isTermName
2205+
&& !name.isConstructorName
2206+
&& !flags.is(Param)
2207+
&& !flags.is(ParamAccessor)
2208+
21872209
// ---- Completion --------------------------------------------------------
21882210

21892211
/** Instances of LazyType are carried by uncompleted symbols.

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@ class Namer { typer: Typer =>
276276
/** Check that flags are OK for symbol. This is done early to avoid
277277
* catastrophic failure when we create a TermSymbol with TypeFlags, or vice versa.
278278
* A more complete check is done in checkWellFormed.
279+
* Also, speculatively add a Local flag to private members that can be Local if
280+
* referred to exclusively from their owner's this-type. The Local flag is retracted in
281+
* `isAccessibleFrom` if the access not from such a this-type.
279282
*/
280283
def checkFlags(flags: FlagSet) =
281284
if (flags.isEmpty) flags
@@ -284,9 +287,12 @@ class Namer { typer: Typer =>
284287
case tree: TypeDef => (flags.isTypeFlags, flags.toTypeFlags, "type")
285288
case _ => (flags.isTermFlags, flags.toTermFlags, "value")
286289
}
287-
if (!ok)
290+
def canBeLocal = tree match
291+
case tree: ValOrDefDef => SymDenotations.canBeLocal(tree.name, flags)
292+
case _ => false
293+
if !ok then
288294
ctx.error(i"modifier(s) `${flags.flagsString}` incompatible with $kind definition", tree.sourcePos)
289-
adapted
295+
if adapted.is(Private) && canBeLocal then adapted | Local else adapted
290296
}
291297

292298
/** Add moduleClass/sourceModule to completer if it is for a module val or class */

tests/run/i1692.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ class LazyNullable(a: => Int) {
1616
private [this] val e = "E"
1717
lazy val l4 = try e finally () // null out e
1818

19+
private val eInf = "E" // still nullable because private[this] is inferred
20+
lazy val l4Inf = eInf
21+
1922
private[this] val i = "I"
2023
// null out i even though the try ends up lifted, because the LazyVals phase runs before the LiftTry phase
2124
lazy val l5 = try i catch { case e: Exception => () }
@@ -39,9 +42,6 @@ class LazyNotNullable {
3942
private[this] lazy val d = "D" // not nullable because lazy
4043
lazy val l3 = d
4144

42-
private val e = "E" // not nullable because not private[this]
43-
lazy val l4 = e
44-
4545
private[this] val f = "F" // not nullable because used in mutiple lazy vals
4646
lazy val l5 = f
4747
lazy val l6 = f
@@ -98,6 +98,9 @@ object Test {
9898
assert(lz.l4 == "E")
9999
assertNull("e")
100100

101+
assert(lz.l4Inf == "E")
102+
assertNull("eInf")
103+
101104
assert(lz.l5 == "I")
102105
assertNull("i")
103106

@@ -124,9 +127,6 @@ object Test {
124127

125128
assert(lz.l3 == "D")
126129

127-
assert(lz.l4 == "E")
128-
assertNotNull("e")
129-
130130
assert(lz.l5 == "F")
131131
assert(lz.l6 == "F")
132132
assertNotNull("f")

tests/run/i1692b.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class LazyNullable(a: => Int) {
1818
private [this] val e = "E"
1919
@threadUnsafe lazy val l4 = try e finally () // null out e
2020

21+
private val eInf = "E"
22+
@threadUnsafe lazy val l4Inf = try eInf finally () // null out e, since private[this] is inferred
23+
2124
private[this] val i = "I"
2225
// null out i even though the try ends up lifted, because the LazyVals phase runs before the LiftTry phase
2326
@threadUnsafe lazy val l5 = try i catch { case e: Exception => () }
@@ -41,9 +44,6 @@ class LazyNotNullable {
4144
@threadUnsafe private[this] lazy val d = "D" // not nullable because lazy
4245
@threadUnsafe lazy val l3 = d
4346

44-
private val e = "E" // not nullable because not private[this]
45-
@threadUnsafe lazy val l4 = e
46-
4747
private[this] val f = "F" // not nullable because used in mutiple @threadUnsafe lazy vals
4848
@threadUnsafe lazy val l5 = f
4949
@threadUnsafe lazy val l6 = f
@@ -100,6 +100,9 @@ object Test {
100100
assert(lz.l4 == "E")
101101
assertNull("e")
102102

103+
assert(lz.l4Inf == "E")
104+
assertNull("eInf")
105+
103106
assert(lz.l5 == "I")
104107
assertNull("i")
105108

@@ -126,9 +129,6 @@ object Test {
126129

127130
assert(lz.l3 == "D")
128131

129-
assert(lz.l4 == "E")
130-
assertNotNull("e")
131-
132132
assert(lz.l5 == "F")
133133
assert(lz.l6 == "F")
134134
assertNotNull("f")

0 commit comments

Comments
 (0)