Skip to content

Commit a843f61

Browse files
committed
Fix #4811: widen Local modifier in ExtensionMethods and Getter
This fixes the crash because in Getter we don't look for a setter that was not generated. The original documentation for ElimLocals says: This phase drops the Local flag from all private[this] and protected[this] members. This allows subsequent code motions where code is moved from a class to its companion object. It invalidates variance checking, which is consequently disabled when retyping. We instead let ExtensionMethods remove the flag when needed. And then remove all Local flags in Getters once there are not needed anymore.
1 parent 18fa15e commit a843f61

File tree

7 files changed

+66
-40
lines changed

7 files changed

+66
-40
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ class Compiler {
6363
new ElimPackagePrefixes) :: // Eliminate references to package prefixes in Select nodes
6464
List(new CheckStatic, // Check restrictions that apply to @static members
6565
new ElimRepeated, // Rewrite vararg parameters and arguments
66-
new NormalizeFlags, // Rewrite some definition flags
6766
new ExpandSAMs, // Expand single abstract method closures to anonymous classes
6867
new ProtectedAccessors, // Add accessors for protected members
6968
new ExtensionMethods, // Expand methods of value classes with extension methods

compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class ExtensionMethods extends MiniPhase with DenotTransformer with FullParamete
4646

4747
override def runsAfter = Set(
4848
ElimRepeated.name,
49-
ProtectedAccessors.name // protected accessors cannot handle code that is moved from class to companion object
49+
ProtectedAccessors.name, // protected accessors cannot handle code that is moved from class to companion object
5050
)
5151

5252
override def runsAfterGroupsOf = Set(FirstTransform.name) // need companion objects to exist
@@ -97,17 +97,21 @@ class ExtensionMethods extends MiniPhase with DenotTransformer with FullParamete
9797
moduleClassSym
9898
}
9999
case ref: SymDenotation =>
100+
var ref1 = ref
100101
if (isMethodWithExtension(ref.symbol) && ref.hasAnnotation(defn.TailrecAnnot)) {
101-
val ref1 = ref.copySymDenotation()
102+
ref1 = ref.copySymDenotation()
102103
ref1.removeAnnotation(defn.TailrecAnnot)
103-
ref1
104104
}
105105
else if (ref.isConstructor && isDerivedValueClass(ref.owner) && ref.is(AccessFlags)) {
106-
val ref1 = ref.copySymDenotation()
106+
ref1 = ref.copySymDenotation()
107107
ref1.resetFlag(AccessFlags)
108-
ref1
109108
}
110-
else ref
109+
// widen private[this] for code that will be moved to the companion object
110+
if (ref.is(Local) && isDerivedValueClass(ref.owner)) {
111+
if (ref1 ne ref) ref1.resetFlag(Local)
112+
else ref1 = ref.copySymDenotation(initFlags = ref.flags &~ Local)
113+
}
114+
ref1
111115
case _ =>
112116
ref
113117
}

compiler/src/dotty/tools/dotc/transform/Flatten.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ class Flatten extends MiniPhase with SymTransformer {
1717

1818
override def phaseName = "flatten"
1919

20+
// private[this] and protected[this] modifiers must be dropped
21+
// before classes are lifted. Getters drop these modifiers.
22+
override def runsAfter = Set(Getters.name)
23+
2024
override def changesMembers = true // the phase removes inner classes
2125

2226
private var LiftedDefs: Store.Location[mutable.ListBuffer[Tree]] = _

compiler/src/dotty/tools/dotc/transform/Getters.scala

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,32 @@ import ValueClasses._
4949
class Getters extends MiniPhase with SymTransformer {
5050
import ast.tpd._
5151

52-
override def phaseName = "getters"
52+
override def phaseName = Getters.name
5353

5454
override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = {
5555
def noGetterNeeded =
5656
d.is(NoGetterNeeded) ||
57-
d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) && !isDerivedValueClass(d.owner) && !d.is(Flags.Lazy) ||
57+
d.is(PrivateLocal) && !d.owner.is(Trait) && !isDerivedValueClass(d.owner) && !d.is(Flags.Lazy) ||
5858
d.is(Module) && d.isStatic ||
5959
d.hasAnnotation(defn.ScalaStaticAnnot) ||
6060
d.isSelfSym
61-
if (d.isTerm && (d.is(Lazy) || d.owner.isClass) && d.info.isValueType && !noGetterNeeded) {
62-
val maybeStable = if (d.isStable) Stable else EmptyFlags
63-
d.copySymDenotation(
64-
initFlags = d.flags | maybeStable | AccessorCreationFlags,
65-
info = ExprType(d.info))
61+
62+
var d0 =
63+
if (d.isTerm && (d.is(Lazy) || d.owner.isClass) && d.info.isValueType && !noGetterNeeded) {
64+
val maybeStable = if (d.isStable) Stable else EmptyFlags
65+
d.copySymDenotation(
66+
initFlags = d.flags | maybeStable | AccessorCreationFlags,
67+
info = ExprType(d.info))
68+
}
69+
else d
70+
71+
// Drops the Local flag from all private[this] and protected[this] members.
72+
// This allows subsequent code motions (e.g. Flatten)
73+
if (d0.is(Local)) {
74+
if (d0 ne d) d0.resetFlag(Local)
75+
else d0 = d.copySymDenotation(initFlags = d.flags &~ Local)
6676
}
67-
else d
77+
d0
6878
}
6979
private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic
7080

@@ -74,3 +84,7 @@ class Getters extends MiniPhase with SymTransformer {
7484
override def transformAssign(tree: Assign)(implicit ctx: Context): Tree =
7585
if (tree.lhs.symbol is Method) tree.lhs.becomes(tree.rhs).withPos(tree.pos) else tree
7686
}
87+
88+
object Getters {
89+
val name = "getters"
90+
}

compiler/src/dotty/tools/dotc/transform/NormalizeFlags.scala

Lines changed: 0 additions & 25 deletions
This file was deleted.

tests/pos/flatten.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
object A1 {
2+
private[this] class B
3+
4+
private[this] class C {
5+
def cons: B = new B
6+
}
7+
}
8+
9+
class A2 {
10+
private[this] class B
11+
12+
private[this] class C {
13+
def cons: B = new B
14+
}
15+
}

tests/pos/i4811.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class StringOps(val s: String) extends AnyVal {
2+
def lines: Iterator[String] = new collection.AbstractIterator[String] {
3+
private[this] var index = 0
4+
def hasNext: Boolean = false
5+
def next(): String = {
6+
index = 1
7+
""
8+
}
9+
}
10+
}
11+
12+
class FooOps(val s: String) extends AnyVal {
13+
private[this] def bar = 2
14+
def foo = bar
15+
}

0 commit comments

Comments
 (0)