Skip to content

Commit

Permalink
Do not generate _N members for case classes in stdlib-bootstrapped (#…
Browse files Browse the repository at this point in the history
…18117)

Follow-up of #17928
  • Loading branch information
smarter authored Jul 13, 2023
2 parents a9f4008 + df3968b commit f4aa793
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 31 deletions.
38 changes: 21 additions & 17 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -667,18 +667,20 @@ object desugar {
DefDef(name, Nil, tpt, rhs).withMods(synthetic)

def productElemMeths =
val caseParams = derivedVparamss.head.toArray
val selectorNamesInBody = normalizedBody.collect {
case vdef: ValDef if vdef.name.isSelectorName =>
vdef.name
case ddef: DefDef if ddef.name.isSelectorName && ddef.paramss.isEmpty =>
ddef.name
}
for i <- List.range(0, arity)
selName = nme.selectorName(i)
if (selName ne caseParams(i).name) && !selectorNamesInBody.contains(selName)
yield syntheticProperty(selName, caseParams(i).tpt,
Select(This(EmptyTypeIdent), caseParams(i).name))
if caseClassInScala2StdLib then Nil
else
val caseParams = derivedVparamss.head.toArray
val selectorNamesInBody = normalizedBody.collect {
case vdef: ValDef if vdef.name.isSelectorName =>
vdef.name
case ddef: DefDef if ddef.name.isSelectorName && ddef.paramss.isEmpty =>
ddef.name
}
for i <- List.range(0, arity)
selName = nme.selectorName(i)
if (selName ne caseParams(i).name) && !selectorNamesInBody.contains(selName)
yield syntheticProperty(selName, caseParams(i).tpt,
Select(This(EmptyTypeIdent), caseParams(i).name))

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

val hasRepeatedParam = constrVparamss.head.exists {
case ValDef(_, tpt, _) => isRepeated(tpt)
Expand Down
33 changes: 31 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
case nme.productArity => Literal(Constant(accessors.length))
case nme.productPrefix if isEnumValue => nameRef
case nme.productPrefix => ownName
case nme.productElement => productElementBody(accessors.length, vrefss.head.head)
case nme.productElement =>
if ctx.settings.Yscala2Stdlib.value then productElementBodyForScala2Compat(accessors.length, vrefss.head.head)
else productElementBody(accessors.length, vrefss.head.head)
case nme.productElementName => productElementNameBody(accessors.length, vrefss.head.head)
}
report.log(s"adding $synthetic to $clazz at ${ctx.phase}")
Expand All @@ -185,7 +187,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
* ```
*/
def productElementBody(arity: Int, index: Tree)(using Context): Tree = {
// case N => _${N + 1}
// case N => this._${N + 1}
val cases = 0.until(arity).map { i =>
val sel = This(clazz).select(nme.selectorName(i), _.info.isParameterless)
CaseDef(Literal(Constant(i)), EmptyTree, sel)
Expand All @@ -194,6 +196,33 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
Match(index, (cases :+ generateIOBECase(index)).toList)
}

/** The class
*
* ```
* case class C(x: T, y: T)
* ```
*
* gets the `productElement` method:
*
* ```
* def productElement(index: Int): Any = index match {
* case 0 => this.x
* case 1 => this.y
* case _ => throw new IndexOutOfBoundsException(index.toString)
* }
* ```
*/
def productElementBodyForScala2Compat(arity: Int, index: Tree)(using Context): Tree = {
val caseParams = ctx.owner.owner.caseAccessors
// case N => this.${paramNames(N)}
val cases = caseParams.zipWithIndex.map { (caseParam, i) =>
val sel = This(clazz).select(caseParam)
CaseDef(Literal(Constant(i)), EmptyTree, sel)
}

Match(index, (cases :+ generateIOBECase(index)).toList)
}

/** The class
*
* ```
Expand Down
19 changes: 8 additions & 11 deletions project/MiMaFilters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,18 @@ object MiMaFilters {
ProblemFilters.exclude[MissingTypesProblem]("scala.UninitializedFieldError$"),
ProblemFilters.exclude[MissingTypesProblem]("scala.collection.StringView$"),

// Tuples
ProblemFilters.exclude[FinalClassProblem]("scala.Tuple1"),
ProblemFilters.exclude[FinalClassProblem]("scala.Tuple2"),
ProblemFilters.exclude[MissingFieldProblem]("scala.Tuple*._*"), // Tuple1._1, Tuple2._1, Tuple2._2

// Scala 2 intrinsic macros
ProblemFilters.exclude[FinalMethodProblem]("scala.StringContext.s"),

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


// Specialization?
ProblemFilters.exclude[MissingFieldProblem]("scala.Tuple1._1"), // field _1 in class scala.Tuple1 does not have a correspondent in current version
ProblemFilters.exclude[MissingFieldProblem]("scala.Tuple2._1"), // field _1 in class scala.Tuple2 does not have a correspondent in current version
ProblemFilters.exclude[MissingFieldProblem]("scala.Tuple2._2"), // field _2 in class scala.Tuple2 does not have a correspondent in current version

// Scala 2 specialization
ProblemFilters.exclude[MissingClassProblem]("scala.*$sp"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.*$sp"),
Expand All @@ -97,6 +98,8 @@ object MiMaFilters {
ProblemFilters.exclude[MissingTypesProblem]("scala.jdk.IntAccumulator"),
ProblemFilters.exclude[MissingTypesProblem]("scala.jdk.LongAccumulator"),
ProblemFilters.exclude[FinalClassProblem]("scala.collection.ArrayOps$ReverseIterator"),
ProblemFilters.exclude[FinalClassProblem]("scala.Tuple1"),
ProblemFilters.exclude[FinalClassProblem]("scala.Tuple2"),

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

// Case class product accessors
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.*._1"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.*._2"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.*._3"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.*._4"),

// abstract method elemTag()scala.reflect.ClassTag in class scala.collection.mutable.ArraySeq does not have a correspondent in other version
ProblemFilters.exclude[DirectAbstractMethodProblem]("scala.collection.immutable.ArraySeq.elemTag"),
ProblemFilters.exclude[DirectAbstractMethodProblem]("scala.collection.mutable.ArraySeq.elemTag"),
Expand Down
2 changes: 1 addition & 1 deletion stdlib-bootstrapped-tasty-tests/src/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ object HelloWorld:

def testScala2CaseClassUnderscoreMembers() = {
val some: Some[Int] = Some(1)
// FIXME: assert(!typeChecks("some._1"))
assert(!typeChecks("some._1"))
}

def testScalaNumberUnderlying() = {
Expand Down

0 comments on commit f4aa793

Please sign in to comment.