All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scalaz.macros.IotaDerivingMacros.scala Maven / Gradle / Ivy

// Copyright: 2017 - 2024 Sam Halliday
// License: http://www.gnu.org/licenses/lgpl-3.0.en.html

package scalaz.macros

import scala._
import scala.reflect.macros.blackbox

final class IotaDerivingMacros(val c: blackbox.Context) {
  import c.universe._

  def gen[F[_], A](implicit
    evF: c.WeakTypeTag[F[_]],
    evA: c.WeakTypeTag[A]
  ): Tree = {
    val F    = evF.tpe.typeConstructor
    val A    = evA.tpe
    val aSym = A.typeSymbol.asClass

    val TNil  = weakTypeOf[scalaz.iotaz.TNil]
    val TCons = weakTypeOf[scalaz.iotaz.TCons[_, _]].typeConstructor
    val Name  = weakTypeOf[scalaz.Name[_]].typeConstructor

    def tlist(parts: List[Type]) =
      parts
        .foldRight(TNil) { (el, els) =>
          appliedType(TCons, el, els)
        }

    val parts =
      if (aSym.isSealed)
        // ordering is ill-defined, we use source ordering
        aSym.asClass.knownDirectSubclasses.toList
          .map(_.asClass)
          .sortBy(_.pos.start)
          .map { cl =>
            if (cl.isModuleClass)
              internal.singleType(cl.thisPrefix, cl.module)
            else {
              // this block is needed to handle the type parameter on a GADT
              val t    = cl.toType
              val args = t.typeArgs.map { a =>
                val sym  = a.typeSymbol
                val tSym = A
                  .find(_.typeSymbol.name == sym.name)
                  .getOrElse(
                    c.abort(
                      c.enclosingPosition,
                      s"type parameters on case classes ($t[${t.typeArgs}]) are not supported unless they are on the sealed trait ($A)"
                    )
                  )
                a.substituteTypes(List(sym), List(tSym))
              }
              appliedType(t, args)
            }
          }
      else
        A.decls.collect {
          case m: MethodSymbol if m.isCaseAccessor =>
            m.asMethod.typeSignatureIn(A).resultType
        }.toList

    val data = tlist(parts)
    val tcs  = tlist(parts.map(s => appliedType(Name, appliedType(F, s))))

    val tcs_rhs = parts.map { s: Type =>
      val tc  = appliedType(F, s)
      val imp =
        c.inferImplicitValue(tc).orElse {
          // when deriving a coproduct, if we can't find implicit evidence for
          // the branches, derive one for use in the coproduct derivation only
          if (aSym.isSealed)
            // doesn't work when s.typeSymbol.isModuleClass
            // https://gitlab.com/fommil/scalaz-deriving/issues/89
            q"_root_.scalaz.macros.DerivingMacros.deriving[$F, $s]: $tc"
          else
            // this will fail later on, but with a compiler-generated implicit
            // search failure message (respecting @implicitNotFound &c.)
            // it would be slightly shorter to `inferImplicitValue` unsilently
            // and use whatever message we find in the exception, but exceptions...
            q"_root_.scala.Predef.implicitly[$tc]: $tc"
        }
      q"_root_.scalaz.Need($imp): _root_.scalaz.Name[$tc]"
    }

    if (aSym.isSealed)
      q"""
       val gen = _root_.scalaz.iotaz.Cop.gen[$A, $data]
       val tcs = _root_.scalaz.iotaz.Prod[$tcs](..$tcs_rhs)
       _root_.scalaz.Deriving[$F].xcoproductz(tcs)(gen.from, gen.to)
       """
    else
      q"""
       val gen = _root_.scalaz.iotaz.Prod.gen[$A, $data]
       val tcs = _root_.scalaz.iotaz.Prod[$tcs](..$tcs_rhs)
       _root_.scalaz.DerivingProducts[$F].xproductz(tcs)(gen.from, gen.to)
       """
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy