|
When a case class is type checked, synthetic methods are added,
such as the `hashCode`/`equals`, implementations of the `Product`
interface. At the same time, a case accessor method is added for
each non-public constructor parameter. This the accessor for a
parameter named `x` is named `x$n`, where `n` is a fresh suffix.
This is all done to retain universal pattern-matchability of
case classes, irrespective of access. What is the point of allowing
non-public parameters if pattern matching can subvert access? I
believe it is to enables private setters:
```
case class C(private var x: String)
scala> val x = new C("")
x: C = C()
scala> val c = new C("")
c: C = C()
scala> val C(x) = c
x: String = ""
scala> c.x
<console>:11: error: variable x in class C cannot be accessed in C
c.x
^
scala> c.x = ""
<console>:13: error: variable x in class C cannot be accessed in C
val $ires2 = c.x
^
<console>:10: error: variable x in class C cannot be accessed in C
c.x = ""
^
```
Perhaps I'm missing additional motivations.
If you think scheme sounds like a binary compatiblity nightmare,
you're right: https://issues.scala-lang.org/browse/SI-8944
`caseFieldAccessors` uses the naming convention to find the right
accessor; this in turn is used in pattern match translation.
The accessors are also needed in the synthetic `unapply` method
in the companion object. Here, we must tread lightly to avoid
triggering a typechecking cycles before; the synthesis of that method
is not allowed to force the info of the case class.
Instead, it uses a back channel, `renamedCaseAccessors` to see
which parameters have corresonding accessors.
This is pretty flaky: if the companion object is typechecked
before the case class, it uses the private param accessor directly,
which it happends to have access to, and which duly gets an
expanded name to allow JVM level access. If the companion
appears afterwards, it uses the case accessor method.
In the presentation compiler, it is possible to typecheck a source
file more than once, in which case we can redefine a case class. This
uses the same `Symbol` with a new type completer. Synthetics must
be re-added to its type.
The reported bug occurs when, during the second typecheck, an entry
in `renamedCaseAccessors` directs the unapply method to use `x$1`
before it has been added to the type of the case class symbol.
This commit clears corresponding entries from that map when we
detect that we are redefining a class symbol.
Case accessors are in need of a larger scale refactoring. But I'm
leaving that for SI-8944.
|