From 65e1202046d9fd81f881ded39a6b1cb1cd6b9076 Mon Sep 17 00:00:00 2001 From: Pask Date: Sat, 7 Aug 2021 20:14:40 +0200 Subject: [PATCH 1/2] Added isEnum field to SealedTrait --- src/core/interface.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/interface.scala b/src/core/interface.scala index 9a85a1bc..05674519 100644 --- a/src/core/interface.scala +++ b/src/core/interface.scala @@ -61,7 +61,8 @@ case class SealedTrait[Typeclass[_], Type] (typeInfo: TypeInfo, subtypes: IArray[SealedTrait.Subtype[Typeclass, Type, _]], annotations: IArray[Any], - typeAnnotations: IArray[Any]) extends Serializable: + typeAnnotations: IArray[Any], + isEnum: Boolean = false) extends Serializable: type Subtype[S] = SealedTrait.SubtypeValue[Typeclass, Type, S] From 024842563742bec9b889c1b261a899877c08b506 Mon Sep 17 00:00:00 2001 From: Pask Date: Tue, 10 Aug 2021 14:55:50 +0200 Subject: [PATCH 2/2] Implemented isEnum macro --- src/core/interface.scala | 2 +- src/core/macro.scala | 29 +++++++++++++++++------------ src/core/magnolia.scala | 2 +- src/examples/SubtypeInfo.scala | 6 +++++- src/test/tests.scala | 13 +++++++++++++ 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/core/interface.scala b/src/core/interface.scala index 05674519..5c1c32a2 100644 --- a/src/core/interface.scala +++ b/src/core/interface.scala @@ -62,7 +62,7 @@ case class SealedTrait[Typeclass[_], Type] subtypes: IArray[SealedTrait.Subtype[Typeclass, Type, _]], annotations: IArray[Any], typeAnnotations: IArray[Any], - isEnum: Boolean = false) extends Serializable: + isEnum: Boolean) extends Serializable: type Subtype[S] = SealedTrait.SubtypeValue[Typeclass, Type, S] diff --git a/src/core/macro.scala b/src/core/macro.scala index 7f52bfde..4abc76c3 100644 --- a/src/core/macro.scala +++ b/src/core/macro.scala @@ -4,6 +4,7 @@ import scala.quoted.* object Macro: inline def isObject[T]: Boolean = ${isObject[T]} + inline def isEnum[T]: Boolean = ${isEnum[T]} inline def anns[T]: List[Any] = ${anns[T]} inline def typeAnns[T]: List[Any] = ${typeAnns[T]} inline def paramAnns[T]: List[(String, List[Any])] = ${paramAnns[T]} @@ -23,7 +24,11 @@ object Macro: import quotes.reflect.* Expr(TypeRepr.of[T].typeSymbol.flags.is(Flags.Module)) - + + def isEnum[T: Type](using Quotes): Expr[Boolean] = + import quotes.reflect.* + + Expr(TypeRepr.of[T].typeSymbol.flags.is(Flags.Enum)) def paramAnns[T: Type](using Quotes): Expr[List[(String, List[Any])]] = import quotes.reflect.* @@ -56,16 +61,16 @@ object Macro: import quotes.reflect.* val tpe = TypeRepr.of[T] - + Expr.ofList { tpe.typeSymbol.annotations.filter { a => a.tpe.typeSymbol.maybeOwner.isNoSymbol || a.tpe.typeSymbol.owner.fullName != "scala.annotation.internal" }.map(_.asExpr.asInstanceOf[Expr[Any]]) } - + def typeAnns[T: Type](using Quotes): Expr[List[Any]] = import quotes.reflect.* - + def getAnnotations(t: TypeRepr): List[Term] = t match case AnnotatedType(inner, ann) => ann :: getAnnotations(inner) case _ => Nil @@ -90,15 +95,15 @@ object Macro: def isValueClass[T: Type](using Quotes): Expr[Boolean] = import quotes.reflect.* - + Expr(TypeRepr.of[T].baseClasses.contains(Symbol.classSymbol("scala.AnyVal"))) - + def defaultValue[T: Type](using Quotes): Expr[List[(String, Option[Any])]] = import quotes.reflect._ // TODO: Implement RHS Expr.ofList(TypeRepr.of[T].typeSymbol.caseFields.map { case s => Expr(s.name -> None) }) - + def paramTypeAnns[T: Type](using Quotes): Expr[List[(String, List[Any])]] = import quotes.reflect._ @@ -111,17 +116,17 @@ object Macro: val tpeRepr = field.tree match case v: ValDef => v.tpt.tpe case d: DefDef => d.returnTpt.tpe - + Expr(field.name) -> getAnnotations(tpeRepr).filter { a => a.tpe.typeSymbol.maybeOwner.isNoSymbol || a.tpe.typeSymbol.owner.fullName != "scala.annotation.internal" }.map(_.asExpr.asInstanceOf[Expr[Any]]) }.filter(_._2.nonEmpty).map { (name, annots) => Expr.ofTuple(name, Expr.ofList(annots)) } } - + def repeated[T: Type](using Quotes): Expr[List[(String, Boolean)]] = import quotes.reflect.* - + def isRepeated[T](tpeRepr: TypeRepr): Boolean = tpeRepr match case a: AnnotatedType => a.annotation.tpe match @@ -132,13 +137,13 @@ object Macro: val tr = TypeRepr.of[T] val symbol = tr.typeSymbol val constr = symbol.primaryConstructor.tree.asInstanceOf[DefDef] - + val areRepeated = constr.paramss.flatMap(_.params.flatMap { case ValDef(name, tpeTree, _) => Some(name -> isRepeated(tpeTree.tpe)) case _ => None } ) - + Expr(areRepeated) def typeInfo[T: Type](using Quotes): Expr[TypeInfo] = diff --git a/src/core/magnolia.scala b/src/core/magnolia.scala index 5edf9545..4d68bb64 100644 --- a/src/core/magnolia.scala +++ b/src/core/magnolia.scala @@ -79,7 +79,7 @@ trait Derivation[TypeClass[_]] extends CommonDerivation[TypeClass]: inline def derivedMirrorSum[A](sum: Mirror.SumOf[A]): Typeclass[A] = val sealedTrait = SealedTrait(typeInfo[A], IArray(subtypes[A, sum.MirroredElemTypes](sum)*), - IArray.from(anns[A]), IArray(paramTypeAnns[A]*)) + IArray.from(anns[A]), IArray(paramTypeAnns[A]*), isEnum[A]) split(sealedTrait) diff --git a/src/examples/SubtypeInfo.scala b/src/examples/SubtypeInfo.scala index a9c3078f..853ea349 100644 --- a/src/examples/SubtypeInfo.scala +++ b/src/examples/SubtypeInfo.scala @@ -6,6 +6,7 @@ trait SubtypeInfo[T] { def subtypeIsObject: Seq[Boolean] def traitAnnotations: Seq[Any] def subtypeAnnotations: Seq[Seq[Any]] + def isEnum: Boolean } object SubtypeInfo extends Derivation[SubtypeInfo]: @@ -14,15 +15,18 @@ object SubtypeInfo extends Derivation[SubtypeInfo]: def subtypeIsObject: Seq[Boolean] = Nil def traitAnnotations: List[Any] = Nil def subtypeAnnotations: List[List[Any]] = Nil + def isEnum: Boolean = false override def split[T](ctx: SealedTrait[SubtypeInfo, T]): SubtypeInfo[T] = new SubtypeInfo[T]: def subtypeIsObject: Seq[Boolean] = ctx.subtypes.map(_.isObject) def traitAnnotations: Seq[Any] = ctx.annotations def subtypeAnnotations: Seq[Seq[Any]] = ctx.subtypes.map(_.annotations.toList).toList + def isEnum: Boolean = ctx.isEnum given fallback[T]: SubtypeInfo[T] = new SubtypeInfo[T]: def subtypeIsObject: Seq[Boolean] = Nil def traitAnnotations: Seq[Any] = Nil - def subtypeAnnotations: Seq[Seq[Any]] = Nil \ No newline at end of file + def subtypeAnnotations: Seq[Seq[Any]] = Nil + def isEnum: Boolean = false \ No newline at end of file diff --git a/src/test/tests.scala b/src/test/tests.scala index 297148d6..563d7506 100644 --- a/src/test/tests.scala +++ b/src/test/tests.scala @@ -217,6 +217,9 @@ sealed trait Y case object A extends Y case class B(s: String) extends Y +enum Size: + case S, M, L + class Tests extends munit.FunSuite { test("construct a Show product instance with alternative apply functions") { @@ -499,4 +502,14 @@ class Tests extends munit.FunSuite { val res = compileErrors("""summon[SemiPrint[Y]].print(A)""") assert(res.nonEmpty) } + + test("isEnum field in SubtypeInfo should be true for enum") { + val derivedSubtypeInfo = SubtypeInfo.derived[Size] + assertEquals(derivedSubtypeInfo.isEnum, true) + } + + test("isEnum field in SubtypeInfo should be false for sealed trait") { + val derivedSubtypeInfo = SubtypeInfo.derived[Sport] + assertEquals(derivedSubtypeInfo.isEnum, false) + } } \ No newline at end of file