diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index b915b199094c..0c44a640ec25 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -457,7 +457,7 @@ object desugar { DefDef(name, Nil, Nil, tpt, rhs).withMods(synthetic) def productElemMeths = { val caseParams = derivedVparamss.head.toArray - for (i <- 0 until arity if nme.selectorName(i) `ne` caseParams(i).name) + for (i <- List.range(0, arity) if nme.selectorName(i) `ne` caseParams(i).name) yield syntheticProperty(nme.selectorName(i), caseParams(i).tpt, Select(This(EmptyTypeIdent), caseParams(i).name)) } @@ -484,8 +484,26 @@ object desugar { } } + // TODO When the Scala library is updated to 2.13.x add the override keyword to this generated method. + // (because Product.scala was updated) + def productElemNameMethod = { + val methodParam = makeSyntheticParameter(tpt = scalaDot(tpnme.Int)) + val paramRef = Ident(methodParam.name) + + val indexAsString = Apply(Select(javaDotLangDot(nme.String), nme.valueOf), paramRef) + val throwOutOfBound = Throw(New(javaDotLangDot(tpnme.IOOBException), List(List(indexAsString)))) + val defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, throwOutOfBound) + + val patternMatchCases = derivedVparamss.head.zipWithIndex.map { case (param, idx) => + CaseDef(Literal(Constant(idx)), EmptyTree, Literal(Constant(param.name.decode.toString))) + } :+ defaultCase + val body = Match(paramRef, patternMatchCases) + DefDef(nme.productElementName, Nil, List(List(methodParam)), javaDotLangDot(tpnme.String), body) + .withFlags(Synthetic) + } + if (isCaseClass) - copyMeths ::: enumTagMeths ::: productElemMeths.toList + productElemNameMethod :: copyMeths ::: enumTagMeths ::: productElemMeths else Nil } diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 88b0dd515bd8..f8f373ca6ba5 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -520,6 +520,7 @@ object Trees { /** selector match { cases } */ case class Match[-T >: Untyped] private[ast] (selector: Tree[T], cases: List[CaseDef[T]]) extends TermTree[T] { + assert(cases.nonEmpty) type ThisTree[-T >: Untyped] = Match[T] def isInline = false } diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 6d23884b0d32..f56cdea95bc5 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -356,6 +356,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def scalaDot(name: Name): Select = Select(rootDot(nme.scala_), name) def scalaUnit: Select = scalaDot(tpnme.Unit) def scalaAny: Select = scalaDot(tpnme.Any) + def javaDotLangDot(name: Name): Select = Select(Select(Ident(nme.java), nme.lang), name) def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef = DefDef(nme.CONSTRUCTOR, tparams, vparamss, TypeTree(), rhs) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 9e0e48374e2b..f618a91271c6 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -203,6 +203,7 @@ object StdNames { final val Serializable: N = "Serializable" final val Singleton: N = "Singleton" final val Throwable: N = "Throwable" + final val IOOBException: N = "IndexOutOfBoundsException" final val ClassfileAnnotation: N = "ClassfileAnnotation" final val ClassManifest: N = "ClassManifest" @@ -483,6 +484,7 @@ object StdNames { val prefix : N = "prefix" val productArity: N = "productArity" val productElement: N = "productElement" + val productElementName: N = "productElementName" val productIterator: N = "productIterator" val productPrefix: N = "productPrefix" val raw_ : N = "raw" diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index de4a5b9b7fd6..ddc97b4e2cd7 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -564,7 +564,8 @@ trait Printers case DefDef(n, _, _, _, _) if d.symbol.owner.flags.isCase => n == "copy" || n.matches("copy\\$default\\$[1-9][0-9]*") || // default parameters for the copy method - n.matches("_[1-9][0-9]*") // Getters from Product + n.matches("_[1-9][0-9]*") || // Getters from Product + n == "productElementName" case _ => false }) } diff --git a/tests/run/productElementName.check b/tests/run/productElementName.check new file mode 100644 index 000000000000..def205de0d2d --- /dev/null +++ b/tests/run/productElementName.check @@ -0,0 +1,11 @@ +User(name=Susan, age=42) +ユーザー(名前=Susan, 年齢=42) +U$er(na$me=Susan, a$ge=42) +type(for=Susan, if=42) +contains spaces(first param=Susan, second param=42) +Symbols(::=Susan, ||=42) +MultipleParamLists(a=Susan, b=42) +AuxiliaryConstructor(a=Susan, b=42) +OverloadedApply(a=Susan, b=123) +PrivateMembers(a=10, b=20, c=30, d=40, e=50, f=60) +NoParams() diff --git a/tests/run/productElementName.scala b/tests/run/productElementName.scala new file mode 100644 index 000000000000..4451c9fad465 --- /dev/null +++ b/tests/run/productElementName.scala @@ -0,0 +1,96 @@ +// These methods are not yet on Product.scala (added in 2.13.x) +trait Product2_13 extends Product { + def productElementName(n: Int): String + + /** An iterator over the names of all the elements of this product. + */ + def productElementNames: Iterator[String] = new scala.collection.AbstractIterator[String] { + private[this] var c: Int = 0 + private[this] val cmax = productArity + + def hasNext = c < cmax + + def next() = { + val result = productElementName(c); c += 1; result + } + } +} + +case class User(name: String, age: Int) extends Product2_13 + +case class ユーザー(名前: String, 年齢: Int) extends Product2_13 + +case class U$er(na$me: String, a$ge: Int) extends Product2_13 + +case class `type`(`for`: String, `if`: Int) extends Product2_13 + +case class `contains spaces`(`first param`: String, `second param`: Int) extends Product2_13 + +case class Symbols(:: : String, || : Int) extends Product2_13 + +case class MultipleParamLists(a: String, b: Int)(c: Boolean) extends Product2_13 + +case class AuxiliaryConstructor(a: String, b: Int) extends Product2_13 { + def this(x: String) = { + this(x, 123) + } +} + +case class OverloadedApply(a: String, b: Int) extends Product2_13 +object OverloadedApply { + def apply(x: String): OverloadedApply = + new OverloadedApply(x, 123) +} + +case class NoParams() extends Product2_13 + +//case class DefinesProductElementName(a: String, b: Int) extends Product2_13 { +// override def productElementName(n: Int): String = "foo" +//} + +//trait A { +// override def productElementName(n: Int): String = "overriden" +//} +//case class InheritsProductElementName(a: String, b: Int) extends A +// +//trait B extends Product2_13 { +// override def productElementName(n: Int): String = "overriden" +//} +//case class InheritsProductElementName_Override(a: String, b: Int) extends B +// +//trait C { self: Product => +// override def productElementName(n: Int): String = "overriden" +//} +//case class InheritsProductElementName_Override_SelfType(a: String, b: Int) extends C + +case class PrivateMembers(a: Int, private val b: Int, c: Int, private val d: Int, e: Int, private val f: Int) extends Product2_13 + +object Test extends App { + def pretty(p: Product2_13): String = + p.productElementNames.zip(p.productIterator) + .map { case (name, value) => s"$name=$value" } + .mkString(p.productPrefix + "(", ", ", ")") + + println(pretty(User("Susan", 42))) + println(pretty(ユーザー("Susan", 42))) + println(pretty(U$er("Susan", 42))) + println(pretty(`type`("Susan", 42))) + println(pretty(`contains spaces`("Susan", 42))) + println(pretty(Symbols("Susan", 42))) + println(pretty(MultipleParamLists("Susan", 42)(true))) + println(pretty(AuxiliaryConstructor("Susan", 42))) + println(pretty(OverloadedApply("Susan"))) +// println(pretty(DefinesProductElementName("Susan", 42))) + +// // uses the synthetic, not the one defined in the trait +// println(pretty(InheritsProductElementName("Susan", 42))) +// +// // uses the override defined in the trait +// println(pretty(InheritsProductElementName_Override("Susan", 42))) +// +// // uses the synthetic, not the one defined in the trait +// println(pretty(InheritsProductElementName_Override_SelfType("Susan", 42))) + + println(pretty(PrivateMembers(10, 20, 30, 40, 50, 60))) + println(pretty(NoParams())) +} diff --git a/tests/run/tasty-extractors-2.check b/tests/run/tasty-extractors-2.check index a388ae5dce48..841acca6ab5c 100644 --- a/tests/run/tasty-extractors-2.check +++ b/tests/run/tasty-extractors-2.check @@ -49,7 +49,7 @@ Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymb Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(DefDef("a", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0))))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), 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()), ""), 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()), ""), 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$")), ""), Nil))), ClassDef("Foo$", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), 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()), ""), Nil))), DefDef("unapply", Nil, List(List(ValDef("x$1", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Literal(Constant.Boolean(true))))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), 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"), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Apply(Term.Ident("throw"), List(Term.Apply(Term.Select(Term.New(TypeTree.Select(Term.Select(Term.Ident("java"), "lang"), "IndexOutOfBoundsException")), ""), List(Term.Apply(Term.Select(Term.Select(Term.Select(Term.Ident("java"), "lang"), "String"), "valueOf"), List(Term.Ident("x$1")))))))))))), DefDef("copy", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), 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()), ""), 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$")), ""), Nil))), ClassDef("Foo$", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), 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()), ""), Nil))), DefDef("unapply", Nil, List(List(ValDef("x$1", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Literal(Constant.Boolean(true))))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo1", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("a", TypeTree.Inferred(), None)))), Term.Literal(Constant.Unit())))