Skip to content

Commit 673ad9b

Browse files
committed
Fix scala#4985: Generate productElementName for case classes
1 parent dab02ed commit 673ad9b

File tree

6 files changed

+116
-3
lines changed

6 files changed

+116
-3
lines changed

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ object desugar {
457457
DefDef(name, Nil, Nil, tpt, rhs).withMods(synthetic)
458458
def productElemMeths = {
459459
val caseParams = derivedVparamss.head.toArray
460-
for (i <- 0 until arity if nme.selectorName(i) `ne` caseParams(i).name)
460+
for (i <- List.range(0, arity) if nme.selectorName(i) `ne` caseParams(i).name)
461461
yield syntheticProperty(nme.selectorName(i), caseParams(i).tpt,
462462
Select(This(EmptyTypeIdent), caseParams(i).name))
463463
}
@@ -483,9 +483,17 @@ object desugar {
483483
.withFlags(Synthetic | constr1.mods.flags & copiedAccessFlags) :: Nil
484484
}
485485
}
486+
def productElemNameMethod = {
487+
val methodParam = makeSyntheticParameter(tpt = scalaDot(tpnme.Int))
488+
val patternMatchCases = derivedVparamss.head.zipWithIndex.map { case (param, idx) =>
489+
CaseDef(Literal(Constant(idx)), EmptyTree, Block(Nil, Literal(Constant(param.name.decode.toString))))
490+
}
491+
val body = Match(Ident(methodParam.name), patternMatchCases)
492+
DefDef(nme.productElementName, Nil, List(List(methodParam)), javaString, body)
493+
}
486494

487495
if (isCaseClass)
488-
copyMeths ::: enumTagMeths ::: productElemMeths.toList
496+
productElemNameMethod :: copyMeths ::: enumTagMeths ::: productElemMeths
489497
else Nil
490498
}
491499

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
356356
def scalaDot(name: Name): Select = Select(rootDot(nme.scala_), name)
357357
def scalaUnit: Select = scalaDot(tpnme.Unit)
358358
def scalaAny: Select = scalaDot(tpnme.Any)
359+
def javaString: Select = Select(Select(Ident(nme.java), nme.lang), tpnme.String)
359360

