Skip to content

Commit b156b83

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 b156b83

File tree

7 files changed

+72
-40
lines changed

7 files changed

+72
-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: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ import SymUtils._
3535
*
3636
* Finally, if the constructor of a value class is private pr protected
3737
* it is widened to public.
38+
*
39+
* Also, drop the Local flag from all private[this] and protected[this] members
40+
* that will be moved to the companion object.
3841
*/
3942
class ExtensionMethods extends MiniPhase with DenotTransformer with FullParameterization { thisPhase =>
4043

@@ -46,7 +49,7 @@ class ExtensionMethods extends MiniPhase with DenotTransformer with FullParamete
4649

4750
override def runsAfter = Set(
4851
ElimRepeated.name,
49-
ProtectedAccessors.name // protected accessors cannot handle code that is moved from class to companion object
52+
ProtectedAccessors.name, // protected accessors cannot handle code that is moved from class to companion object
5053
)
5154

5255
override def runsAfterGroupsOf = Set(FirstTransform.name) // need companion objects to exist
@@ -97,17 +100,22 @@ class ExtensionMethods extends MiniPhase with DenotTransformer with FullParamete
97100
moduleClassSym
98101
}
99102
case ref: SymDenotation =>
103+
var ref1 = ref
100104
if (isMethodWithExtension(ref.symbol) && ref.hasAnnotation(defn.TailrecAnnot)) {
101-
val ref1 = ref.copySymDenotation()
105+
ref1 = ref.copySymDenotation()
102106
ref1.removeAnnotation(defn.TailrecAnnot)
103-
ref1
104107
}
105108
else if (ref.isConstructor && isDerivedValueClass(ref.owner) && ref.is(AccessFlags)) {
106-
val ref1 = ref.copySymDenotation()
109+
ref1 = ref.copySymDenotation()
107110
ref1.resetFlag(AccessFlags)
108-
ref1
109111
}
110-
else ref
112+
// Drop the Local flag from all private[this] and protected[this] members
113+
// that will be moved to the companion object.
114+
if (ref.is(Local) && isDerivedValueClass(ref.owner)) {
115+
if (ref1 ne ref) ref1.resetFlag(Local)
116+
else ref1 = ref1.copySymDenotation(initFlags = ref1.flags &~ Local)
117+
}
118+
ref1
111119
case _ =>
112120
ref
113121
}

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: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,26 +45,38 @@ import ValueClasses._
4545
* --> p.x_=(e)
4646
*
4747
* No fields are generated yet. This is done later in phase Memoize.
48+
*
49+
* Also, drop the Local flag from all private[this] and protected[this] members.
50+
* This allows subsequent code motions in Flatten.
4851
*/
4952
class Getters extends MiniPhase with SymTransformer {
5053
import ast.tpd._
5154

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

5457
override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = {
5558
def noGetterNeeded =
5659
d.is(NoGetterNeeded) ||
57-
d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) && !isDerivedValueClass(d.owner) && !d.is(Flags.Lazy) ||
60+
d.is(PrivateLocal) && !d.owner.is(Trait) && !isDerivedValueClass(d.owner) && !d.is(Flags.Lazy) ||
5861
d.is(Module) && d.isStatic ||
5962
d.hasAnnotation(defn.ScalaStaticAnnot) ||
6063
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))
64+
65+
var d1 =
66+
if (d.isTerm && (d.is(Lazy) || d.owner.isClass) && d.info.isValueType && !noGetterNeeded) {
67+
val maybeStable = if (d.isStable) Stable else EmptyFlags
68+
d.copySymDenotation(
69+
initFlags = d.flags | maybeStable | AccessorCreationFlags,
70+
info = ExprType(d.info))
71+
}
72+
else d
73+
74+
// Drop the Local flag from all private[this] and protected[this] members.
75+
if (d1.is(Local)) {
76+
if (d1 ne d) d1.resetFlag(Local)
77+
else d1 = d1.copySymDenotation(initFlags = d1.flags &~ Local)
6678
}
67-
else d
79+
d1
6880
}
6981
private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic
7082

@@ -74,3 +86,7 @@ class Getters extends MiniPhase with SymTransformer {
7486
override def transformAssign(tree: Assign)(implicit ctx: Context): Tree =
7587
if (tree.lhs.symbol is Method) tree.lhs.becomes(tree.rhs).withPos(tree.pos) else tree
7688
}
89+
90+
object Getters {
91+
val name = "getters"
92+
}

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)