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

japgolly.microlibs.compiletime.MacroEnv.scala Maven / Gradle / Ivy

There is a newer version: 4.2.1
Show newest version
package japgolly.microlibs.compiletime

import scala.annotation.targetName
import scala.quoted.*

object MacroEnvStatic {
  import MacroEnv.*

  inline def summonLater[A]: A =
    scala.compiletime.summonInline[A]
}

object MacroEnv {

  export japgolly.microlibs.compiletime.{
    ExprMap,
    ExprSet,
    Field,
    Fields,
    Init,
    MacroUtils,
  }
  export EasierValDef.*

  def fail(msg: String)(using Quotes): Nothing =
    import quotes.reflect.*
    quotes.reflect.report.throwError(msg, Position.ofMacroExpansion)

  def failNoStack(msg: String): Nothing = {
    val e = new RuntimeException(msg)
    e.setStackTrace(Array.empty)
    throw e
  }

  // ===================================================================================================================
  extension (unused: Expr.type) {

    /** Requires that macro be transparent.
      *
      * https://github.com/lampepfl/dotty/issues/12359
      */
    def summonLater[A: Type](using Quotes): Expr[A] =
      '{ MacroEnvStatic.summonLater[A] }

    def summonOrError[A](using Type[A])(using Quotes): Expr[A] =
      import quotes.reflect.*
      Implicits.search(TypeRepr.of[A]) match
        case iss: ImplicitSearchSuccess => iss.tree.asExpr.asInstanceOf[Expr[A]]
        case isf: ImplicitSearchFailure => report.throwError(isf.explanation)

    def inlineConstNull(using Quotes): Expr[Null] =
      import quotes.reflect.*
      Literal(NullConstant()).asInlineExprOf[Null]

    def inlineConstUnit(using Quotes): Expr[Unit] =
      import quotes.reflect.*
      Literal(UnitConstant()).asInlineExprOf[Unit]

    def inlineConst(value: Boolean)(using Quotes): Expr[Boolean] =
      import quotes.reflect.*
      Literal(BooleanConstant(value)).asInlineExprOf[Boolean]

    def inlineConst(value: Byte)(using Quotes): Expr[Byte] =
      import quotes.reflect.*
      Literal(ByteConstant(value)).asInlineExprOf[Byte]

    def inlineConst(value: Short)(using Quotes): Expr[Short] =
      import quotes.reflect.*
      Literal(ShortConstant(value)).asInlineExprOf[Short]

    def inlineConst(value: Int)(using Quotes): Expr[Int] =
      import quotes.reflect.*
      Literal(IntConstant(value)).asInlineExprOf[Int]

    def inlineConst(value: Long)(using Quotes): Expr[Long] =
      import quotes.reflect.*
      Literal(LongConstant(value)).asInlineExprOf[Long]

    def inlineConst(value: Float)(using Quotes): Expr[Float] =
      import quotes.reflect.*
      Literal(FloatConstant(value)).asInlineExprOf[Float]

    def inlineConst(value: Double)(using Quotes): Expr[Double] =
      import quotes.reflect.*
      Literal(DoubleConstant(value)).asInlineExprOf[Double]

    def inlineConst(value: Char)(using Quotes): Expr[Char] =
      import quotes.reflect.*
      Literal(CharConstant(value)).asInlineExprOf[Char]

    def inlineConst(value: String)(using Quotes): Expr[String] =
      inlineConstOrNull(value)

    def inlineConstOrNull(s: String | Null)(using Quotes): Expr[String | Null] =
      import quotes.reflect.*
      val const = if s == null then NullConstant() else StringConstant(s)
      Literal(const).asInlineExprOf[String | Null]

    // def productToTuple[P](using m: Mirror.ProductOf[P]): Expr[P => m.MirroredElemTypes] =
    //   1

    // def tupleToProduct[P](using m: Mirror.ProductOf[P]): Expr[m.MirroredElemTypes => P] =
    //   1
  }


  // ===================================================================================================================
  extension [A] (self: Expr[A]) {

    def inlined(using Quotes, Type[A]): Expr[A] =
      import quotes.reflect.*
      Inlined(None, Nil, self.asTerm).asExprOf[A]

    def showType(using Quotes): String =
      import quotes.reflect.*
      self.asTerm.tpe.show

    def tapShow()(using Quotes): Expr[A] =
      println(s"\n${self.show}\n")
      self

    def castTo[B](using Quotes, Type[A], Type[B]): Expr[B] =
      '{ $self.asInstanceOf[B] }

    def prepend[B](e: Expr[B])(using Quotes, Type[A], Type[B]): Expr[A] =
      '{ $e; $self }

    def prependPrintln(msg: String)(using Quotes, Type[A]): Expr[A] =
      prependPrintln(Expr.inlineConst(msg))

    def prependPrintln(msg: Expr[String])(using Quotes, Type[A]): Expr[A] =
      prepend('{ println($msg) })
  }

  // ===================================================================================================================

