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

mill.define.Applicative.scala Maven / Gradle / Ivy

The newest version!
package mill.define

import mill.api.internal

import scala.annotation.compileTimeOnly
import scala.reflect.macros.blackbox.Context

/**
 * A generic Applicative-functor macro: translates calls to
 *
 * Applier.apply{ ... applyable1.apply() ... applyable2.apply() ... }
 *
 * into
 *
 * Applier.zipMap(applyable1, applyable2){ (a1, a2, ctx) => ... a1 ... a2 ... }
 */
@internal
object Applicative {
  trait ApplyHandler[M[+_]] {

    /**
     * Extracts the current value [[T]] out of the wrapping [[M[T]]
     */
    def apply[T](t: M[T]): T
  }
  object ApplyHandler {
    @compileTimeOnly("Target#apply() can only be used with a Task{...} block")
    implicit def defaultApplyHandler[M[+_]]: ApplyHandler[M] = ???
  }
  trait Applyable[M[+_], +T] {
    def self: M[T]
    def apply()(implicit handler: ApplyHandler[M]): T = handler(self)
  }

  type Id[+T] = T

  trait Applyer[W[_], T[_], Z[_], Ctx] {
    def ctx()(implicit c: Ctx) = c
    def traverseCtx[I, R](xs: Seq[W[I]])(f: (IndexedSeq[I], Ctx) => Z[R]): T[R]
  }

  def impl[M[_], T: c.WeakTypeTag, Ctx: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[M[T]] = {
    impl0(c)(t.tree)(implicitly[c.WeakTypeTag[T]], implicitly[c.WeakTypeTag[Ctx]])
  }
  def impl0[M[_], T: c.WeakTypeTag, Ctx: c.WeakTypeTag](c: Context)(t: c.Tree): c.Expr[M[T]] = {
    import c.universe._
    def rec(t: Tree): Iterator[c.Tree] = Iterator(t) ++ t.children.flatMap(rec(_))

    val exprs = collection.mutable.Buffer.empty[c.Tree]
    val targetApplySym = typeOf[Applyable[Nothing, _]].member(TermName("apply"))

    val itemsName = c.freshName(TermName("items"))
    val itemsSym = c.internal.newTermSymbol(c.internal.enclosingOwner, itemsName)
    c.internal.setFlag(itemsSym, (1L << 44).asInstanceOf[c.universe.FlagSet])
    c.internal.setInfo(itemsSym, typeOf[Seq[Any]])
    // Derived from @olafurpg's
    // https://gist.github.com/olafurpg/596d62f87bf3360a29488b725fbc7608
    val defs = rec(t).filter(_.isDef).map(_.symbol).toSet

    val ctxName = TermName(c.freshName("ctx"))
    val ctxSym = c.internal.newTermSymbol(c.internal.enclosingOwner, ctxName)
    c.internal.setInfo(ctxSym, weakTypeOf[Ctx])

    val transformed = c.internal.typingTransform(t) {
      case (t @ q"$fun.apply()($handler)", api) if t.symbol == targetApplySym =>
        val localDefs = rec(fun).filter(_.isDef).map(_.symbol).toSet
        val banned = rec(t).filter(x => defs(x.symbol) && !localDefs(x.symbol))

        if (banned.hasNext) {
          val banned0 = banned.next()
          c.abort(
            banned0.pos,
            "Target#apply() call cannot use `" + banned0.symbol + "` defined within the Task{...} block"
          )
        }
        val tempName = c.freshName(TermName("tmp"))
        val tempSym = c.internal.newTermSymbol(c.internal.enclosingOwner, tempName)
        c.internal.setInfo(tempSym, t.tpe)
        val tempIdent = Ident(tempSym)
        c.internal.setType(tempIdent, t.tpe)
        c.internal.setFlag(tempSym, (1L << 44).asInstanceOf[c.universe.FlagSet])
        val itemsIdent = Ident(itemsSym)
        exprs.append(q"$fun")
        c.typecheck(q"$itemsIdent(${exprs.size - 1}).asInstanceOf[${t.tpe}]")
      case (t, api)
          if t.symbol != null
            && t.symbol.annotations.exists(_.tree.tpe =:= typeOf[mill.api.Ctx.ImplicitStub]) =>
        val tempIdent = Ident(ctxSym)
        c.internal.setType(tempIdent, t.tpe)
        c.internal.setFlag(ctxSym, (1L << 44).asInstanceOf[c.universe.FlagSet])
        tempIdent

      case (t, api) => api.default(t)
    }

    val ctxBinding = c.internal.valDef(ctxSym)

    val itemsBinding = c.internal.valDef(itemsSym)
    val callback = c.typecheck(q"{(${itemsBinding}, ${ctxBinding}) => $transformed}")

    val res =
      q"${c.prefix}.traverseCtx[_root_.scala.Any, ${weakTypeOf[T]}](${exprs.toList}){ $callback }"

    c.internal.changeOwner(transformed, c.internal.enclosingOwner, callback.symbol)

    c.Expr[M[T]](res)
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy