Skip to content

Commit d9e0f5f

Browse files
authored
Merge pull request scala#6972 from cb372/productElementName
Product element name
2 parents 240cbcd + efc4821 commit d9e0f5f

File tree

12 files changed

+158
-36
lines changed

12 files changed

+158
-36
lines changed

src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ trait SyntheticMethods extends ast.TreeDSL {
3838
import definitions._
3939
import CODE._
4040

41-
private lazy val productSymbols = List(Product_productPrefix, Product_productArity, Product_productElement, Product_iterator, Product_canEqual)
41+
private lazy val productSymbols = List(Product_productPrefix, Product_productArity, Product_productElement, Product_productElementName, Product_iterator, Product_canEqual)
4242
private lazy val valueSymbols = List(Any_hashCode, Any_equals)
4343
private lazy val caseSymbols = List(Object_hashCode, Object_toString) ::: productSymbols
4444
private lazy val caseValueSymbols = Any_toString :: valueSymbols ::: productSymbols
@@ -117,11 +117,13 @@ trait SyntheticMethods extends ast.TreeDSL {
117117
)
118118
}
119119

120-
/* Common code for productElement and (currently disabled) productElementName */
121120
def perElementMethod(name: Name, returnType: Type)(caseFn: Symbol => Tree): Tree =
122121
createSwitchMethod(name, accessors.indices, returnType)(idx => caseFn(accessors(idx)))
123122

124-
// def productElementNameMethod = perElementMethod(nme.productElementName, StringTpe)(x => LIT(x.name.toString))
123+
def productElementNameMethod = {
124+
val constrParamAccessors = clazz.constrParamAccessors
125+
createSwitchMethod(nme.productElementName, constrParamAccessors.indices, StringTpe)(idx => LIT(constrParamAccessors(idx).name.dropLocal.decode))
126+
}
125127

126128
var syntheticCanEqual = false
127129