  extension [A, Z] (self: Expr[A => Z])
    def apply(a: Expr[A])(using Quotes, Type[A], Type[Z]): Expr[Z] =
      Expr.betaReduce('{ $self($a) })

  extension [A, B, Z] (self: Expr[(A, B) => Z])
    def apply(a: Expr[A], b: Expr[B])(using Quotes, Type[A], Type[B], Type[Z]): Expr[Z] =
      Expr.betaReduce('{ $self($a, $b) })

  extension [A, B, C, Z] (self: Expr[(A, B, C) => Z])
    def apply(a: Expr[A], b: Expr[B], c: Expr[C])(using Quotes, Type[A], Type[B], Type[C], Type[Z]): Expr[Z] =
      Expr.betaReduce('{ $self($a, $b, $c) })

  extension [A, B, C, D, Z] (self: Expr[(A, B, C, D) => Z])
    def apply(a: Expr[A], b: Expr[B], c: Expr[C], d: Expr[D])(using Quotes, Type[A], Type[B], Type[C], Type[D], Type[Z]): Expr[Z] =
      Expr.betaReduce('{ $self($a, $b, $c, $d) })

  extension [A, B, C, D, E, Z] (self: Expr[(A, B, C, D, E) => Z])
    def apply(a: Expr[A], b: Expr[B], c: Expr[C], d: Expr[D], e: Expr[E])(using Quotes, Type[A], Type[B], Type[C], Type[D], Type[E], Type[Z]): Expr[Z] =
      Expr.betaReduce('{ $self($a, $b, $c, $d, $e) })

  // ===================================================================================================================
  extension [F[_], A] (self: Expr[F[A]]) {

    inline def asExprOfF[B]: Expr[F[B]] =
      self.asInstanceOf[Expr[F[B]]]

    def castToF[B](using Quotes, Type[F], Type[A], Type[B]): Expr[F[B]] =
      '{ $self.asInstanceOf[F[B]] }

    inline def asExprOfFAny: Expr[F[Any]] =
      asExprOfF[Any]

    def castToFAny(using Quotes, Type[F], Type[A]): Expr[F[Any]] =
      castToF[Any]
  }

  // ===================================================================================================================
  extension [A](self: Type[A]) {

    def dealias(using Quotes): Type[A] =
      _type_dealias[A](using self)

    def summonOrError(using Quotes): Expr[A] =
      Expr.summonOrError[A](using self)
  }

  private def _type_dealias[A](using Type[A])(using Quotes): Type[A] =
    import quotes.reflect.*
    TypeRepr.of[A].dealias.asType.asInstanceOf[Type[A]]

  // ===================================================================================================================
  extension (using q: Quotes)(self: q.reflect.TypeRepr) {

    def asTypeTree: q.reflect.TypeTree =
      q.reflect.TypeTree.of(using self.asType)

    def exists: Boolean =
      import q.reflect.*
      self <:< TypeRepr.of[AnyRef] || self <:< TypeRepr.of[AnyVal]

    @targetName("summon_TypeRepr")
    def summon: Option[Expr[?]] =
      self.asType match
        case '[a] => Expr.summon[a]

    @targetName("summonOrError_TypeRepr")
    def summonOrError: Expr[?] =
      self.asType match
        case '[a] => Type.of[a].summonOrError
  }

  // ===================================================================================================================
  extension (using q: Quotes)(self: q.reflect.TypeTree) {

    inline def asType: Type[?] =
      self.tpe.asType

    @targetName("summon_TypeTree")
    def summon: Option[Expr[?]] =
      self.tpe.summon

    @targetName("summonOrError_TypeTree")
    def summonOrError: Expr[?] =
      self.tpe.summonOrError
  }

  // ===================================================================================================================
  extension (using q: Quotes)(self: q.reflect.Term) {

    def asConstant: q.reflect.Constant =
      import q.reflect.*
      self match
        case Literal(c) => c
        case x          => fail(s"Expected a constant literal, got: ${x.show} ")

    def simplify: q.reflect.Term =
      import q.reflect.*
      self match
        case Block(_, t) => t
        case _           => self

    def asInlineExprOf[A](using Type[A]): Expr[A] =
      import q.reflect.*
      Inlined(None, Nil, self).asExprOf[A]

    def implicitlyConvertTo[B](using Type[B]): Option[q.reflect.Term] =
      import quotes.reflect.*
      if self.tpe <:< TypeRepr.of[B] then
        Some(self)
      else
        self.tpe.asType match
          case '[t] =>
            Expr.summon[t => B].map { i =>
              val e = self.asExprOf[t]
              Expr.betaReduce('{ $i($e) }).asTerm
            }

    def implicitlyConvertToOrError[B](using Type[B]): q.reflect.Term =
      self.implicitlyConvertTo[B].getOrElse {
        val a = self.tpe.show
        val msg = s"Can't convert $a to ${Type.show[B]}"
        import quotes.reflect.*
        report.throwError(msg, Position.ofMacroExpansion)
      }
  }

  // ===================================================================================================================
  extension (using q: Quotes)(self: q.reflect.Symbol) {

    def getType: Option[q.reflect.TypeRepr] =
      import q.reflect.*
      self.tree match
        case ValDef(_, t, _)    => Some(t.tpe)
        case DefDef(_, _, t, _) => Some(t.tpe)
        case _                  => None

    inline def needType(inline descType: String): q.reflect.TypeRepr =
      self.needType(_ => descType)

    def needType(descType: q.reflect.Symbol => String): q.reflect.TypeRepr =
      self.getType.getOrElse(fail(s"Unable to determine type of ${descType(self)}"))

    /** Oldest returned first */
    def ownerPath: List[q.reflect.Symbol] =
      import q.reflect.*
      def loop(s: Symbol, acc: List[Symbol]): List[Symbol] =
        if s.isNoSymbol then
          acc
        else
          loop(s.maybeOwner, s :: acc)
      if self.isNoSymbol then
        self :: Nil
      else
        loop(self, Nil)

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy