Skip to content

Commit 1b299b3

Browse files
authored
Merge pull request #15737 from dotty-staging/fix-15723
Fix treatment of parameter selections via this in constructors.
2 parents 837b1cc + 4710941 commit 1b299b3

File tree

6 files changed

+88
-10
lines changed

6 files changed

+88
-10
lines changed

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -149,17 +149,26 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
149149
// (2) If the parameter accessor reference was to an alias getter,
150150
// drop the () when replacing by the parameter.
151151
object intoConstr extends TreeMap {
152-
private var isSuperCall = false
152+
private var inSuperCall = false
153153
override def transform(tree: Tree)(using Context): Tree = tree match {
154154
case Ident(_) | Select(This(_), _) =>
155155
var sym = tree.symbol
156-
if sym.is(ParamAccessor) && (!sym.is(Mutable) || isSuperCall)
157-
// Variables need to go through the getter since they might have been updated,
158-
// except if we are in a super call, since then the virtual getter call would
159-
// be illegal.
160-
then
156+
def isOverridableSelect = tree.isInstanceOf[Select] && !sym.isEffectivelyFinal
157+
def switchOutsideSupercall = !sym.is(Mutable) && !isOverridableSelect
158+
// If true, switch to constructor parameters also in the constructor body
159+
// that follows the super call.
160+
// Variables need to go through the getter since they might have been updated.
161+
// References via this need to use the getter as well as long as that getter
162+
// can be overridden. This is needed to handle overrides correctly. See run/i15723.scala.
163+
// Note that in a supercall we need to switch to parameters in any case since then
164+
// calling the virtual getter call would be illegal.
165+
//
166+
// Note: We intentionally treat references via this and identifiers differently
167+
// here. Identifiers in a constructor always bind to the parameter. This is
168+
// done for backwards compatbility.
169+
if sym.is(ParamAccessor) && (switchOutsideSupercall || inSuperCall) then
161170
sym = sym.subst(accessors, paramSyms)
162-
if (sym.maybeOwner.isConstructor) ref(sym).withSpan(tree.span) else tree
171+
if sym.maybeOwner.isConstructor then ref(sym).withSpan(tree.span) else tree
163172
case Apply(fn, Nil) =>
164173
val fn1 = transform(fn)
165174
if ((fn1 ne fn) && fn1.symbol.is(Param) && fn1.symbol.owner.isPrimaryConstructor)
@@ -170,7 +179,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
170179
}
171180

172181
def apply(tree: Tree, prevOwner: Symbol)(using Context): Tree =
173-
isSuperCall = isSuperConstrCall(tree)
182+
inSuperCall = isSuperConstrCall(tree)
174183
transform(tree).changeOwnerAfter(prevOwner, constr.symbol, thisPhase)
175184
}
176185

tests/pos/fewer-braces.scala

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import language.experimental.fewerBraces
22

3-
object Test:
3+
def Test =
4+
5+
val xo: Option[Int] = Some(1)
6+
7+
val y =
8+
xo.fold:
9+
22
10+
.apply: x =>
11+
x + 1
12+
println(y)
413

5-
assert((new Object: Any).isInstanceOf[Object])

tests/run/i15723.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
20
2+
20

tests/run/i15723.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class B(val y: Int) {
2+
println(this.y)
3+
foo()
4+
def foo() = println(this.y)
5+
}
6+
class C(override val y: Int) extends B(10)
7+
8+
object Test extends App {
9+
new C(20)
10+
}

tests/run/patmat.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Bob is 22 years old and lives in Paris
2+
Hello Peter
3+
Bob is 22 years old and lives in Paris
4+
Hello PersonExtractor(Peter)

tests/run/patmat.scala

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
object Test1:
2+
class User(val name: String, val age: Int, val city: String)
3+
object User:
4+
def unapply(user: User) = UserExtractor(user.name, user.age, user.city)
5+
6+
case class UserExtractor(name: String, age: Int, city: String)
7+
8+
class Person(val name: String)
9+
object Person:
10+
def unapply(person: Person) = PersonExtractor(person.name)
11+
12+
case class PersonExtractor(name: String)
13+
14+
def test =
15+
val user = User("Bob", 22, "Paris")
16+
(user: Any) match
17+
case User(name, age, city) => println(s"$name is $age years old and lives in $city")
18+
val p = Person("Peter")
19+
(p: Any) match
20+
case Person(n) => println(s"Hello $n")
21+
22+
object Test2:
23+
class User(val name: String, val age: Int, val city: String)
24+
object User:
25+
def unapply(user: User): Option[UserExtractor] = Some(UserExtractor(user.name, user.age, user.city))
26+
27+
case class UserExtractor(name: String, age: Int, city: String)
28+
29+
class Person(val name: String)
30+
object Person:
31+
def unapply(person: Person): Some[PersonExtractor] = Some(PersonExtractor(person.name))
32+
33+
case class PersonExtractor(name: String)
34+
35+
def test =
36+
val user = User("Bob", 22, "Paris")
37+
(user: Any) match
38+
case User(name, age, city) => println(s"$name is $age years old and lives in $city")
39+
val p = Person("Peter")
40+
(p: Any) match
41+
case Person(n) => println(s"Hello $n")
42+
43+
@main def Test =
44+
Test1.test
45+
Test2.test

0 commit comments

Comments
 (0)