From 525e8e2ece64c4020a67f6467994542db98a00b4 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Thu, 30 Jun 2016 20:06:07 +0200 Subject: [PATCH 1/2] Add `toMap` method to case classes --- src/dotty/DottyPredef.scala | 10 +++++++ src/dotty/tools/dotc/ast/Desugar.scala | 5 ++-- src/dotty/tools/dotc/core/Definitions.scala | 11 +++++-- src/dotty/tools/dotc/core/StdNames.scala | 2 ++ .../dotc/transform/SyntheticMethods.scala | 30 +++++++++++++++++-- tests/pos/toMap.scala | 7 +++++ 6 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 tests/pos/toMap.scala diff --git a/src/dotty/DottyPredef.scala b/src/dotty/DottyPredef.scala index cd90c4882b77..dbc219134312 100644 --- a/src/dotty/DottyPredef.scala +++ b/src/dotty/DottyPredef.scala @@ -4,6 +4,7 @@ import scala.reflect.runtime.universe.TypeTag import scala.reflect.ClassTag import scala.Predef.??? import scala.collection.Seq +import scala.collection.immutable.Map /** unimplemented implicit for TypeTag */ object DottyPredef { @@ -42,4 +43,13 @@ object DottyPredef { implicit def eqNumFloat : Eq[Number, Float] = Eq implicit def eqDoubleNum: Eq[Double, Number] = Eq implicit def eqNumDouble: Eq[Number, Double] = Eq + + def _toMap(x: Product, constructorParams: List[String]): Map[String, Any] = { + val values = x.productIterator.toList + constructorParams.zip(values).toMap + } +} + +trait DottyProduct extends scala.Product { + def toMap: Map[String, Any] } diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index f603f68178a3..ef2c4fff93d9 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -370,10 +370,11 @@ object desugar { if (targs.isEmpty) tycon else AppliedTypeTree(tycon, targs) } - // Case classes and case objects get a ProductN parent + // Case classes and case objects get a ProductN parent, as well as a DottyProduct parent + val dottyProduct = Select(rootDot(nme.dotty_), "DottyProduct".toTypeName) var parents1 = parents if (mods.is(Case) && arity <= Definitions.MaxTupleArity) - parents1 = parents1 :+ productConstr(arity) + parents1 = parents1 :+ productConstr(arity) :+ dottyProduct // The thicket which is the desugared version of the companion object // synthetic object C extends parentTpt { defs } diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 5cb373cfd0a1..c345e1da19b3 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -246,6 +246,7 @@ class Definitions { def DottyPredefModule(implicit ctx: Context) = DottyPredefModuleRef.symbol def Predef_eqAny(implicit ctx: Context) = DottyPredefModule.requiredMethod(nme.eqAny) + def predef_method(name: PreName) = DottyPredefModule.requiredMethodRef(name) lazy val DottyArraysModuleRef = ctx.requiredModuleRef("dotty.runtime.Arrays") def DottyArraysModule(implicit ctx: Context) = DottyArraysModuleRef.symbol @@ -264,12 +265,16 @@ class Definitions { lazy val SeqType: TypeRef = ctx.requiredClassRef("scala.collection.Seq") def SeqClass(implicit ctx: Context) = SeqType.symbol.asClass - lazy val Seq_applyR = SeqClass.requiredMethodRef(nme.apply) def Seq_apply(implicit ctx: Context) = Seq_applyR.symbol lazy val Seq_headR = SeqClass.requiredMethodRef(nme.head) def Seq_head(implicit ctx: Context) = Seq_headR.symbol + lazy val ListModuleRef = ctx.requiredModuleRef("scala.collection.immutable.List") + def ListModule(implicit ctx: Context) = ListModuleRef.symbol + lazy val List_applyR = ListModule.requiredMethodRef(nme.apply) + def List_apply(implicit ctx: Context) = List_applyR.symbol + lazy val ArrayType: TypeRef = ctx.requiredClassRef("scala.Array") def ArrayClass(implicit ctx: Context) = ArrayType.symbol.asClass lazy val Array_applyR = ArrayClass.requiredMethodRef(nme.apply) @@ -416,7 +421,7 @@ class Definitions { def DynamicClass(implicit ctx: Context) = DynamicType.symbol.asClass lazy val OptionType: TypeRef = ctx.requiredClassRef("scala.Option") def OptionClass(implicit ctx: Context) = OptionType.symbol.asClass - lazy val ProductType: TypeRef = ctx.requiredClassRef("scala.Product") + lazy val ProductType: TypeRef = ctx.requiredClassRef("dotty.DottyProduct") def ProductClass(implicit ctx: Context) = ProductType.symbol.asClass lazy val Product_canEqualR = ProductClass.requiredMethodRef(nme.canEqual_) def Product_canEqual(implicit ctx: Context) = Product_canEqualR.symbol @@ -424,6 +429,8 @@ class Definitions { def Product_productArity(implicit ctx: Context) = Product_productArityR.symbol lazy val Product_productPrefixR = ProductClass.requiredMethodRef(nme.productPrefix) def Product_productPrefix(implicit ctx: Context) = Product_productPrefixR.symbol + lazy val Product_toMapR = ProductClass.requiredMethodRef(nme.toMap) + def Product_toMap(implicit ctx: Context) = Product_toMapR.symbol lazy val LanguageModuleRef = ctx.requiredModule("dotty.language") def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl") diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 81f6da0e2c57..994d03af8579 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -476,6 +476,7 @@ object StdNames { val runtimeMirror: N = "runtimeMirror" val sameElements: N = "sameElements" val scala_ : N = "scala" + val dotty_ : N = "dotty" val selectDynamic: N = "selectDynamic" val selectOverloadedMethod: N = "selectOverloadedMethod" val selectTerm: N = "selectTerm" @@ -501,6 +502,7 @@ object StdNames { val toList: N = "toList" val toObjectArray : N = "toObjectArray" val toSeq: N = "toSeq" + val toMap: N = "toMap" val toString_ : N = "toString" val toTypeConstructor: N = "toTypeConstructor" val tpe : N = "tpe" diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala index 9dfd92fe9f2c..0a00dfb85f59 100644 --- a/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -43,8 +43,14 @@ class SyntheticMethods(thisTransformer: DenotTransformer) { private def initSymbols(implicit ctx: Context) = if (myValueSymbols.isEmpty) { myValueSymbols = List(defn.Any_hashCode, defn.Any_equals) - myCaseSymbols = myValueSymbols ++ List(defn.Any_toString, defn.Product_canEqual, - defn.Product_productArity, defn.Product_productPrefix) + myCaseSymbols = myValueSymbols ++ { + defn.Any_toString :: + defn.Product_canEqual :: + defn.Product_productArity :: + defn.Product_productPrefix :: + defn.Product_toMap :: + Nil + } } def valueSymbols(implicit ctx: Context) = { initSymbols; myValueSymbols } @@ -80,6 +86,22 @@ class SyntheticMethods(thisTransformer: DenotTransformer) { def forwardToRuntime(vrefss: List[List[Tree]]): Tree = ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefss.head) + def synthMap(vrefss: List[List[Tree]]): Tree = { + val paramNames = SeqLiteral( + clazz + .primaryConstructor.info + .asInstanceOf[MethodType] + .paramNames.map(x => Literal(Constant(x.show))), + TypeTree(defn.StringType) + ) + + val repeatedConstrParams = + Typed(paramNames, TypeTree(paramNames.tpe.widen.translateParameterized(defn.SeqClass, defn.RepeatedParamClass))) + val listOfConstrParams = ref(defn.List_apply).appliedToType(defn.StringType).appliedTo(repeatedConstrParams) + + ref(defn.predef_method("_" + sym.name.toString)).appliedToArgs(This(clazz) :: listOfConstrParams :: Nil) + } + def ownName(vrefss: List[List[Tree]]): Tree = Literal(Constant(clazz.name.stripModuleClassSuffix.decode.toString)) @@ -91,6 +113,7 @@ class SyntheticMethods(thisTransformer: DenotTransformer) { case nme.canEqual_ => vrefss => canEqualBody(vrefss.head.head) case nme.productArity => vrefss => Literal(Constant(accessors.length)) case nme.productPrefix => ownName + case nme.toMap => synthMap } ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}") DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic))) @@ -107,7 +130,8 @@ class SyntheticMethods(thisTransformer: DenotTransformer) { * that match { * case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y * case _ => false - * } + * } + * } * * If C is a value class the initial `eq` test is omitted. */ diff --git a/tests/pos/toMap.scala b/tests/pos/toMap.scala new file mode 100644 index 000000000000..b172b9ac7dd0 --- /dev/null +++ b/tests/pos/toMap.scala @@ -0,0 +1,7 @@ +object Test { + case class Car(`type`: String, year: Int) + + def main(args: Array[String]): Unit = { + println(Car("Mustang", 1977).toMap) + } +} From 50ca487b9a0c052edd12bf4322cd6dae87777995 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Thu, 30 Jun 2016 20:20:37 +0200 Subject: [PATCH 2/2] Add `fields` method to DottyProduct class --- src/dotty/DottyPredef.scala | 4 +++- src/dotty/tools/dotc/core/Definitions.scala | 2 ++ src/dotty/tools/dotc/core/StdNames.scala | 3 ++- .../tools/dotc/transform/SyntheticMethods.scala | 16 +++++++++++----- tests/pos/cc_fields.scala | 5 +++++ 5 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 tests/pos/cc_fields.scala diff --git a/src/dotty/DottyPredef.scala b/src/dotty/DottyPredef.scala index dbc219134312..394c730a4454 100644 --- a/src/dotty/DottyPredef.scala +++ b/src/dotty/DottyPredef.scala @@ -44,7 +44,7 @@ object DottyPredef { implicit def eqDoubleNum: Eq[Double, Number] = Eq implicit def eqNumDouble: Eq[Number, Double] = Eq - def _toMap(x: Product, constructorParams: List[String]): Map[String, Any] = { + def _toMap(x: DottyProduct, constructorParams: List[String]): Map[String, Any] = { val values = x.productIterator.toList constructorParams.zip(values).toMap } @@ -52,4 +52,6 @@ object DottyPredef { trait DottyProduct extends scala.Product { def toMap: Map[String, Any] + + def fields: List[String] } diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index c345e1da19b3..ecf49b1be90b 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -425,6 +425,8 @@ class Definitions { def ProductClass(implicit ctx: Context) = ProductType.symbol.asClass lazy val Product_canEqualR = ProductClass.requiredMethodRef(nme.canEqual_) def Product_canEqual(implicit ctx: Context) = Product_canEqualR.symbol + lazy val Product_fieldsR = ProductClass.requiredMethodRef(nme.fields) + def Product_fields(implicit ctx: Context) = Product_fieldsR.symbol lazy val Product_productArityR = ProductClass.requiredMethodRef(nme.productArity) def Product_productArity(implicit ctx: Context) = Product_productArityR.symbol lazy val Product_productPrefixR = ProductClass.requiredMethodRef(nme.productPrefix) diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 994d03af8579..b160d2f62996 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -399,6 +399,7 @@ object StdNames { val experimental: N = "experimental" val f: N = "f" val false_ : N = "false" + val fields: N = "fields" val filter: N = "filter" val finalize_ : N = "finalize" val find_ : N = "find" @@ -500,9 +501,9 @@ object StdNames { val throw_ : N = "throw" val toArray: N = "toArray" val toList: N = "toList" + val toMap: N = "toMap" val toObjectArray : N = "toObjectArray" val toSeq: N = "toSeq" - val toMap: N = "toMap" val toString_ : N = "toString" val toTypeConstructor: N = "toTypeConstructor" val tpe : N = "tpe" diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala index 0a00dfb85f59..487d6892549a 100644 --- a/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -46,6 +46,7 @@ class SyntheticMethods(thisTransformer: DenotTransformer) { myCaseSymbols = myValueSymbols ++ { defn.Any_toString :: defn.Product_canEqual :: + defn.Product_fields :: defn.Product_productArity :: defn.Product_productPrefix :: defn.Product_toMap :: @@ -86,7 +87,7 @@ class SyntheticMethods(thisTransformer: DenotTransformer) { def forwardToRuntime(vrefss: List[List[Tree]]): Tree = ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefss.head) - def synthMap(vrefss: List[List[Tree]]): Tree = { + def synthFields(vrefss: List[List[Tree]]): Tree = { val paramNames = SeqLiteral( clazz .primaryConstructor.info @@ -95,13 +96,17 @@ class SyntheticMethods(thisTransformer: DenotTransformer) { TypeTree(defn.StringType) ) - val repeatedConstrParams = - Typed(paramNames, TypeTree(paramNames.tpe.widen.translateParameterized(defn.SeqClass, defn.RepeatedParamClass))) - val listOfConstrParams = ref(defn.List_apply).appliedToType(defn.StringType).appliedTo(repeatedConstrParams) + val repeatedConstrParams = Typed( + paramNames, + TypeTree(paramNames.tpe.widen.translateParameterized(defn.SeqClass, defn.RepeatedParamClass)) + ) - ref(defn.predef_method("_" + sym.name.toString)).appliedToArgs(This(clazz) :: listOfConstrParams :: Nil) + ref(defn.List_apply).appliedToType(defn.StringType).appliedTo(repeatedConstrParams) } + def synthMap(vrefss: List[List[Tree]]): Tree = + ref(defn.predef_method("_" + sym.name.toString)).appliedToArgs(This(clazz) :: synthFields(vrefss) :: Nil) + def ownName(vrefss: List[List[Tree]]): Tree = Literal(Constant(clazz.name.stripModuleClassSuffix.decode.toString)) @@ -114,6 +119,7 @@ class SyntheticMethods(thisTransformer: DenotTransformer) { case nme.productArity => vrefss => Literal(Constant(accessors.length)) case nme.productPrefix => ownName case nme.toMap => synthMap + case nme.fields => synthFields } ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}") DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic))) diff --git a/tests/pos/cc_fields.scala b/tests/pos/cc_fields.scala new file mode 100644 index 000000000000..cb029a346cba --- /dev/null +++ b/tests/pos/cc_fields.scala @@ -0,0 +1,5 @@ +object Test { + case class Car(`type`: String, year: Int) + def main(args: Array[String]): Unit = + println(Car("Mustang", 1977).fields) +}