@@ -248,14 +250,12 @@ trait SyntheticMethods extends ast.TreeDSL {
248250
// methods for both classes and objects
249251
def productMethods = {
250252
List(
251-
Product_productPrefix -> (() => constantNullary(nme.productPrefix, clazz.name.decode)),
252-
Product_productArity -> (() => constantNullary(nme.productArity, arity)),
253-
Product_productElement -> (() => perElementMethod(nme.productElement, AnyTpe)(mkThisSelect)),
254-
Product_iterator -> (() => productIteratorMethod),
255-
Product_canEqual -> (() => canEqualMethod)
256-
// This is disabled pending a reimplementation which doesn't add any
257-
// weight to case classes (i.e. inspects the bytecode.)
258-
// Product_productElementName -> (() => productElementNameMethod(accessors)),
253+
Product_productPrefix -> (() => constantNullary(nme.productPrefix, clazz.name.decode)),
254+
Product_productArity -> (() => constantNullary(nme.productArity, arity)),
255+
Product_productElement -> (() => perElementMethod(nme.productElement, AnyTpe)(mkThisSelect)),
256+
Product_productElementName -> (() => productElementNameMethod),
257+
Product_iterator -> (() => productIteratorMethod),
258+
Product_canEqual -> (() => canEqualMethod)
259259
)
260260
}
261261

src/library/scala/Product.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,24 @@ trait Product extends Any with Equals {
4848
* @return in the default implementation, the empty string
4949
*/
5050
def productPrefix = ""
51+
52+
/** The name of the n^th^ element of this product, 0-based.
53+
* In the default implementation, an empty string.
54+
*
55+
* @param n the index of the element name to return
56+
* @throws IndexOutOfBoundsException
57+
* @return the name of the specified element
58+
*/
59+
def productElementName(n: Int): String =
60+
if (n >= 0 && n < productArity) ""
61+
else throw new IndexOutOfBoundsException(n.toString)
62+
63+
/** An iterator over the names of all the elements of this product.
64+
*/
65+
def productElementNames: Iterator[String] = new scala.collection.AbstractIterator[String] {
66+
private[this] var c: Int = 0
67+
private[this] val cmax = productArity
68+
def hasNext = c < cmax
69+
def next() = { val result = productElementName(c); c += 1; result }
70+
}
5171
}

src/reflect/scala/reflect/internal/Definitions.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,7 @@ trait Definitions extends api.StandardDefinitions {
749749
lazy val ProductRootClass: ClassSymbol = requiredClass[scala.Product]
750750
def Product_productArity = getMemberMethod(ProductRootClass, nme.productArity)
751751
def Product_productElement = getMemberMethod(ProductRootClass, nme.productElement)
752+
def Product_productElementName = getMemberMethod(ProductRootClass, nme.productElementName)
752753
def Product_iterator = getMemberMethod(ProductRootClass, nme.productIterator)
753754
def Product_productPrefix = getMemberMethod(ProductRootClass, nme.productPrefix)
754755
def Product_canEqual = getMemberMethod(ProductRootClass, nme.canEqual_)

src/reflect/scala/reflect/internal/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,7 @@ trait StdNames {
771771
val prefix : NameType = "prefix"
772772
val productArity: NameType = "productArity"
773773
val productElement: NameType = "productElement"
774+
val productElementName: NameType = "productElementName"
774775
val productIterator: NameType = "productIterator"
775776
val productPrefix: NameType = "productPrefix"
776777
val raw_ : NameType = "raw"

test/files/run/idempotency-case-classes.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ C(2,3)
2020
case 1 => C.this.y
2121
case _ => throw new IndexOutOfBoundsException(x$1.toString())
2222
};
23+
override <synthetic> def productElementName(x$1: Int): String = x$1 match {
24+
case 0 => "x"
25+
case 1 => "y"
26+
case _ => throw new IndexOutOfBoundsException(x$1.toString())
27+
};
2328
override <synthetic> def productIterator: Iterator[Any] = scala.runtime.ScalaRunTime.typedProductIterator[Any](C.this);
2429
<synthetic> def canEqual(x$1: Any): Boolean = x$1.$isInstanceOf[C]();
2530
override <synthetic> def hashCode(): Int = {

test/files/run/macroPlugins-namerHooks.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ enterSym(super.<init>())
3737
enterStat(super.<init>())
3838
enterSym(case <synthetic> val x1: Int = x$1)
3939
enterStat(case <synthetic> val x1: Int = x$1)
40+
enterSym(case <synthetic> val x1: Int = x$1)
41+
enterStat(case <synthetic> val x1: Int = x$1)
4042
enterSym(case <synthetic> val x1: Any = x$1)
4143
enterSym(case5(){ if (x1.isInstanceOf[C]) matchEnd4(true) else case6() })
4244
enterSym(case6(){ matchEnd4(false) })
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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+
DefinesProductElementName(foo=Susan, foo=42)
11+
InheritsProductElementName(a=Susan, b=42)
12+
InheritsProductElementName_Override(overriden=Susan, overriden=42)
13+
InheritsProductElementName_Override_SelfType(a=Susan, b=42)
14+
PrivateMembers(a=10, b=20, c=30, d=40, e=50, f=60)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
2+
case class User(name: String, age: Int)
3+
4+
case class ーザ(名前: String, 年齢: Int)
5+
6+
case class U$er(na$me: String, a$ge: Int)
7+
8+
case class `type`(`for`: String, `if`: Int)
9+
10+
case class `contains spaces`(`first param`: String, `second param`: Int)
11+
12+
case class Symbols(:: : String, || : Int)
13+
14+
case class MultipleParamLists(a: String, b: Int)(c: Boolean)
15+
16+
case class AuxiliaryConstructor(a: String, b: Int) {
17+
def this(x: String) {
18+
this(x, 123)
19+
}
20+
}
21+
22+
case class OverloadedApply(a: String, b: Int)
23+
object OverloadedApply {
24+
def apply(x: String): OverloadedApply =
25+
new OverloadedApply(x, 123)
26+
}
27+
28+
case class DefinesProductElementName(a: String, b: Int) {
29+
override def productElementName(n: Int): String = "foo"
30+
}
31+
32+
trait A {
33+
def productElementName(n: Int): String = "overriden"
34+
}
35+
case class InheritsProductElementName(a: String, b: Int) extends A
36+
37+
trait B extends Product {
38+
override def productElementName(n: Int): String = "overriden"
39+
}
40+
case class InheritsProductElementName_Override(a: String, b: Int) extends B
41+
42+
trait C { self: Product =>
43+
override def productElementName(n: Int): String = "overriden"
44+
}
45+
case class InheritsProductElementName_Override_SelfType(a: String, b: Int) extends C
46+
47+
case class PrivateMembers(a: Int, private val b: Int, c: Int, private val d: Int, e: Int, private val f: Int)
48+
49+
object Test extends App {
50+
def pretty(p: Product): String =
51+
p.productElementNames.zip(p.productIterator)
52+
.map { case (name, value) => s"$name=$value" }
53+
.mkString(p.productPrefix + "(", ", ", ")")
54+
55+
println(pretty(User("Susan", 42)))
56+
println(pretty(ユーザ("Susan", 42)))
57+
println(pretty(U$er("Susan", 42)))
58+
println(pretty(`type`("Susan", 42)))
59+
println(pretty(`contains spaces`("Susan", 42)))
60+
println(pretty(Symbols("Susan", 42)))
61+
println(pretty(MultipleParamLists("Susan", 42)(true)))
62+
println(pretty(AuxiliaryConstructor("Susan", 42)))
63+
println(pretty(OverloadedApply("Susan")))
64+
println(pretty(DefinesProductElementName("Susan", 42)))
65+
66+
// uses the synthetic, not the one defined in the trait
67+
println(pretty(InheritsProductElementName("Susan", 42)))
68+
69+
// uses the override defined in the trait
70+
println(pretty(InheritsProductElementName_Override("Susan", 42)))
71+
72+
// uses the synthetic, not the one defined in the trait
73+
println(pretty(InheritsProductElementName_Override_SelfType("Susan", 42)))
74+
75+
println(pretty(PrivateMembers(10, 20, 30, 40, 50, 60)))
76+
}
77+

test/files/scalap/caseClass.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ case class CaseClass[A <: scala.Seq[scala.Int]](i: A, s: scala.Predef.String) ex
66
override def productPrefix: java.lang.String = { /* compiled code */ }
77
def productArity: scala.Int = { /* compiled code */ }
88
def productElement(x$1: scala.Int): scala.Any = { /* compiled code */ }
9+
override def productElementName(x$1: scala.Int): java.lang.String = { /* compiled code */ }
910
override def productIterator: scala.collection.Iterator[scala.Any] = { /* compiled code */ }
1011
def canEqual(x$1: scala.Any): scala.Boolean = { /* compiled code */ }
1112
override def hashCode(): scala.Int = { /* compiled code */ }

test/files/scalap/caseObject.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ case object CaseObject extends scala.AnyRef with scala.Product with scala.Serial
33
override def productPrefix: java.lang.String = { /* compiled code */ }
44
def productArity: scala.Int = { /* compiled code */ }
55
def productElement(x$1: scala.Int): scala.Any = { /* compiled code */ }
6+
override def productElementName(x$1: scala.Int): java.lang.String = { /* compiled code */ }
67
override def productIterator: scala.collection.Iterator[scala.Any] = { /* compiled code */ }
78
def canEqual(x$1: scala.Any): scala.Boolean = { /* compiled code */ }
89
override def hashCode(): scala.Int = { /* compiled code */ }

0 commit comments

Comments
 (0)