<commit_before>package scalaxy.reified

import scala.language.experimental.macros

import scala.reflect._
import scala.reflect.macros.Context
import scala.reflect.runtime.universe

import scalaxy.reified.internal.Utils._

package object internal {

  private def runtimeExpr[A](c: Context)(tree: c.universe.Tree): c.Expr[universe.Expr[A]] = {
    c.Expr[universe.Expr[A]](
      c.reifyTree(
        c.universe.treeBuild.mkRuntimeUniverseRef,
        c.universe.EmptyTree,
        tree
      )
    )
  }

  private[reified] def reifyMacro[A: universe.TypeTag](v: A): ReifiedValue[A] = macro reifyImpl[A]
  private[reified] def reifyWithDifferentRuntimeValue[A: universe.TypeTag](v: A, runtimeValue: A): ReifiedValue[A] = macro reifyWithDifferentRuntimeValueImpl[A]

  def reifyImpl[A: c.WeakTypeTag](c: Context)(v: c.Expr[A])(tt: c.Expr[universe.TypeTag[A]]): c.Expr[ReifiedValue[A]] = {

    import c.universe._

    val (expr, capturesExpr) = transformReifiedRefs(c)(v)
    reify({
      implicit val valueTag = tt.splice
      new ReifiedValue[A](
        v.splice,
        Utils.typeCheck(expr.splice, valueTag.tpe),
        capturesExpr.splice)
    })
  }

  def reifyWithDifferentRuntimeValueImpl[A: c.WeakTypeTag](c: Context)(v: c.Expr[A], runtimeValue: c.Expr[A])(tt: c.Expr[universe.TypeTag[A]]): c.Expr[ReifiedValue[A]] = {

    import c.universe._

    val (expr, capturesExpr) = transformReifiedRefs(c)(v)
    reify({
      implicit val valueTag = tt.splice
      new ReifiedValue[A](
        runtimeValue.splice,
        Utils.typeCheck(expr.splice, valueTag.tpe),
        capturesExpr.splice)
    })
  }

  /**
   * Detect captured references, replace them by capture tags and
   *  return their ordered list along with the resulting tree.
   */
  private def transformReifiedRefs[A](c: Context)(expr: c.Expr[A]): (c.Expr[universe.Expr[A]], c.Expr[Seq[(AnyRef, universe.Type)]]) = {
    import c.universe._
    import definitions._

    val tree = c.typeCheck(expr.tree)

    val localDefSyms = collection.mutable.HashSet[Symbol]()
    def isDefLike(t: Tree) = t match {
      case Template(_, _, _) => true
      case Function(_, _) => true
      case _ if t.isDef => true
      case _ => false
    }
    (new Traverser {
      override def traverse(t: Tree) {
        super.traverse(t)
        if (t.symbol != null && isDefLike(t)) {
          localDefSyms += t.symbol
        }
      }
    }).traverse(tree)

    var lastCaptureIndex = -1
    val capturedTerms = collection.mutable.ArrayBuffer[(Tree, Type)]()
    val capturedSymbols = collection.mutable.HashMap[TermSymbol, Int]()
    val capturedTypeTags = collection.mutable.Set[String]()

    val transformer = new Transformer {
      override def transform(t: Tree): Tree = {
        // TODO check which types can be captured
        val sym = t.symbol
        if (sym != null && !isDefLike(t) && sym.isTerm && !localDefSyms.contains(sym)) {
          val tsym = sym.asTerm
          if (tsym.isVar) {
            c.error(t.pos, "Cannot capture this var: " + tsym)
            t
          } else if (tsym.isLazy && tsym.fullName != "scala.reflect.runtime.universe") {
            c.error(t.pos, "Cannot capture this lazy val: " + tsym)
            t
          } else if (tsym.isMethod || tsym.isModule && tsym.isStable) {
            super.transform(t)
          } else if (tsym.isVal || tsym.isAccessor) {
            val captureIndexExpr = c.literal(
              capturedSymbols.get(tsym) match {
                case Some(i) =>
                  i
                case None =>
                  lastCaptureIndex += 1
                  capturedSymbols += tsym -> lastCaptureIndex
                  capturedTerms += Ident(tsym) -> t.tpe

                  println("Capturing " + t) // + " (symbol: " + tsym + ": " + tsym.getClass.getName + ")")

                  lastCaptureIndex
              }
            )

            val tpe = t.tpe.normalize.widen
            // Abuse reify to get correct reference to `capture`.
            val Apply(TypeApply(f, List(_)), _) = {
              reify(scalaxy.reified.internal.CaptureTag[Int](10, 1)).tree
            }
            c.typeCheck(
              Apply(
                TypeApply(
                  f,
                  List(TypeTree(tpe))),
                List(t, captureIndexExpr.tree)),
              tpe)
          } else {
            c.error(t.pos, s"Cannot capture this type of expression (symbol = $tsym): " + tsym)
            t
          }
        } else {
          super.transform(t)
        }
      }
    }

    val transformed = transformer.transform(tree)
    val transformedExpr = runtimeExpr[A](c)(transformed)
    val capturesArrayExpr = {
      // Abuse reify to get correct seq constructor.
      val Apply(seqConstructor, _) = reify(Seq[(AnyRef, universe.Type)]()).tree
      // Build the list of captured terms (with their types).
      c.Expr[Seq[(AnyRef, universe.Type)]](
        c.typeCheck(
          Apply(
            seqConstructor,
            capturedTerms.map({
              case (term, tpe) =>
                val termExpr = c.Expr[Any](term)
                val typeExpr = c.Expr[universe.TypeTag[_]](
                  c.reifyType(
                    treeBuild.mkRuntimeUniverseRef,
                    Select(
                      treeBuild.mkRuntimeUniverseRef,
                      newTermName("rootMirror")
                    ),
                    tpe.normalize.widen
                  )
                )
                reify({
                  (
                    termExpr.splice.asInstanceOf[AnyRef],
                    typeExpr.splice.tpe
                  )
                }).tree
            }).toList),
          c.typeOf[Seq[(AnyRef, universe.Type)]]))
    }
    (transformedExpr, capturesArrayExpr)
  }
}
<commit_msg>Reified: log info when capturing symbol<commit_after>package scalaxy.reified

