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

dfhdl.core.DFOpaque.scala Maven / Gradle / Ivy

package dfhdl.core
import dfhdl.compiler.ir
import dfhdl.compiler.printing.{DefaultPrinter, Printer}
import dfhdl.internals.*
import scala.quoted.*

import scala.annotation.unchecked.uncheckedVariance
import scala.annotation.targetName

type DFOpaque[+TFE <: DFOpaque.Abstract] =
  DFType[ir.DFOpaque, Args1[TFE @uncheckedVariance]]
object DFOpaque:
  protected[core] sealed trait Abstract extends HasTypeName, ir.DFOpaque.CustomId:
    type ActualType <: DFTypeAny
    protected[core] val actualType: ActualType
  object Abstract:
    inline implicit def fromComp[TFE <: Abstract, Comp <: AnyRef](tfeComp: Comp)(implicit
        cc: CaseClass[Comp, TFE]
    ): cc.CC = compiletime.summonInline[ClassEv[cc.CC]].value
    def fromCompMacro[TFE <: Abstract, Comp <: AnyRef](using
        Quotes,
        Type[TFE],
        Type[Comp]
    ): Expr[TFE] =
      import quotes.reflect.*
      val compTpe = TypeRepr.of[Comp]
      val tfeTpe = compTpe.getCompanionClassTpe
      val tfeType = tfeTpe.asTypeOf[TFE]
      '{
        compiletime.summonInline[ClassEv[tfeType.Underlying]].value
      }
  end Abstract

  abstract class Frontend[A <: DFTypeAny](final protected[core] val actualType: A) extends Abstract:
    type ActualType = A

  given [TFE <: Abstract](using ce: ClassEv[TFE]): DFOpaque[TFE] = DFOpaque(ce.value)

  def apply[TFE <: Abstract](
      t: TFE
  ): DFOpaque[TFE] =
    ir.DFOpaque(t.typeName, t, t.actualType.asIR).asFE[DFOpaque[TFE]]
  extension [A <: DFTypeAny, TFE <: Frontend[A]](dfType: DFOpaque[TFE])
    def actualType: A = dfType.asIR.actualType.asFE[A]
    def opaqueType: TFE = dfType.asIR.id.asInstanceOf[TFE]

  object Val:
    object TC:
      import DFVal.TC
      given DFOpaqueValFromDFOpaqueVal[
          TFE <: Abstract,
          RT <: Abstract,
          RP,
          V <: DFValTP[DFOpaque[RT], RP]
      ](using RT <:< TFE): TC[DFOpaque[TFE], V] with
        type OutP = RP
        def conv(dfType: DFOpaque[TFE], value: V)(using DFC): Out =
          value.asValTP[DFOpaque[TFE], RP]

    object Ops:
      extension [L](inline lhs: L)
        transparent inline def as[Comp <: AnyRef](tfeComp: Comp): Any = ${
          asMacro[L, Comp]('lhs, 'tfeComp)
        }
      private def asDFVector[A <: DFTypeAny](dfVals: Iterable[DFValOf[A]])(using
          DFC
      ): DFValOf[DFVector[A, Tuple1[Int]]] =
        val dfType = DFVector(dfVals.head.dfType, Tuple1(dfVals.size))
        DFVal.Func(dfType, DFVal.Func.Op.++, dfVals.toList)
      extension [A <: DFTypeAny](lhs: Iterable[DFValOf[A]])
        transparent inline def as[Comp <: AnyRef](
            tfeComp: Comp
        )(using DFC): Any = ${
          asMacro[DFValOf[DFVector[A, Tuple1[Int]]], Comp]('{ asDFVector(lhs) }, 'tfeComp)
        }
      private def asMacro[L, Comp <: AnyRef](
          lhs: Expr[L],
          tfeComp: Expr[Comp]
      )(using Quotes, Type[L], Type[Comp]): Expr[Any] =
        import quotes.reflect.*
        val compTpe = TypeRepr.of[Comp]
        val (tfe, tfeTpe) =
          if (compTpe <:< TypeRepr.of[Abstract]) (tfeComp.asExprOf[Abstract], compTpe)
          else
            val tfeTpe = compTpe.getCompanionClassTpe
            val tfeType = tfeTpe.asTypeOf[Abstract]
            val tfe = '{
              compiletime
                .summonInline[ClassEv[tfeType.Underlying]]
                .value
            }
            (tfe, tfeTpe)
        val tfeType = tfeTpe.asTypeOf[Abstract]
        tfeTpe.baseType(TypeRepr.of[Frontend[? <: DFTypeAny]].typeSymbol) match
          case AppliedType(_, aTpe :: _) =>
            val aType = aTpe.asTypeOf[DFTypeAny]
            val lhsTerm = lhs.asTerm.exactTerm
            val lhsTpe = lhsTerm.tpe
            val lhsExpr = lhsTerm.asExpr
            // println(lhsExpr.show)
            // println(lhsTpe.show)
            val lhsType = lhsTpe.asTypeOf[Any]
            val pType = lhsTpe.isConstTpe.asTypeOf[Any]
            val aExpr = '{ $tfe.actualType.asInstanceOf[aType.Underlying] }
            '{
              val tc = compiletime.summonInline[DFVal.TC[aType.Underlying, lhsType.Underlying]]
              val dfc = compiletime.summonInline[DFC]
              DFVal.Alias.AsIs(
                DFOpaque[tfeType.Underlying]($tfe),
                tc($aExpr, $lhsExpr)(using dfc)
              )(using dfc)
                // TODO: `P` should be automatically derived from tc.OutP, but there is issue
                // https://github.com/lampepfl/dotty/issues/19554
                .asValTP[DFOpaque[tfeType.Underlying], pType.Underlying]
            }
          case _ =>
            report.errorAndAbort("Not a valid opaque type companion.")
        end match
      end asMacro

      extension [AT <: DFTypeAny, TFE <: Frontend[AT], A, P](
          lhs: DFVal[DFOpaque[TFE], Modifier[A, Any, Any, P]]
      )
        def opaqueType: TFE = lhs.dfType.asIR.id.asInstanceOf[TFE]
        def actual(using DFC): DFVal[AT, Modifier[A, Any, Any, P]] = trydf {
          DFVal.Alias.AsIs(lhs.dfType.actualType, lhs)
        }
        def actualMap(
            f: DFValOf[AT] => DFValOf[AT]
        )(using dfc: DFC, ce: ClassEv[TFE]): DFValOf[DFOpaque[TFE]] =
          DFVal.Alias.AsIs(
            DFOpaque[TFE](ce.value),
            f(lhs.actual)
          )
      end extension
    end Ops
  end Val

end DFOpaque




© 2015 - 2024 Weber Informatics LLC | Privacy Policy