diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index e0791f313970..2c4211e223ca 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -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 @@ -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) diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 0bdf3001bf26..20e138eb1e26 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -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}") @@ -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) @@ -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 * * ``` diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 662edd331a98..98dbf4859d92 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -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"), @@ -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"), @@ -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"), diff --git a/stdlib-bootstrapped-tasty-tests/src/Main.scala b/stdlib-bootstrapped-tasty-tests/src/Main.scala index 6b82f97c34cb..b579baf6513d 100644 --- a/stdlib-bootstrapped-tasty-tests/src/Main.scala +++ b/stdlib-bootstrapped-tasty-tests/src/Main.scala @@ -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() = {