Skip to content

Commit f4aa793

Browse files
authored
Do not generate _N members for case classes in stdlib-bootstrapped (#18117)
Follow-up of #17928
2 parents a9f4008 + df3968b commit f4aa793

File tree

4 files changed

+61
-31
lines changed

4 files changed

+61
-31
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -667,18 +667,20 @@ object desugar {
667667
DefDef(name, Nil, tpt, rhs).withMods(synthetic)
668668

669669
def productElemMeths =
670-
val caseParams = derivedVparamss.head.toArray
671-
val selectorNamesInBody = normalizedBody.collect {
672-
case vdef: ValDef if vdef.name.isSelectorName =>
673-
vdef.name
674-
case ddef: DefDef if ddef.name.isSelectorName && ddef.paramss.isEmpty =>
675-
ddef.name
676-
}
677-
for i <- List.range(0, arity)
678-
selName = nme.selectorName(i)
679-
if (selName ne caseParams(i).name) && !selectorNamesInBody.contains(selName)
680-
yield syntheticProperty(selName, caseParams(i).tpt,
681-
Select(This(EmptyTypeIdent), caseParams(i).name))
670+
if caseClassInScala2StdLib then Nil
671+
else
672+
val caseParams = derivedVparamss.head.toArray
673+
val selectorNamesInBody = normalizedBody.collect {
674+
case vdef: ValDef if vdef.name.isSelectorName =>
675+
vdef.name
676+
case ddef: DefDef if ddef.name.isSelectorName && ddef.paramss.isEmpty =>
677+
ddef.name
678+
}
679+
for i <- List.range(0, arity)
680+
selName = nme.selectorName(i)
681+
if (selName ne caseParams(i).name) && !selectorNamesInBody.contains(selName)
682+
yield syntheticProperty(selName, caseParams(i).tpt,
683+
Select(This(EmptyTypeIdent), caseParams(i).name))
682684

683685
def enumCaseMeths =
684686
if isEnumCase then
@@ -768,11 +770,13 @@ object desugar {
768770
val unapplyMeth = {
769771
def scala2LibCompatUnapplyRhs(unapplyParamName: Name) =
770772
assert(arity <= Definitions.MaxTupleArity, "Unexpected case class with tuple larger than 22: "+ cdef.show)
771-
if arity == 1 then Apply(scalaDot(nme.Option), Select(Ident(unapplyParamName), nme._1))
772-
else
773-
val tupleApply = Select(Ident(nme.scala), s"Tuple$arity".toTermName)
774-
val members = List.tabulate(arity) { n => Select(Ident(unapplyParamName), s"_${n+1}".toTermName) }
775-
Apply(scalaDot(nme.Option), Apply(tupleApply, members))
773+
derivedVparamss.head match
774+
case vparam :: Nil =>
775+
Apply(scalaDot(nme.Option), Select(Ident(unapplyParamName), vparam.name))
776+
case vparams =>
777+
val tupleApply = Select(Ident(nme.scala), s"Tuple$arity".toTermName)
778+
val members = vparams.map(vparam => Select(Ident(unapplyParamName), vparam.name))
779+
Apply(scalaDot(nme.Option), Apply(tupleApply, members))
776780

777781
val hasRepeatedParam = constrVparamss.head.exists {
778782
case ValDef(_, tpt, _) => isRepeated(tpt)

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

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
161161
case nme.productArity => Literal(Constant(accessors.length))
162162
case nme.productPrefix if isEnumValue => nameRef
163163
case nme.productPrefix => ownName
164-
case nme.productElement => productElementBody(accessors.length, vrefss.head.head)
164+
case nme.productElement =>
165+
if ctx.settings.Yscala2Stdlib.value then productElementBodyForScala2Compat(accessors.length, vrefss.head.head)
166+
else productElementBody(accessors.length, vrefss.head.head)
165167
case nme.productElementName => productElementNameBody(accessors.length, vrefss.head.head)
166168
}
167169
report.log(s"adding $synthetic to $clazz at ${ctx.phase}")
@@ -185,7 +187,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
185187
* ```
186188
*/
187189
def productElementBody(arity: Int, index: Tree)(using Context): Tree = {
188-
// case N => _${N + 1}
190+
// case N => this._${N + 1}
189191
val cases = 0.until(arity).map { i =>
190192
val sel = This(clazz).select(nme.selectorName(i), _.info.isParameterless)
191193
CaseDef(Literal(Constant(i)), EmptyTree, sel)
@@ -194,6 +196,33 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
194196
Match(index, (cases :+ generateIOBECase(index)).toList)
195197
}
196198

199+
/** The class
200+
*
201+
* ```
202+
* case class C(x: T, y: T)
203+
* ```
204+
*
205+
* gets the `productElement` method:
206+
*
207+
* ```
208+
* def productElement(index: Int): Any = index match {
209+
* case 0 => this.x
210+
* case 1 => this.y
211+
* case _ => throw new IndexOutOfBoundsException(index.toString)
212+
* }
213+
* ```
214+
*/
215+
def productElementBodyForScala2Compat(arity: Int, index: Tree)(using Context): Tree = {
216+
val caseParams = ctx.owner.owner.caseAccessors
217+
// case N => this.${paramNames(N)}
218+
val cases = caseParams.zipWithIndex.map { (caseParam, i) =>
219+
val sel = This(clazz).select(caseParam)
220+
CaseDef(Literal(Constant(i)), EmptyTree, sel)
221+
}
222+
223+
Match(index, (cases :+ generateIOBECase(index)).toList)
224+
}
225+
197226
/** The class
198227
*
199228
* ```

project/MiMaFilters.scala

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,18 @@ object MiMaFilters {
7171
ProblemFilters.exclude[MissingTypesProblem]("scala.UninitializedFieldError$"),
7272
ProblemFilters.exclude[MissingTypesProblem]("scala.collection.StringView$"),
7373

74-
// Tuples
75-
ProblemFilters.exclude[FinalClassProblem]("scala.Tuple1"),
76-
ProblemFilters.exclude[FinalClassProblem]("scala.Tuple2"),
77-
ProblemFilters.exclude[MissingFieldProblem]("scala.Tuple*._*"), // Tuple1._1, Tuple2._1, Tuple2._2
78-
7974
// Scala 2 intrinsic macros
8075
ProblemFilters.exclude[FinalMethodProblem]("scala.StringContext.s"),
8176

8277
// scala.math.Ordering.tryCompare
8378
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.*.tryCompare"),
8479

80+
81+
// Specialization?
82+
ProblemFilters.exclude[MissingFieldProblem]("scala.Tuple1._1"), // field _1 in class scala.Tuple1 does not have a correspondent in current version
83+
ProblemFilters.exclude[MissingFieldProblem]("scala.Tuple2._1"), // field _1 in class scala.Tuple2 does not have a correspondent in current version
84+
ProblemFilters.exclude[MissingFieldProblem]("scala.Tuple2._2"), // field _2 in class scala.Tuple2 does not have a correspondent in current version
85+
8586
// Scala 2 specialization
8687
ProblemFilters.exclude[MissingClassProblem]("scala.*$sp"),
8788
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.*$sp"),
@@ -97,6 +98,8 @@ object MiMaFilters {
9798
ProblemFilters.exclude[MissingTypesProblem]("scala.jdk.IntAccumulator"),
9899
ProblemFilters.exclude[MissingTypesProblem]("scala.jdk.LongAccumulator"),
99100
ProblemFilters.exclude[FinalClassProblem]("scala.collection.ArrayOps$ReverseIterator"),
101+
ProblemFilters.exclude[FinalClassProblem]("scala.Tuple1"),
102+
ProblemFilters.exclude[FinalClassProblem]("scala.Tuple2"),
100103

101104
// other
102105
ProblemFilters.exclude[FinalMethodProblem]("scala.Enumeration.ValueOrdering"),
@@ -168,12 +171,6 @@ object MiMaFilters {
168171
// Companion module class: Missing type java.io.Serializable
169172
ProblemFilters.exclude[MissingTypesProblem]("scala.*$"),
170173

171-
// Case class product accessors
172-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.*._1"),
173-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.*._2"),
174-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.*._3"),
175-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.*._4"),
176-
177174
// abstract method elemTag()scala.reflect.ClassTag in class scala.collection.mutable.ArraySeq does not have a correspondent in other version
178175
ProblemFilters.exclude[DirectAbstractMethodProblem]("scala.collection.immutable.ArraySeq.elemTag"),
179176
ProblemFilters.exclude[DirectAbstractMethodProblem]("scala.collection.mutable.ArraySeq.elemTag"),

stdlib-bootstrapped-tasty-tests/src/Main.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ object HelloWorld:
3737

3838
def testScala2CaseClassUnderscoreMembers() = {
3939
val some: Some[Int] = Some(1)
40-
// FIXME: assert(!typeChecks("some._1"))
40+
assert(!typeChecks("some._1"))
4141
}
4242

4343
def testScalaNumberUnderlying() = {

0 commit comments

Comments
 (0)