import scala.language.experimental.macros

import scala.reflect._
import scala.reflect.macros.Context
import scala.reflect.runtime.universe

import scalaxy.reified.internal.Utils._

package object internal {

  private def runtimeExpr[A](c: Context)(tree: c.universe.Tree): c.Expr[universe.Expr[A]] = {
    c.Expr[universe.Expr[A]](
      c.reifyTree(
        c.universe.treeBuild.mkRuntimeUniverseRef,
        c.universe.EmptyTree,
        tree
      )
    )
  }

  private[reified] def reifyMacro[A: universe.TypeTag](v: A): ReifiedValue[A] = macro reifyImpl[A]
  private[reified] def reifyWithDifferentRuntimeValue[A: universe.TypeTag](v: A, runtimeValue: A): ReifiedValue[A] = macro reifyWithDifferentRuntimeValueImpl[A]

  def reifyImpl[A: c.WeakTypeTag](c: Context)(v: c.Expr[A])(tt: c.Expr[universe.TypeTag[A]]): c.Expr[ReifiedValue[A]] = {

    import c.universe._

    val (expr, capturesExpr) = transformReifiedRefs(c)(v)
    reify({
      implicit val valueTag = tt.splice
      new ReifiedValue[A](
        v.splice,
        Utils.typeCheck(expr.splice, valueTag.tpe),
        capturesExpr.splice)
    })
  }

  def reifyWithDifferentRuntimeValueImpl[A: c.WeakTypeTag](c: Context)(v: c.Expr[A], runtimeValue: c.Expr[A])(tt: c.Expr[universe.TypeTag[A]]): c.Expr[ReifiedValue[A]] = {

    import c.universe._

    val (expr, capturesExpr) = transformReifiedRefs(c)(v)
    reify({
      implicit val valueTag = tt.splice
      new ReifiedValue[A](
        runtimeValue.splice,
        Utils.typeCheck(expr.splice, valueTag.tpe),
        capturesExpr.splice)
    })
  }

  /**
   * Detect captured references, replace them by capture tags and
   *  return their ordered list along with the resulting tree.
   */
  private def transformReifiedRefs[A](c: Context)(expr: c.Expr[A]): (c.Expr[universe.Expr[A]], c.Expr[Seq[(AnyRef, universe.Type)]]) = {
    import c.universe._
    import definitions._

    val tree = c.typeCheck(expr.tree)

    val localDefSyms = collection.mutable.HashSet[Symbol]()
    def isDefLike(t: Tree) = t match {
      case Template(_, _, _) => true
      case Function(_, _) => true
      case _ if t.isDef => true
      case _ => false
    }
    (new Traverser {
      override def traverse(t: Tree) {
        super.traverse(t)
        if (t.symbol != null && isDefLike(t)) {
          localDefSyms += t.symbol
        }
      }
    }).traverse(tree)

    var lastCaptureIndex = -1
    val capturedTerms = collection.mutable.ArrayBuffer[(Tree, Type)]()
    val capturedSymbols = collection.mutable.HashMap[TermSymbol, Int]()
    val capturedTypeTags = collection.mutable.Set[String]()

    val transformer = new Transformer {
      override def transform(t: Tree): Tree = {
        // TODO check which types can be captured
        val sym = t.symbol
        if (sym != null && !isDefLike(t) && sym.isTerm && !localDefSyms.contains(sym)) {
          val tsym = sym.asTerm
          if (tsym.isVar) {
            c.error(t.pos, "Cannot capture this var: " + tsym)
            t
          } else if (tsym.isLazy && tsym.fullName != "scala.reflect.runtime.universe") {
            c.error(t.pos, "Cannot capture this lazy val: " + tsym)
            t
          } else if (tsym.isMethod || tsym.isModule && tsym.isStable) {
            super.transform(t)
          } else if (tsym.isVal || tsym.isAccessor) {
            val captureIndexExpr = c.literal(
              capturedSymbols.get(tsym) match {
                case Some(i) =>
                  i
                case None =>
                  c.info(t.pos, "Reified value will capture " + tsym, false)

                  lastCaptureIndex += 1
                  capturedSymbols += tsym -> lastCaptureIndex
                  capturedTerms += Ident(tsym) -> t.tpe

                  lastCaptureIndex
              }
            )

            val tpe = t.tpe.normalize.widen
            // Abuse reify to get correct reference to `capture`.
            val Apply(TypeApply(f, List(_)), _) = {
              reify(scalaxy.reified.internal.CaptureTag[Int](10, 1)).tree
            }
            c.typeCheck(
              Apply(
                TypeApply(
                  f,
                  List(TypeTree(tpe))),
                List(t, captureIndexExpr.tree)),
              tpe)
          } else {
            c.error(t.pos, s"Cannot capture this type of expression (symbol = $tsym): " + tsym)
            t
          }
        } else {
          super.transform(t)
        }
      }
    }

    val transformed = transformer.transform(tree)
    val transformedExpr = runtimeExpr[A](c)(transformed)
    val capturesArrayExpr = {
      // Abuse reify to get correct seq constructor.
      val Apply(seqConstructor, _) = reify(Seq[(AnyRef, universe.Type)]()).tree
      // Build the list of captured terms (with their types).
      c.Expr[Seq[(AnyRef, universe.Type)]](
        c.typeCheck(
          Apply(
            seqConstructor,
            capturedTerms.map({
              case (term, tpe) =>
                val termExpr = c.Expr[Any](term)
                val typeExpr = c.Expr[universe.TypeTag[_]](
                  c.reifyType(
                    treeBuild.mkRuntimeUniverseRef,
                    Select(
                      treeBuild.mkRuntimeUniverseRef,
                      newTermName("rootMirror")
                    ),
                    tpe.normalize.widen
                  )
                )
                reify({
                  (
                    termExpr.splice.asInstanceOf[AnyRef],
                    typeExpr.splice.tpe
                  )
                }).tree
            }).toList),
          c.typeOf[Seq[(AnyRef, universe.Type)]]))
    }
    (transformedExpr, capturesArrayExpr)
  }
}

{'fraction_non_alphanumeric': 0.11823975198070961, 'fraction_numerical': 0.000861178091629349, 'mean_word_length': 1.911255953873151, 'pattern_counts': {'":': 1, '<': 3, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2369950', 'n_tokens_mistral': 3590, 'n_tokens_neox': 3468, 'n_words': 721}