360361
def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef =
361362
DefDef(nme.CONSTRUCTOR, tparams, vparamss, TypeTree(), rhs)

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,7 @@ object StdNames {
483483
val prefix : N = "prefix"
484484
val productArity: N = "productArity"
485485
val productElement: N = "productElement"
486+
val productElementName: N = "productElementName"
486487
val productIterator: N = "productIterator"
487488
val productPrefix: N = "productPrefix"
488489
val raw_ : N = "raw"

tests/run/productElementName.check

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
User(name=Susan, age=42)
2+
ユーザ(名前=Susan, 年齢=42)
3+
U$er(na$me=Susan, a$ge=42)
4+
type(for=Susan, if=42)
5+
contains spaces(first param=Susan, second param=42)
6+
Symbols(::=Susan, ||=42)
7+
MultipleParamLists(a=Susan, b=42)
8+
AuxiliaryConstructor(a=Susan, b=42)
9+
OverloadedApply(a=Susan, b=123)
10+
PrivateMembers(a=10, b=20, c=30, d=40, e=50, f=60)

tests/run/productElementName.scala

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// These methods are not yet on Product.scala (added in 2.13.x)
2+
trait Product2_13 extends Product {
3+
def productElementName(n: Int): String
4+
5+
/** An iterator over the names of all the elements of this product.
6+
*/
7+
def productElementNames: Iterator[String] = new scala.collection.AbstractIterator[String] {
8+
private[this] var c: Int = 0
9+
private[this] val cmax = productArity
10+
11+
def hasNext = c < cmax
12+
13+
def next() = {
14+
val result = productElementName(c); c += 1; result
15+
}
16+
}
17+
}
18+
19+
case class User(name: String, age: Int) extends Product2_13
20+
21+
case class ーザ(名前: String, 年齢: Int) extends Product2_13
22+
23+
case class U$er(na$me: String, a$ge: Int) extends Product2_13
24+
25+
case class `type`(`for`: String, `if`: Int) extends Product2_13
26+
27+
case class `contains spaces`(`first param`: String, `second param`: Int) extends Product2_13
28+
29+
case class Symbols(:: : String, || : Int) extends Product2_13
30+
31+
case class MultipleParamLists(a: String, b: Int)(c: Boolean) extends Product2_13
32+
33+
case class AuxiliaryConstructor(a: String, b: Int) extends Product2_13 {
34+
def this(x: String) = {
35+
this(x, 123)
36+
}
37+
}
38+
39+
case class OverloadedApply(a: String, b: Int) extends Product2_13
40+
object OverloadedApply {
41+
def apply(x: String): OverloadedApply =
42+
new OverloadedApply(x, 123)
43+
}
44+
45+
//case class DefinesProductElementName(a: String, b: Int) extends Product2_13 {
46+
// override def productElementName(n: Int): String = "foo"
47+
//}
48+
49+
//trait A {
50+
// override def productElementName(n: Int): String = "overriden"
51+
//}
52+
//case class InheritsProductElementName(a: String, b: Int) extends A
53+
//
54+
//trait B extends Product2_13 {
55+
// override def productElementName(n: Int): String = "overriden"
56+
//}
57+
//case class InheritsProductElementName_Override(a: String, b: Int) extends B
58+
//
59+
//trait C { self: Product =>
60+
// override def productElementName(n: Int): String = "overriden"
61+
//}
62+
//case class InheritsProductElementName_Override_SelfType(a: String, b: Int) extends C
63+
64+
case class PrivateMembers(a: Int, private val b: Int, c: Int, private val d: Int, e: Int, private val f: Int) extends Product2_13
65+
66+
object Test extends App {
67+
def pretty(p: Product2_13): String =
68+
p.productElementNames.zip(p.productIterator)
69+
.map { case (name, value) => s"$name=$value" }
70+
.mkString(p.productPrefix + "(", ", ", ")")
71+
72+
println(pretty(User("Susan", 42)))
73+
println(pretty(ユーザ("Susan", 42)))
74+
println(pretty(U$er("Susan", 42)))
75+
println(pretty(`type`("Susan", 42)))
76+
println(pretty(`contains spaces`("Susan", 42)))
77+
println(pretty(Symbols("Susan", 42)))
78+
println(pretty(MultipleParamLists("Susan", 42)(true)))
79+
println(pretty(AuxiliaryConstructor("Susan", 42)))
80+
println(pretty(OverloadedApply("Susan")))
81+
// println(pretty(DefinesProductElementName("Susan", 42)))
82+
83+
// // uses the synthetic, not the one defined in the trait
84+
// println(pretty(InheritsProductElementName("Susan", 42)))
85+
//
86+
// // uses the override defined in the trait
87+
// println(pretty(InheritsProductElementName_Override("Susan", 42)))
88+
//
89+
// // uses the synthetic, not the one defined in the trait
90+
// println(pretty(InheritsProductElementName_Override_SelfType("Susan", 42)))
91+
92+
println(pretty(PrivateMembers(10, 20, 30, 40, 50, 60)))
93+
}

tests/run/tasty-extractors-2.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Type.SymRef(IsClassSymbol(<scala.Unit>), Type.ThisType(Type.SymRef(IsPackageSymb
4949
Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("<init>", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), "<init>"), Nil)), None, List(DefDef("a", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0))))))), Term.Literal(Constant.Unit())))
5050
Type.SymRef(IsClassSymbol(<scala.Unit>), Type.ThisType(Type.SymRef(IsPackageSymbol(<scala>), NoPrefix())))
5151

52-
Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("<init>", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), "<init>"), Nil), TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Product")), None, List(DefDef("copy", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), "<init>"), Nil))), DefDef("hashCode", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Literal(Constant.Int(394005536)))), DefDef("equals", Nil, List(List(ValDef("x$0", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.Apply(Term.Select(Term.This(Some(Id("Foo"))), "eq"), List(Term.TypeApply(Term.Select(Term.Ident("x$0"), "asInstanceOf"), List(TypeTree.Inferred())))), "||"), List(Term.Match(Term.Ident("x$0"), List(CaseDef(Pattern.Bind("x$0", Pattern.TypeTest(TypeTree.Inferred())), None, Term.Literal(Constant.Boolean(true))), CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Literal(Constant.Boolean(false))))))))), DefDef("toString", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Ident("_toString"), List(Term.This(Some(Id("Foo"))))))), DefDef("canEqual", Nil, List(List(ValDef("that", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.TypeApply(Term.Select(Term.Ident("that"), "isInstanceOf"), List(TypeTree.Inferred())))), DefDef("productArity", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0)))), DefDef("productPrefix", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.String("Foo")))), DefDef("productElement", Nil, List(List(ValDef("n", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Match(Term.Ident("n"), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Apply(Term.Ident("throw"), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), "<init>"), List(Term.Apply(Term.Select(Term.Ident("n"), "toString"), Nil)))))))))))), ValDef("Foo", TypeTree.Ident("Foo$"), Some(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo$")), "<init>"), Nil))), ClassDef("Foo$", DefDef("<init>", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), "<init>"), Nil), TypeTree.Applied(TypeTree.Inferred(), List(TypeTree.Inferred()))), Some(ValDef("_", TypeTree.Singleton(Term.Ident("Foo")), None)), List(DefDef("apply", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), "<init>"), Nil))), DefDef("unapply", Nil, List(List(ValDef("x$1", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Literal(Constant.Boolean(true))))))), Term.Literal(Constant.Unit())))
52+
Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("<init>", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), "<init>"), Nil), TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Product")), None, List(DefDef("productElementName", Nil, List(List(ValDef("x$1", TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Int"), None))), TypeTree.Select(Term.Select(Term.Ident("java"), "lang"), "String"), Some(Term.Match(Term.Ident("x$1"), Nil))), DefDef("copy", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), "<init>"), Nil))), DefDef("hashCode", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Literal(Constant.Int(394005536)))), DefDef("equals", Nil, List(List(ValDef("x$0", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.Apply(Term.Select(Term.This(Some(Id("Foo"))), "eq"), List(Term.TypeApply(Term.Select(Term.Ident("x$0"), "asInstanceOf"), List(TypeTree.Inferred())))), "||"), List(Term.Match(Term.Ident("x$0"), List(CaseDef(Pattern.Bind("x$0", Pattern.TypeTest(TypeTree.Inferred())), None, Term.Literal(Constant.Boolean(true))), CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Literal(Constant.Boolean(false))))))))), DefDef("toString", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Ident("_toString"), List(Term.This(Some(Id("Foo"))))))), DefDef("canEqual", Nil, List(List(ValDef("that", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.TypeApply(Term.Select(Term.Ident("that"), "isInstanceOf"), List(TypeTree.Inferred())))), DefDef("productArity", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0)))), DefDef("productPrefix", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.String("Foo")))), DefDef("productElement", Nil, List(List(ValDef("n", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Match(Term.Ident("n"), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Apply(Term.Ident("throw"), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), "<init>"), List(Term.Apply(Term.Select(Term.Ident("n"), "toString"), Nil)))))))))))), ValDef("Foo", TypeTree.Ident("Foo$"), Some(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo$")), "<init>"), Nil))), ClassDef("Foo$", DefDef("<init>", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), "<init>"), Nil), TypeTree.Applied(TypeTree.Inferred(), List(TypeTree.Inferred()))), Some(ValDef("_", TypeTree.Singleton(Term.Ident("Foo")), None)), List(DefDef("apply", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), "<init>"), Nil))), DefDef("unapply", Nil, List(List(ValDef("x$1", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Literal(Constant.Boolean(true))))))), Term.Literal(Constant.Unit())))
5353
Type.SymRef(IsClassSymbol(<scala.Unit>), Type.ThisType(Type.SymRef(IsPackageSymbol(<scala>), NoPrefix())))
5454

5555
Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo1", DefDef("<init>", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), "<init>"), Nil)), None, List(ValDef("a", TypeTree.Inferred(), None)))), Term.Literal(Constant.Unit())))

0 commit comments

Comments
 (0)