Skip to content

Do not generate _N members for case classes in stdlib-bootstrapped #18117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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