Skip to content

Commit

Permalink
rework tags wip
Browse files Browse the repository at this point in the history
  • Loading branch information
dos65 committed Dec 20, 2020
1 parent 42d6317 commit 61ffd51
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 54 deletions.
4 changes: 2 additions & 2 deletions modules/core/src/main/scala/make/Tag.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ object Tag {

final case class TpeTag[A](tpe: TpeTag.Type)

final case class TPTag[A](symbol: String)
final case class TPTag[A](tpe: TpeTag.Type)
object TPTag {
implicit def materialize[A]: TPTag[A] =
macro TpeTagMacro.materializeTPTag[A]
Expand All @@ -50,7 +50,7 @@ object Tag {
def render: String = {
val argsStr = arguments match {
case Nil => ""
case lst => lst.mkString("[", ",", "]")
case lst => lst.mkString("[", ", ", "]")
}
s"$symbol$argsStr"
}
Expand Down
232 changes: 198 additions & 34 deletions modules/core/src/main/scala/make/internal/TpeTagMacro.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,189 @@ package make.internal
import scala.reflect.macros.blackbox
import make.Tag.TpeTag
import make.Tag
import scala.reflect.internal.Constants

class TpeTagMacro(val c:blackbox.Context) {
class TpeTagMacro(val c: blackbox.Context) {

import c.universe._

def materializeTpeTag[A : WeakTypeTag]: c.Expr[TpeTag[A]] = {
val tpe = weakTypeOf[A]
val tree = q"_root_.make.Tag.TpeTag[${tpe}](${transformTpe(tpe)})"
println(s"CALL MATERIALIZE: $tpe ${tpe.typeSymbol.isParameter}")
val value = transformTpe3(tpe)
val tree = q"_root_.make.Tag.TpeTag[${tpe}]($value)"
println(s"OK!! $tree")
c.Expr[TpeTag[A]](tree)
}

private def transformTpe(t: c.Type): c.Tree = {
val normalized = t.dealias.etaExpand
val resolved =
if (normalized.typeSymbol.isParameter) {
normalized match {
case PolyType(typeParams, _) =>
typeParams.size match {
case 1 =>
val tcTagTpe = appliedType(weakTypeOf[Tag.TCTag[X] forSome {type X[_]}].typeConstructor, t.typeConstructor)
optionFromImplicitTree(c.inferImplicitValue(tcTagTpe))
.map(t => q"$t.symbol")
case n =>
None
}
case x if x.typeSymbol.isParameter =>
val tagTpe = appliedType(weakTypeOf[Tag.TPTag[X] forSome {type X}].typeConstructor, t)
optionFromImplicitTree(c.inferImplicitValue(tagTpe))
.map{t => q"$t.symbol"}
case x => None
private def transformTpe3(t: c.Type): c.Tree = {
if (t.typeSymbol.isParameter)
transformTpeParameter(t)
else
tranformDefinedTpe(t)
}

private def tranformDefinedTpe(t: c.Type): c.Tree = {
val name = t.typeSymbol.fullName
val nameTree = q"$name"
processTypeParameters(nameTree, t)
}

private def processTypeParameters(symbolName: c.Tree, t: c.Type): c.Tree = {
val inner = t.dealias.typeArgs.map(transformTpe3)
q"_root_.make.Tag.TpeTag.Type($symbolName, $inner)"
// t.dealias match {
// case ref: TypeRef =>
// val inner = ref.args.map(transformTpe3)
// q"_root_.make.Tag.TpeTag.Type($symbolName, $inner)"
// case x =>
// c.info(c.enclosingPosition, s"TpeTag not implemented for $x: ${x.getClass}", true)
// c.abort(c.enclosingPosition, "Not implemented")
// }
}

private def transformTpeParameter(t: c.Type): c.Tree = {
t.etaExpand match {
case tpe: PolyType =>
tpe.typeParams.size match {
case 1 => processTypeParameters(searchTCTag(t), t)
case n => c.abort(c.enclosingPosition, s"Not implemented for type paramters where n=$n")
}
} else {
Some(q"${normalized.typeSymbol.fullName}")
}

resolved match {
case None => c.abort(c.enclosingPosition, s"Not found Tag for $t")
case Some(tree) =>
val arguments = t.typeArgs.map(transformTpe)
q"_root_.make.Tag.TpeTag.Type($tree, List(..$arguments))"
case _ => searchTPTag(t)
}
// val kind = t.dealias.typeArgs
// println(s"TYPE ARGS for $t : $kind")
// kind.size match {
// case 0 => searchTPTag(t)
// case 1 => processTypeParameters(searchTCTag(t), t)
// case n => c.abort(c.enclosingPosition, "Not implemented")
// }

// t.dealias match {
// case tpe: TypeRef =>
// val inner = tpe.args.map(transformTpe3)
// q"_root_.make.Tag.TpeTag.Type(${symbolTree}, $inner)"
// case x =>
// c.info(c.enclosingPosition, s"TpeTag(parameter) not implemented for $x: ${x.getClass}", true)
// c.abort(c.enclosingPosition, "Not implemented")
// }
}


private def searchTCTag(t: c.Type): c.Tree = {
val tcTagTpe = appliedType(weakTypeOf[Tag.TCTag[X] forSome {type X[_]}].typeConstructor, t.typeConstructor)
optionFromImplicitTree(c.inferImplicitValue(tcTagTpe))
.map(t => q"$t.symbol")
.getOrElse(c.abort(c.enclosingPosition, "Not implemented"))
}

private def searchTPTag(t: c.Type): c.Tree = {
val tagTpe = appliedType(weakTypeOf[Tag.TPTag[X] forSome {type X}].typeConstructor, t)
optionFromImplicitTree(c.inferImplicitValue(tagTpe))
.map{t => q"$t.tpe"}
.getOrElse(c.abort(c.enclosingPosition, "Not implemented"))
}

// private def transformTpe2(t: c.Type): c.Tree = {
// println(s"transform $t ${t.typeSymbol.isParameter} ${t.getClass}")
// val normalized = t
// if (t.typeSymbol.isParameter) {
// t.dealias match {
// case ref: PolyType =>
// println(s"IS POLY ${ref.typeParams} ${ref.typeArgs}")
// val kind = math.max(ref.typeParams.size, ref.typeArgs.size)
// ref.typeParams.size match {
// case 0 =>
// val inner = ref.typeArgs.map(transformTpe2)
// q"_root_.make.Tag.TpeTag.Type(${ref.typeSymbol.fullName}, $inner)"
// case 1 =>
// val tcTagTpe = appliedType(weakTypeOf[Tag.TCTag[X] forSome {type X[_]}].typeConstructor, t.typeConstructor)
// optionFromImplicitTree(c.inferImplicitValue(tcTagTpe))
// .map(t => q"$t.tpe")
// .getOrElse(c.abort(c.enclosingPosition, "Not implemented"))
// case n =>
// c.abort(c.enclosingPosition, "Not implemented")
// }
// case ref: TypeRef =>
// ref.typeParams.size match {
// case 0 =>
// val inner = ref.typeArgs.map(transformTpe2)
// q"_root_.make.Tag.TpeTag.Type(${ref.typeSymbol.fullName}, $inner)"
// case 1 =>
// val tcTagTpe = appliedType(weakTypeOf[Tag.TCTag[X] forSome {type X[_]}].typeConstructor, t.typeConstructor)
// optionFromImplicitTree(c.inferImplicitValue(tcTagTpe))
// .map(t => q"$t.tpe")
// .getOrElse(c.abort(c.enclosingPosition, "Not implemented"))
// case n =>
// c.abort(c.enclosingPosition, "Not implemented")
// }

// // case PolyType(typeParams, _) =>
// // println(s"POLY TYPE: $typeParams")
// // typeParams.size match {
// // case 1 =>
// // val tcTagTpe = appliedType(weakTypeOf[Tag.TCTag[X] forSome {type X[_]}].typeConstructor, t.typeConstructor)
// // optionFromImplicitTree(c.inferImplicitValue(tcTagTpe))
// // .map(t => q"$t.tpe")
// // .getOrElse(c.abort(c.enclosingPosition, "Not implemented"))
// // case n =>
// // c.abort(c.enclosingPosition, "Not implemented")
// // }
// // case x if x.typeSymbol.isParameter =>
// // val tagTpe = appliedType(weakTypeOf[Tag.TPTag[X] forSome {type X}].typeConstructor, t)
// // optionFromImplicitTree(c.inferImplicitValue(tagTpe))
// // .map{t => q"$t.tpe"}
// // .getOrElse(c.abort(c.enclosingPosition, "Not implemented"))
// case x =>
// c.abort(c.enclosingPosition, "Not implemented")
// }
// } else {
// t match {
// case ref: TypeRef =>
// val inner = ref.args.map(transformTpe2)
// q"_root_.make.Tag.TpeTag.Type(${ref.typeSymbol.fullName}, $inner)"
// case x =>
// c.info(c.enclosingPosition, s"TpeTag not implemented for $x: ${x.getClass}", true)
// c.abort(c.enclosingPosition, "Not implemented")
// }
// }
// }

// private def transformTpe(t: c.Type): c.Tree = {
// val normalized = t.dealias.etaExpand
// println(normalized + " " + normalized.getClass)
// val resolved =
// if (normalized.typeSymbol.isParameter) {
// normalized match {
// case PolyType(typeParams, _) =>
// println(s"POLY TYPE: $typeParams")
// typeParams.size match {
// case 1 =>
// val tcTagTpe = appliedType(weakTypeOf[Tag.TCTag[X] forSome {type X[_]}].typeConstructor, t.typeConstructor)
// optionFromImplicitTree(c.inferImplicitValue(tcTagTpe))
// .map(t => q"$t.tpe")
// case n =>
// None
// }
// case x if x.typeSymbol.isParameter =>
// val tagTpe = appliedType(weakTypeOf[Tag.TPTag[X] forSome {type X}].typeConstructor, t)
// optionFromImplicitTree(c.inferImplicitValue(tagTpe))
// .map{t => q"$t.tpe"}
// case x => None
// }
// } else {
// Some(q"${normalized.typeSymbol.fullName}")
// }

// resolved match {
// case None => c.abort(c.enclosingPosition, s"Not found Tag for $t")
// case Some(tree) =>
// tree
// // val arguments = t.typeArgs.map(transformTpe)
// // q"_root_.make.Tag.TpeTag.Type($tree, List(..$arguments))"
// }
// }

private def optionFromImplicitTree(tree: c.Tree): Option[c.Tree] =
tree match {
case EmptyTree => None
Expand All @@ -56,11 +196,18 @@ class TpeTagMacro(val c:blackbox.Context) {
weakTypeTag: WeakTypeTag[F[X] forSome {type X}]
): c.Expr[Tag.TCTag[F]] = {

val tpe = weakTypeTag.tpe.finalResultType
val tpe = weakTypeTag.tpe
println(s"TCTAG: ${tpe} ${tpe.getClass}")
tpe match {
case tpe: PolyType =>
val symbol = c.internal.fullyInitialize(tpe.resultType.typeSymbol)
val name = fullName(symbol)
val tree = q"""_root_.make.Tag.TCTag[${weakTypeTag.tpe}](${name})"""
c.Expr[Tag.TCTag[F]](tree)
case ref: TypeRef =>
val symbol = ref.sym
val tree = q"""_root_.make.Tag.TCTag[${weakTypeTag.tpe}](${symbol.fullName})"""
val name = fullName(symbol)
val tree = q"""_root_.make.Tag.TCTag[${weakTypeTag.tpe}](${name})"""
c.Expr[Tag.TCTag[F]](tree)
case x =>
c.warning(c.enclosingPosition, s"Failed to create Tag.TCTag for $tpe")
Expand All @@ -74,11 +221,28 @@ class TpeTagMacro(val c:blackbox.Context) {
val tpe = weakTypeTag.tpe.dealias.etaExpand
val symbol = tpe.typeSymbol
if (symbol.isParameter) {
c.abort(c.enclosingPosition, "Failed to make TCTag")
c.abort(c.enclosingPosition, s"Failed to make TPTag for $tpe")
} else {
val tree = q"""_root_.make.Tag.TPTag[${weakTypeTag.tpe}](${symbol.fullName})"""
val tree = q"""_root_.make.Tag.TPTag[${weakTypeTag.tpe}](_root_.make.Tag.TpeTag.Type(${symbol.fullName}, List.empty))"""
c.Expr[Tag.TPTag[A]](tree)
}
}

private def fullName(s: c.Symbol): String = {
def loop(sym: c.Symbol, acc: List[String]): String = {
val name = sym.name.decodedName.toString
if (sym == NoSymbol || name == "<root>") acc.mkString(".")
else {
// see test case with cats.Id
val skip =
name.startsWith("<local ") || name == "package" || (sym.isAbstract && sym.isParameter)
// val skip = false
val next = if (skip) acc else name :: acc
loop(sym.owner, next)
}
}
println(s.fullName)
loop(s, List.empty)
}

}
9 changes: 4 additions & 5 deletions modules/core/src/test/scala/make/MakeTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,13 @@ class MakeTest extends FunSuite {
case class B(a: String, i: Int)

@autoMake
case class C(b: B, a: A[IO])
case class C(b: B)

implicit def bMake[F[_]: Applicative](
implicit
intM: Make[F, Int],
aM: Make[F, A[F]]
deps: Make[F, (Int, A[F])],
): Make[F, B] =
(intM, aM).mapFN((i, a) => a.value.map(v => B(v, i)))
deps.mapFN((i, a) => a.value.map(v => B(v, i)))

import enableDebug._

Expand All @@ -134,7 +133,7 @@ class MakeTest extends FunSuite {


val resolved = Make.of[IO, (C, B)]
// assertEquals(resolved.make.unsafeRunSync(), B("42", 42))
assertEquals(resolved.make.unsafeRunSync(), (C(B("42", 42)), B("42", 42)))
}

}
30 changes: 17 additions & 13 deletions modules/core/src/test/scala/make/TagTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@ package make

import munit.FunSuite
import cats.effect.IO
import make.Tag.TpeTag

class TagTest extends FunSuite {

test("tags") {
assertEquals(Tag.TpeTag[Int].tpe.render, "scala.Int")
assertEquals(Tag.TpeTag[List[String]].tpe.render, "scala.collection.immutable.List[java.lang.String]")
assertEquals(func[IO].tpe.render, "make.TagTest.ABC[cats.effect.IO]")
assertEquals(func2[IO].tpe.render, "cats.effect.IO[scala.Int]")
assertEquals(func3[String].tpe.render, "scala.collection.immutable.List[java.lang.String]")
assertEquals(func[cats.Id].tpe.render, "make.TagTest.ABC[cats.Id]")
assertEquals(func2[cats.Id].tpe.render, "cats.Id[scala.Int]")
}
// test("tags") {
// assertEquals(Tag.TpeTag[Int].tpe.render, "scala.Int")
// assertEquals(Tag.TpeTag[List[String]].tpe.render, "scala.collection.immutable.List[java.lang.String]")
// assertEquals(func[IO].tpe.render, "make.TagTest.ABC[cats.effect.IO]")
// assertEquals(func2[IO].tpe.render, "cats.effect.IO[scala.Int]")
// assertEquals(func3[String].tpe.render, "scala.collection.immutable.List[java.lang.String]")
// assertEquals(func[cats.Id].tpe.render, "make.TagTest.ABC[cats.Id]")
// assertEquals(func2[cats.Id].tpe.render, "cats.Id[scala.Int]")
// assertEquals(func4[String, IO[List[Int]]].tpe.render, "scala.Tuple2[java.lang.String, cats.effect.IO[scala.collection.immutable.List[scala.Int]]]")
// }

class ABC[F[_]]
def func[F[_]: Tag.TCTag]: Tag.TpeTag[ABC[F]] = Tag.TpeTag[ABC[F]]
// class ABC[F[_]]
// def func[F[_]: Tag.TCTag]: Tag.TpeTag[ABC[F]] = Tag.TpeTag[ABC[F]]

// def func2[G[_]: Tag.TCTag]: Tag.TpeTag[G[Int]] = Tag.TpeTag[G[Int]]
// def func3[A: Tag.TPTag]: Tag.TpeTag[List[A]] = Tag.TpeTag[List[A]]
// def func4[A, B](implicit tag: TpeTag[(A, B)]): Tag.TpeTag[(A, B)] = Tag.TpeTag[(A, B)]

def func2[G[_]: Tag.TCTag]: Tag.TpeTag[G[Int]] = Tag.TpeTag[G[Int]]
def func3[A: Tag.TPTag]: Tag.TpeTag[List[A]] = Tag.TpeTag[List[A]]
}

0 comments on commit 61ffd51

Please sign in to comment.