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

shapeless.orphans.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2015-16 Miles Sabin
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package shapeless

import scala.language.experimental.macros

import scala.reflect.macros.whitebox

case class Orphan[F[_], D, T](instance: F[T])

object Orphan {
  implicit def materializeOrphan[F[_], D, T]: Orphan[F, D, T] = macro OrphanMacros.materializeOrphanImpl[F, D, T]
}

case class WrappedOrphan[T](instance: T)

object WrappedOrphan {
  implicit def apply[T]: WrappedOrphan[T] = macro OrphanMacros.materializeWrapped[T]
}

trait OrphanDeriver[F[_], D] {
  implicit def materialize[T]: F[T] = macro OrphanMacros.materializeImpl[F, D, T]
}

@macrocompat.bundle
class OrphanMacros(val c: whitebox.Context) extends CaseClassMacros {
  import c.universe._

  def materializeImpl[F[_], D, T]
    (implicit fTag: WeakTypeTag[F[_]], dTag: WeakTypeTag[D], tTag: WeakTypeTag[T]): Tree =
    materializeAux[F, D, T](false)

  def materializeOrphanImpl[F[_], D, T]
    (implicit fTag: WeakTypeTag[F[_]], dTag: WeakTypeTag[D], tTag: WeakTypeTag[T]): Tree = {
    val inst = materializeAux[F, D, T](true)
    val fTpe = fTag.tpe.typeConstructor
    val dTpe = dTag.tpe
    val tTpe = tTag.tpe

    q"""
      new _root_.shapeless.Orphan[$fTpe, $dTpe, $tTpe]($inst)
     """
  }

  def materializeAux[F[_], D, T](proxied: Boolean)
    (implicit fTag: WeakTypeTag[F[_]], dTag: WeakTypeTag[D], tTag: WeakTypeTag[T]): Tree = {
    val fTcTpe = fTag.tpe.typeConstructor
    val dTpe = dTag.tpe
    val tTpe = tTag.tpe
    val appTpe = appliedType(fTcTpe, List(tTpe))

    val open = c.openImplicits
    val materializerIdx = if(proxied) 1 else 0
    val materializer = open(materializerIdx)
    val checkIdx = (materializerIdx*2)+1
    if(open.size > checkIdx) {
      val check = open(checkIdx)
      if(materializer.sym == check.sym && materializer.pre =:= check.pre && materializer.pt =:= check.pt)
        c.abort(c.enclosingPosition, "Backtrack")
    }

    val deriver =
      dTpe match {
        case SingleType(pre, sym) => mkAttributedRef(pre, sym)
        case other =>
          c.abort(c.enclosingPosition, "Deriver $dTpe not found")
      }

    val inst = c.inferImplicitValue(appTpe, silent = true)

    val masks =
      if(!proxied) List(q"def materialize = ???")
      else {
        val proxyOwner = materializer.sym.owner
        val proxyTpe = proxyOwner.typeSignature
        val proxyNames = proxyTpe.members.filter(_.isImplicit).map(_.name)

        proxyNames.map { name => q"def ${name.toTermName} = ???" }
      }

    val probe =
      q"""
        ..$masks
        import $deriver._
        _root_.shapeless.the[$appTpe]
       """

    val checkedProbe = c.typecheck(probe, pt = appTpe, silent = true)
    if(checkedProbe == EmptyTree) {
      if(inst == EmptyTree) {
        c.abort(c.enclosingPosition, s"No derived instance $appTpe")
      } else {
        inst
      }
    } else {
      val derived = checkedProbe match {
        case b: Block => b.expr
      }

      if(derived.equalsStructure(inst)) inst
      else if(inst == EmptyTree) derived
      else {
        val resTpeD = derived.symbol.asMethod.info.finalResultType
        val resTpeI = inst.symbol.asMethod.info.finalResultType

        val useDerived =
          resTpeD.typeArgs.zip(resTpeI.typeArgs).forall { case (ad, ai) =>
            ai.typeSymbol.isParameter ||
            (!ad.typeSymbol.isParameter && !(ad <:< ai))
          }

        if(useDerived) derived else inst
      }
    }
  }

  def materializeWrapped[T](implicit tTag: WeakTypeTag[T]): Tree = {
    val open = c.openImplicits
    val masks =
      if(open.size < 2) Nil
      else {
        val sym = open(1).sym
        List(q"def ${sym.name.toTermName} = ???")
      }

    val tpe = tTag.tpe
    q"""
    {
      ..$masks
      _root_.shapeless.WrappedOrphan[$tpe](_root_.shapeless.lazily[$tpe])
    }
    """
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy