mirror of https://github.com/sbt/sbt.git
Reformat all util-appmacro
This commit is contained in:
parent
ed38fcd695
commit
d2f019a47a
|
|
@ -251,13 +251,13 @@ lazy val commandProj = (project in file("main-command"))
|
|||
|
||||
// The core macro project defines the main logic of the DSL, abstracted
|
||||
// away from several sbt implementators (tasks, settings, et cetera).
|
||||
lazy val coreMacrosProj = (project in file("core-macros")).
|
||||
settings(
|
||||
lazy val coreMacrosProj = (project in file("core-macros"))
|
||||
.settings(
|
||||
commonSettings,
|
||||
name := "Core Macros",
|
||||
libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value
|
||||
)
|
||||
.configure(addSbtUtilCollection)
|
||||
.configure(addSbtUtilCollection)
|
||||
|
||||
// Fixes scope=Scope for Setting (core defined in collectionProj) to define the settings system used in build definitions
|
||||
lazy val mainSettingsProj = (project in file("main-settings"))
|
||||
|
|
|
|||
|
|
@ -22,16 +22,17 @@ object ContextUtil {
|
|||
* Given `myImplicitConversion(someValue).extensionMethod`, where `extensionMethod` is a macro that uses this
|
||||
* method, the result of this method is `f(<Tree of someValue>)`.
|
||||
*/
|
||||
def selectMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(f: (c.Expr[Any], c.Position) => c.Expr[T]): c.Expr[T] =
|
||||
{
|
||||
import c.universe._
|
||||
c.macroApplication match {
|
||||
case s @ Select(Apply(_, t :: Nil), tp) => f(c.Expr[Any](t), s.pos)
|
||||
case x => unexpectedTree(x)
|
||||
}
|
||||
def selectMacroImpl[T: c.WeakTypeTag](c: blackbox.Context)(
|
||||
f: (c.Expr[Any], c.Position) => c.Expr[T]): c.Expr[T] = {
|
||||
import c.universe._
|
||||
c.macroApplication match {
|
||||
case s @ Select(Apply(_, t :: Nil), tp) => f(c.Expr[Any](t), s.pos)
|
||||
case x => unexpectedTree(x)
|
||||
}
|
||||
}
|
||||
|
||||
def unexpectedTree[C <: blackbox.Context](tree: C#Tree): Nothing = sys.error("Unexpected macro application tree (" + tree.getClass + "): " + tree)
|
||||
def unexpectedTree[C <: blackbox.Context](tree: C#Tree): Nothing =
|
||||
sys.error("Unexpected macro application tree (" + tree.getClass + "): " + tree)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -68,15 +69,14 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
* Constructs a new, synthetic, local ValDef Type `tpe`, a unique name,
|
||||
* Position `pos`, an empty implementation (no rhs), and owned by `owner`.
|
||||
*/
|
||||
def freshValDef(tpe: Type, pos: Position, owner: Symbol): ValDef =
|
||||
{
|
||||
val SYNTHETIC = (1 << 21).toLong.asInstanceOf[FlagSet]
|
||||
val sym = owner.newTermSymbol(freshTermName("q"), pos, SYNTHETIC)
|
||||
setInfo(sym, tpe)
|
||||
val vd = internal.valDef(sym, EmptyTree)
|
||||
vd.setPos(pos)
|
||||
vd
|
||||
}
|
||||
def freshValDef(tpe: Type, pos: Position, owner: Symbol): ValDef = {
|
||||
val SYNTHETIC = (1 << 21).toLong.asInstanceOf[FlagSet]
|
||||
val sym = owner.newTermSymbol(freshTermName("q"), pos, SYNTHETIC)
|
||||
setInfo(sym, tpe)
|
||||
val vd = internal.valDef(sym, EmptyTree)
|
||||
vd.setPos(pos)
|
||||
vd
|
||||
}
|
||||
|
||||
lazy val parameterModifiers = Modifiers(Flag.PARAM)
|
||||
|
||||
|
|
@ -84,22 +84,23 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
* Collects all definitions in the tree for use in checkReferences.
|
||||
* This excludes definitions in wrapped expressions because checkReferences won't allow nested dereferencing anyway.
|
||||
*/
|
||||
def collectDefs(tree: Tree, isWrapper: (String, Type, Tree) => Boolean): collection.Set[Symbol] =
|
||||
{
|
||||
val defs = new collection.mutable.HashSet[Symbol]
|
||||
// adds the symbols for all non-Ident subtrees to `defs`.
|
||||
val process = new Traverser {
|
||||
override def traverse(t: Tree) = t match {
|
||||
case _: Ident => ()
|
||||
case ApplyTree(TypeApply(Select(_, nme), tpe :: Nil), qual :: Nil) if isWrapper(nme.decodedName.toString, tpe.tpe, qual) => ()
|
||||
case tree =>
|
||||
if (tree.symbol ne null) defs += tree.symbol;
|
||||
super.traverse(tree)
|
||||
}
|
||||
def collectDefs(tree: Tree, isWrapper: (String, Type, Tree) => Boolean): collection.Set[Symbol] = {
|
||||
val defs = new collection.mutable.HashSet[Symbol]
|
||||
// adds the symbols for all non-Ident subtrees to `defs`.
|
||||
val process = new Traverser {
|
||||
override def traverse(t: Tree) = t match {
|
||||
case _: Ident => ()
|
||||
case ApplyTree(TypeApply(Select(_, nme), tpe :: Nil), qual :: Nil)
|
||||
if isWrapper(nme.decodedName.toString, tpe.tpe, qual) =>
|
||||
()
|
||||
case tree =>
|
||||
if (tree.symbol ne null) defs += tree.symbol;
|
||||
super.traverse(tree)
|
||||
}
|
||||
process.traverse(tree)
|
||||
defs
|
||||
}
|
||||
process.traverse(tree)
|
||||
defs
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference is illegal if it is to an M instance defined within the scope of the macro call.
|
||||
|
|
@ -112,10 +113,13 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
* A function that checks the provided tree for illegal references to M instances defined in the
|
||||
* expression passed to the macro and for illegal dereferencing of M instances.
|
||||
*/
|
||||
def checkReferences(defs: collection.Set[Symbol], isWrapper: (String, Type, Tree) => Boolean): Tree => Unit = {
|
||||
def checkReferences(defs: collection.Set[Symbol],
|
||||
isWrapper: (String, Type, Tree) => Boolean): Tree => Unit = {
|
||||
case s @ ApplyTree(TypeApply(Select(_, nme), tpe :: Nil), qual :: Nil) =>
|
||||
if (isWrapper(nme.decodedName.toString, tpe.tpe, qual)) ctx.error(s.pos, DynamicDependencyError)
|
||||
case id @ Ident(name) if illegalReference(defs, id.symbol) => ctx.error(id.pos, DynamicReferenceError + ": " + name)
|
||||
if (isWrapper(nme.decodedName.toString, tpe.tpe, qual))
|
||||
ctx.error(s.pos, DynamicDependencyError)
|
||||
case id @ Ident(name) if illegalReference(defs, id.symbol) =>
|
||||
ctx.error(id.pos, DynamicReferenceError + ": " + name)
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
|
|
@ -142,55 +146,65 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
|
||||
/** Creates a new, synthetic type variable with the specified `owner`. */
|
||||
def newTypeVariable(owner: Symbol, prefix: String = "T0"): TypeSymbol =
|
||||
owner.asInstanceOf[global.Symbol].newSyntheticTypeParam(prefix, 0L).asInstanceOf[ctx.universe.TypeSymbol]
|
||||
owner
|
||||
.asInstanceOf[global.Symbol]
|
||||
.newSyntheticTypeParam(prefix, 0L)
|
||||
.asInstanceOf[ctx.universe.TypeSymbol]
|
||||
|
||||
/** The type representing the type constructor `[X] X` */
|
||||
lazy val idTC: Type =
|
||||
{
|
||||
val tvar = newTypeVariable(NoSymbol)
|
||||
internal.polyType(tvar :: Nil, refVar(tvar))
|
||||
}
|
||||
lazy val idTC: Type = {
|
||||
val tvar = newTypeVariable(NoSymbol)
|
||||
internal.polyType(tvar :: Nil, refVar(tvar))
|
||||
}
|
||||
|
||||
/** A Type that references the given type variable. */
|
||||
def refVar(variable: TypeSymbol): Type = variable.toTypeConstructor
|
||||
|
||||
/** Constructs a new, synthetic type variable that is a type constructor. For example, in type Y[L[x]], L is such a type variable. */
|
||||
def newTCVariable(owner: Symbol): TypeSymbol =
|
||||
{
|
||||
val tc = newTypeVariable(owner)
|
||||
val arg = newTypeVariable(tc, "x");
|
||||
tc.setInfo(internal.polyType(arg :: Nil, emptyTypeBounds))
|
||||
tc
|
||||
}
|
||||
def newTCVariable(owner: Symbol): TypeSymbol = {
|
||||
val tc = newTypeVariable(owner)
|
||||
val arg = newTypeVariable(tc, "x");
|
||||
tc.setInfo(internal.polyType(arg :: Nil, emptyTypeBounds))
|
||||
tc
|
||||
}
|
||||
|
||||
/** >: Nothing <: Any */
|
||||
def emptyTypeBounds: TypeBounds = internal.typeBounds(definitions.NothingClass.toType, definitions.AnyClass.toType)
|
||||
def emptyTypeBounds: TypeBounds =
|
||||
internal.typeBounds(definitions.NothingClass.toType, definitions.AnyClass.toType)
|
||||
|
||||
/** Creates a new anonymous function symbol with Position `pos`. */
|
||||
def functionSymbol(pos: Position): Symbol =
|
||||
callsiteTyper.context.owner.newAnonymousFunctionValue(pos.asInstanceOf[global.Position]).asInstanceOf[ctx.universe.Symbol]
|
||||
callsiteTyper.context.owner
|
||||
.newAnonymousFunctionValue(pos.asInstanceOf[global.Position])
|
||||
.asInstanceOf[ctx.universe.Symbol]
|
||||
|
||||
def functionType(args: List[Type], result: Type): Type =
|
||||
{
|
||||
val tpe = global.definitions.functionType(args.asInstanceOf[List[global.Type]], result.asInstanceOf[global.Type])
|
||||
tpe.asInstanceOf[Type]
|
||||
}
|
||||
def functionType(args: List[Type], result: Type): Type = {
|
||||
val tpe = global.definitions
|
||||
.functionType(args.asInstanceOf[List[global.Type]], result.asInstanceOf[global.Type])
|
||||
tpe.asInstanceOf[Type]
|
||||
}
|
||||
|
||||
/** Create a Tree that references the `val` represented by `vd`, copying attributes from `replaced`. */
|
||||
def refVal(replaced: Tree, vd: ValDef): Tree =
|
||||
treeCopy.Ident(replaced, vd.name).setSymbol(vd.symbol)
|
||||
|
||||
/** Creates a Function tree using `functionSym` as the Symbol and changing `initialOwner` to `functionSym` in `body`.*/
|
||||
def createFunction(params: List[ValDef], body: Tree, functionSym: Symbol): Tree =
|
||||
{
|
||||
changeOwner(body, initialOwner, functionSym)
|
||||
val f = Function(params, body)
|
||||
setSymbol(f, functionSym)
|
||||
f
|
||||
}
|
||||
def createFunction(params: List[ValDef], body: Tree, functionSym: Symbol): Tree = {
|
||||
changeOwner(body, initialOwner, functionSym)
|
||||
val f = Function(params, body)
|
||||
setSymbol(f, functionSym)
|
||||
f
|
||||
}
|
||||
|
||||
def changeOwner(tree: Tree, prev: Symbol, next: Symbol): Unit =
|
||||
new ChangeOwnerAndModuleClassTraverser(prev.asInstanceOf[global.Symbol], next.asInstanceOf[global.Symbol]).traverse(tree.asInstanceOf[global.Tree])
|
||||
new ChangeOwnerAndModuleClassTraverser(
|
||||
prev.asInstanceOf[global.Symbol],
|
||||
next.asInstanceOf[global.Symbol]).traverse(tree.asInstanceOf[global.Tree])
|
||||
|
||||
// Workaround copied from scala/async:can be removed once https://github.com/scala/scala/pull/3179 is merged.
|
||||
private[this] class ChangeOwnerAndModuleClassTraverser(oldowner: global.Symbol, newowner: global.Symbol) extends global.ChangeOwnerTraverser(oldowner, newowner) {
|
||||
private[this] class ChangeOwnerAndModuleClassTraverser(oldowner: global.Symbol,
|
||||
newowner: global.Symbol)
|
||||
extends global.ChangeOwnerTraverser(oldowner, newowner) {
|
||||
override def traverse(tree: global.Tree): Unit = {
|
||||
tree match {
|
||||
case _: global.DefTree => change(tree.symbol.moduleClass)
|
||||
|
|
@ -204,7 +218,7 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
def singleton[T <: AnyRef with Singleton](i: T)(implicit it: ctx.TypeTag[i.type]): Symbol =
|
||||
it.tpe match {
|
||||
case SingleType(_, sym) if !sym.isFreeTerm && sym.isStatic => sym
|
||||
case x => sys.error("Instance must be static (was " + x + ").")
|
||||
case x => sys.error("Instance must be static (was " + x + ").")
|
||||
}
|
||||
|
||||
def select(t: Tree, name: String): Tree = Select(t, TermName(name))
|
||||
|
|
@ -221,14 +235,14 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
* `object Demo { type M[x] = List[x] }`, the call `extractTC(Demo, "M")` will return a type representing
|
||||
* the type constructor `[x] List[x]`.
|
||||
*/
|
||||
def extractTC(tcp: AnyRef with Singleton, name: String)(implicit it: ctx.TypeTag[tcp.type]): ctx.Type =
|
||||
{
|
||||
val itTpe = it.tpe.asInstanceOf[global.Type]
|
||||
val m = itTpe.nonPrivateMember(global.newTypeName(name))
|
||||
val tc = itTpe.memberInfo(m).asInstanceOf[ctx.universe.Type]
|
||||
assert(tc != NoType && tc.takesTypeArgs, "Invalid type constructor: " + tc)
|
||||
tc
|
||||
}
|
||||
def extractTC(tcp: AnyRef with Singleton, name: String)(
|
||||
implicit it: ctx.TypeTag[tcp.type]): ctx.Type = {
|
||||
val itTpe = it.tpe.asInstanceOf[global.Type]
|
||||
val m = itTpe.nonPrivateMember(global.newTypeName(name))
|
||||
val tc = itTpe.memberInfo(m).asInstanceOf[ctx.universe.Type]
|
||||
assert(tc != NoType && tc.takesTypeArgs, "Invalid type constructor: " + tc)
|
||||
tc
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes wrappers in tree `t` with the result of `subWrapper`.
|
||||
|
|
@ -236,26 +250,26 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
* Typically, `f` is a `Select` or `Ident`.
|
||||
* The wrapper is replaced with the result of `subWrapper(<Type of T>, <Tree of v>, <wrapper Tree>)`
|
||||
*/
|
||||
def transformWrappers(t: Tree, subWrapper: (String, Type, Tree, Tree) => Converted[ctx.type]): Tree =
|
||||
{
|
||||
// the main tree transformer that replaces calls to InputWrapper.wrap(x) with
|
||||
// plain Idents that reference the actual input value
|
||||
object appTransformer extends Transformer {
|
||||
override def transform(tree: Tree): Tree =
|
||||
tree match {
|
||||
case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) =>
|
||||
subWrapper(nme.decodedName.toString, targ.tpe, qual, tree) match {
|
||||
case Converted.Success(t, finalTx) =>
|
||||
changeOwner(qual, currentOwner, initialOwner) // Fixes https://github.com/sbt/sbt/issues/1150
|
||||
finalTx(t)
|
||||
case Converted.Failure(p, m) => ctx.abort(p, m)
|
||||
case _: Converted.NotApplicable[_] => super.transform(tree)
|
||||
}
|
||||
case _ => super.transform(tree)
|
||||
}
|
||||
}
|
||||
appTransformer.atOwner(initialOwner) {
|
||||
appTransformer.transform(t)
|
||||
}
|
||||
def transformWrappers(t: Tree,
|
||||
subWrapper: (String, Type, Tree, Tree) => Converted[ctx.type]): Tree = {
|
||||
// the main tree transformer that replaces calls to InputWrapper.wrap(x) with
|
||||
// plain Idents that reference the actual input value
|
||||
object appTransformer extends Transformer {
|
||||
override def transform(tree: Tree): Tree =
|
||||
tree match {
|
||||
case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) =>
|
||||
subWrapper(nme.decodedName.toString, targ.tpe, qual, tree) match {
|
||||
case Converted.Success(t, finalTx) =>
|
||||
changeOwner(qual, currentOwner, initialOwner) // Fixes https://github.com/sbt/sbt/issues/1150
|
||||
finalTx(t)
|
||||
case Converted.Failure(p, m) => ctx.abort(p, m)
|
||||
case _: Converted.NotApplicable[_] => super.transform(tree)
|
||||
}
|
||||
case _ => super.transform(tree)
|
||||
}
|
||||
}
|
||||
appTransformer.atOwner(initialOwner) {
|
||||
appTransformer.transform(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ sealed trait Converted[C <: blackbox.Context with Singleton] {
|
|||
}
|
||||
object Converted {
|
||||
def NotApplicable[C <: blackbox.Context with Singleton] = new NotApplicable[C]
|
||||
final case class Failure[C <: blackbox.Context with Singleton](position: C#Position, message: String) extends Converted[C] {
|
||||
final case class Failure[C <: blackbox.Context with Singleton](position: C#Position,
|
||||
message: String)
|
||||
extends Converted[C] {
|
||||
def isSuccess = false
|
||||
def transform(f: C#Tree => C#Tree): Converted[C] = new Failure(position, message)
|
||||
}
|
||||
|
|
@ -27,11 +29,14 @@ object Converted {
|
|||
def isSuccess = false
|
||||
def transform(f: C#Tree => C#Tree): Converted[C] = this
|
||||
}
|
||||
final case class Success[C <: blackbox.Context with Singleton](tree: C#Tree, finalTransform: C#Tree => C#Tree) extends Converted[C] {
|
||||
final case class Success[C <: blackbox.Context with Singleton](tree: C#Tree,
|
||||
finalTransform: C#Tree => C#Tree)
|
||||
extends Converted[C] {
|
||||
def isSuccess = true
|
||||
def transform(f: C#Tree => C#Tree): Converted[C] = Success(f(tree), finalTransform)
|
||||
}
|
||||
object Success {
|
||||
def apply[C <: blackbox.Context with Singleton](tree: C#Tree): Success[C] = Success(tree, idFun)
|
||||
def apply[C <: blackbox.Context with Singleton](tree: C#Tree): Success[C] =
|
||||
Success(tree, idFun)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,9 @@ object Instance {
|
|||
final val MapName = "map"
|
||||
final val InstanceTCName = "M"
|
||||
|
||||
final class Input[U <: Universe with Singleton](val tpe: U#Type, val expr: U#Tree, val local: U#ValDef)
|
||||
final class Input[U <: Universe with Singleton](val tpe: U#Type,
|
||||
val expr: U#Tree,
|
||||
val local: U#ValDef)
|
||||
trait Transform[C <: blackbox.Context with Singleton, N[_]] {
|
||||
def apply(in: C#Tree): C#Tree
|
||||
}
|
||||
|
|
@ -75,119 +77,122 @@ object Instance {
|
|||
* If this is for multi-input flatMap (app followed by flatMap),
|
||||
* this should be the argument wrapped in Right.
|
||||
*/
|
||||
def contImpl[T, N[_]](c: blackbox.Context, i: Instance with Singleton, convert: Convert, builder: TupleBuilder)(t: Either[c.Expr[T], c.Expr[i.M[T]]], inner: Transform[c.type, N])(
|
||||
implicit
|
||||
tt: c.WeakTypeTag[T], nt: c.WeakTypeTag[N[T]], it: c.TypeTag[i.type]
|
||||
): c.Expr[i.M[N[T]]] =
|
||||
{
|
||||
import c.universe.{ Apply => ApplyTree, _ }
|
||||
def contImpl[T, N[_]](
|
||||
c: blackbox.Context,
|
||||
i: Instance with Singleton,
|
||||
convert: Convert,
|
||||
builder: TupleBuilder)(t: Either[c.Expr[T], c.Expr[i.M[T]]], inner: Transform[c.type, N])(
|
||||
implicit tt: c.WeakTypeTag[T],
|
||||
nt: c.WeakTypeTag[N[T]],
|
||||
it: c.TypeTag[i.type]
|
||||
): c.Expr[i.M[N[T]]] = {
|
||||
import c.universe.{ Apply => ApplyTree, _ }
|
||||
|
||||
val util = ContextUtil[c.type](c)
|
||||
val mTC: Type = util.extractTC(i, InstanceTCName)
|
||||
val mttpe: Type = appliedType(mTC, nt.tpe :: Nil).dealias
|
||||
val util = ContextUtil[c.type](c)
|
||||
val mTC: Type = util.extractTC(i, InstanceTCName)
|
||||
val mttpe: Type = appliedType(mTC, nt.tpe :: Nil).dealias
|
||||
|
||||
// the tree for the macro argument
|
||||
val (tree, treeType) = t match {
|
||||
case Left(l) => (l.tree, nt.tpe.dealias)
|
||||
case Right(r) => (r.tree, mttpe)
|
||||
}
|
||||
// the Symbol for the anonymous function passed to the appropriate Instance.map/flatMap/pure method
|
||||
// this Symbol needs to be known up front so that it can be used as the owner of synthetic vals
|
||||
val functionSym = util.functionSymbol(tree.pos)
|
||||
|
||||
val instanceSym = util.singleton(i)
|
||||
// A Tree that references the statically accessible Instance that provides the actual implementations of map, flatMap, ...
|
||||
val instance = Ident(instanceSym)
|
||||
|
||||
val isWrapper: (String, Type, Tree) => Boolean = convert.asPredicate(c)
|
||||
|
||||
// Local definitions `defs` in the macro. This is used to ensure references are to M instances defined outside of the macro call.
|
||||
// Also `refCount` is the number of references, which is used to create the private, synthetic method containing the body
|
||||
val defs = util.collectDefs(tree, isWrapper)
|
||||
val checkQual: Tree => Unit = util.checkReferences(defs, isWrapper)
|
||||
|
||||
type In = Input[c.universe.type]
|
||||
var inputs = List[In]()
|
||||
|
||||
// transforms the original tree into calls to the Instance functions pure, map, ...,
|
||||
// resulting in a value of type M[T]
|
||||
def makeApp(body: Tree): Tree =
|
||||
inputs match {
|
||||
case Nil => pure(body)
|
||||
case x :: Nil => single(body, x)
|
||||
case xs => arbArity(body, xs)
|
||||
}
|
||||
|
||||
// no inputs, so construct M[T] via Instance.pure or pure+flatten
|
||||
def pure(body: Tree): Tree =
|
||||
{
|
||||
val typeApplied = TypeApply(util.select(instance, PureName), TypeTree(treeType) :: Nil)
|
||||
val f = util.createFunction(Nil, body, functionSym)
|
||||
val p = ApplyTree(typeApplied, f :: Nil)
|
||||
if (t.isLeft) p else flatten(p)
|
||||
}
|
||||
// m should have type M[M[T]]
|
||||
// the returned Tree will have type M[T]
|
||||
def flatten(m: Tree): Tree =
|
||||
{
|
||||
val typedFlatten = TypeApply(util.select(instance, FlattenName), TypeTree(tt.tpe) :: Nil)
|
||||
ApplyTree(typedFlatten, m :: Nil)
|
||||
}
|
||||
|
||||
// calls Instance.map or flatmap directly, skipping the intermediate Instance.app that is unnecessary for a single input
|
||||
def single(body: Tree, input: In): Tree =
|
||||
{
|
||||
val variable = input.local
|
||||
val param = treeCopy.ValDef(variable, util.parameterModifiers, variable.name, variable.tpt, EmptyTree)
|
||||
val typeApplied = TypeApply(util.select(instance, MapName), variable.tpt :: TypeTree(treeType) :: Nil)
|
||||
val f = util.createFunction(param :: Nil, body, functionSym)
|
||||
val mapped = ApplyTree(typeApplied, input.expr :: f :: Nil)
|
||||
if (t.isLeft) mapped else flatten(mapped)
|
||||
}
|
||||
|
||||
// calls Instance.app to get the values for all inputs and then calls Instance.map or flatMap to evaluate the body
|
||||
def arbArity(body: Tree, inputs: List[In]): Tree =
|
||||
{
|
||||
val result = builder.make(c)(mTC, inputs)
|
||||
val param = util.freshMethodParameter(appliedType(result.representationC, util.idTC :: Nil))
|
||||
val bindings = result.extract(param)
|
||||
val f = util.createFunction(param :: Nil, Block(bindings, body), functionSym)
|
||||
val ttt = TypeTree(treeType)
|
||||
val typedApp = TypeApply(util.select(instance, ApplyName), TypeTree(result.representationC) :: ttt :: Nil)
|
||||
val app = ApplyTree(ApplyTree(typedApp, result.input :: f :: Nil), result.alistInstance :: Nil)
|
||||
if (t.isLeft) app else flatten(app)
|
||||
}
|
||||
|
||||
// Called when transforming the tree to add an input.
|
||||
// For `qual` of type M[A], and a `selection` qual.value,
|
||||
// the call is addType(Type A, Tree qual)
|
||||
// The result is a Tree representing a reference to
|
||||
// the bound value of the input.
|
||||
def addType(tpe: Type, qual: Tree, selection: Tree): Tree =
|
||||
{
|
||||
qual.foreach(checkQual)
|
||||
val vd = util.freshValDef(tpe, qual.pos, functionSym)
|
||||
inputs ::= new Input(tpe, qual, vd)
|
||||
util.refVal(selection, vd)
|
||||
}
|
||||
def sub(name: String, tpe: Type, qual: Tree, replace: Tree): Converted[c.type] =
|
||||
{
|
||||
val tag = c.WeakTypeTag[T](tpe)
|
||||
convert[T](c)(name, qual)(tag) transform { tree =>
|
||||
addType(tpe, tree, replace)
|
||||
}
|
||||
}
|
||||
|
||||
// applies the transformation
|
||||
val tx = util.transformWrappers(tree, (n, tpe, t, replace) => sub(n, tpe, t, replace))
|
||||
// resetting attributes must be: a) local b) done here and not wider or else there are obscure errors
|
||||
val tr = makeApp(inner(tx))
|
||||
c.Expr[i.M[N[T]]](tr)
|
||||
// the tree for the macro argument
|
||||
val (tree, treeType) = t match {
|
||||
case Left(l) => (l.tree, nt.tpe.dealias)
|
||||
case Right(r) => (r.tree, mttpe)
|
||||
}
|
||||
// the Symbol for the anonymous function passed to the appropriate Instance.map/flatMap/pure method
|
||||
// this Symbol needs to be known up front so that it can be used as the owner of synthetic vals
|
||||
val functionSym = util.functionSymbol(tree.pos)
|
||||
|
||||
val instanceSym = util.singleton(i)
|
||||
// A Tree that references the statically accessible Instance that provides the actual implementations of map, flatMap, ...
|
||||
val instance = Ident(instanceSym)
|
||||
|
||||
val isWrapper: (String, Type, Tree) => Boolean = convert.asPredicate(c)
|
||||
|
||||
// Local definitions `defs` in the macro. This is used to ensure references are to M instances defined outside of the macro call.
|
||||
// Also `refCount` is the number of references, which is used to create the private, synthetic method containing the body
|
||||
val defs = util.collectDefs(tree, isWrapper)
|
||||
val checkQual: Tree => Unit = util.checkReferences(defs, isWrapper)
|
||||
|
||||
type In = Input[c.universe.type]
|
||||
var inputs = List[In]()
|
||||
|
||||
// transforms the original tree into calls to the Instance functions pure, map, ...,
|
||||
// resulting in a value of type M[T]
|
||||
def makeApp(body: Tree): Tree =
|
||||
inputs match {
|
||||
case Nil => pure(body)
|
||||
case x :: Nil => single(body, x)
|
||||
case xs => arbArity(body, xs)
|
||||
}
|
||||
|
||||
// no inputs, so construct M[T] via Instance.pure or pure+flatten
|
||||
def pure(body: Tree): Tree = {
|
||||
val typeApplied = TypeApply(util.select(instance, PureName), TypeTree(treeType) :: Nil)
|
||||
val f = util.createFunction(Nil, body, functionSym)
|
||||
val p = ApplyTree(typeApplied, f :: Nil)
|
||||
if (t.isLeft) p else flatten(p)
|
||||
}
|
||||
// m should have type M[M[T]]
|
||||
// the returned Tree will have type M[T]
|
||||
def flatten(m: Tree): Tree = {
|
||||
val typedFlatten = TypeApply(util.select(instance, FlattenName), TypeTree(tt.tpe) :: Nil)
|
||||
ApplyTree(typedFlatten, m :: Nil)
|
||||
}
|
||||
|
||||
// calls Instance.map or flatmap directly, skipping the intermediate Instance.app that is unnecessary for a single input
|
||||
def single(body: Tree, input: In): Tree = {
|
||||
val variable = input.local
|
||||
val param =
|
||||
treeCopy.ValDef(variable, util.parameterModifiers, variable.name, variable.tpt, EmptyTree)
|
||||
val typeApplied =
|
||||
TypeApply(util.select(instance, MapName), variable.tpt :: TypeTree(treeType) :: Nil)
|
||||
val f = util.createFunction(param :: Nil, body, functionSym)
|
||||
val mapped = ApplyTree(typeApplied, input.expr :: f :: Nil)
|
||||
if (t.isLeft) mapped else flatten(mapped)
|
||||
}
|
||||
|
||||
// calls Instance.app to get the values for all inputs and then calls Instance.map or flatMap to evaluate the body
|
||||
def arbArity(body: Tree, inputs: List[In]): Tree = {
|
||||
val result = builder.make(c)(mTC, inputs)
|
||||
val param = util.freshMethodParameter(appliedType(result.representationC, util.idTC :: Nil))
|
||||
val bindings = result.extract(param)
|
||||
val f = util.createFunction(param :: Nil, Block(bindings, body), functionSym)
|
||||
val ttt = TypeTree(treeType)
|
||||
val typedApp =
|
||||
TypeApply(util.select(instance, ApplyName), TypeTree(result.representationC) :: ttt :: Nil)
|
||||
val app =
|
||||
ApplyTree(ApplyTree(typedApp, result.input :: f :: Nil), result.alistInstance :: Nil)
|
||||
if (t.isLeft) app else flatten(app)
|
||||
}
|
||||
|
||||
// Called when transforming the tree to add an input.
|
||||
// For `qual` of type M[A], and a `selection` qual.value,
|
||||
// the call is addType(Type A, Tree qual)
|
||||
// The result is a Tree representing a reference to
|
||||
// the bound value of the input.
|
||||
def addType(tpe: Type, qual: Tree, selection: Tree): Tree = {
|
||||
qual.foreach(checkQual)
|
||||
val vd = util.freshValDef(tpe, qual.pos, functionSym)
|
||||
inputs ::= new Input(tpe, qual, vd)
|
||||
util.refVal(selection, vd)
|
||||
}
|
||||
def sub(name: String, tpe: Type, qual: Tree, replace: Tree): Converted[c.type] = {
|
||||
val tag = c.WeakTypeTag[T](tpe)
|
||||
convert[T](c)(name, qual)(tag) transform { tree =>
|
||||
addType(tpe, tree, replace)
|
||||
}
|
||||
}
|
||||
|
||||
// applies the transformation
|
||||
val tx = util.transformWrappers(tree, (n, tpe, t, replace) => sub(n, tpe, t, replace))
|
||||
// resetting attributes must be: a) local b) done here and not wider or else there are obscure errors
|
||||
val tr = makeApp(inner(tx))
|
||||
c.Expr[i.M[N[T]]](tr)
|
||||
}
|
||||
|
||||
import Types._
|
||||
|
||||
implicit def applicativeInstance[A[_]](implicit ap: Applicative[A]): Instance { type M[x] = A[x] } = new Instance {
|
||||
implicit def applicativeInstance[A[_]](
|
||||
implicit ap: Applicative[A]): Instance { type M[x] = A[x] } = new Instance {
|
||||
type M[x] = A[x]
|
||||
def app[K[L[x]], Z](in: K[A], f: K[Id] => Z)(implicit a: AList[K]) = a.apply[A, Z](in, f)
|
||||
def map[S, T](in: A[S], f: S => T) = ap.map(f, in)
|
||||
|
|
@ -195,17 +200,17 @@ object Instance {
|
|||
}
|
||||
|
||||
type AI[A[_]] = Instance { type M[x] = A[x] }
|
||||
def compose[A[_], B[_]](implicit a: AI[A], b: AI[B]): Instance { type M[x] = A[B[x]] } = new Composed[A, B](a, b)
|
||||
def compose[A[_], B[_]](implicit a: AI[A], b: AI[B]): Instance { type M[x] = A[B[x]] } =
|
||||
new Composed[A, B](a, b)
|
||||
// made a public, named, unsealed class because of trouble with macros and inference when the Instance is not an object
|
||||
class Composed[A[_], B[_]](a: AI[A], b: AI[B]) extends Instance {
|
||||
type M[x] = A[B[x]]
|
||||
def pure[S](s: () => S): A[B[S]] = a.pure(() => b.pure(s))
|
||||
def map[S, T](in: M[S], f: S => T): M[T] = a.map(in, (bv: B[S]) => b.map(bv, f))
|
||||
def app[K[L[x]], Z](in: K[M], f: K[Id] => Z)(implicit alist: AList[K]): A[B[Z]] =
|
||||
{
|
||||
val g: K[B] => B[Z] = in => b.app[K, Z](in, f)
|
||||
type Split[L[x]] = K[(L ∙ B)#l]
|
||||
a.app[Split, B[Z]](in, g)(AList.asplit(alist))
|
||||
}
|
||||
def app[K[L[x]], Z](in: K[M], f: K[Id] => Z)(implicit alist: AList[K]): A[B[Z]] = {
|
||||
val g: K[B] => B[Z] = in => b.app[K, Z](in, f)
|
||||
type Split[L[x]] = K[(L ∙ B)#l]
|
||||
a.app[Split, B[Z]](in, g)(AList.asplit(alist))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,59 +6,67 @@ import macros._
|
|||
|
||||
/** A `TupleBuilder` that uses a KList as the tuple representation.*/
|
||||
object KListBuilder extends TupleBuilder {
|
||||
def make(c: blackbox.Context)(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = new BuilderResult[c.type] {
|
||||
val ctx: c.type = c
|
||||
val util = ContextUtil[c.type](c)
|
||||
import c.universe.{ Apply => ApplyTree, _ }
|
||||
import util._
|
||||
def make(c: blackbox.Context)(mt: c.Type,
|
||||
inputs: Inputs[c.universe.type]): BuilderResult[c.type] =
|
||||
new BuilderResult[c.type] {
|
||||
val ctx: c.type = c
|
||||
val util = ContextUtil[c.type](c)
|
||||
import c.universe.{ Apply => ApplyTree, _ }
|
||||
import util._
|
||||
|
||||
val knilType = c.typeOf[KNil]
|
||||
val knil = Ident(knilType.typeSymbol.companion)
|
||||
val kconsTpe = c.typeOf[KCons[Int, KNil, List]]
|
||||
val kcons = kconsTpe.typeSymbol.companion
|
||||
val mTC: Type = mt.asInstanceOf[c.universe.Type]
|
||||
val kconsTC: Type = kconsTpe.typeConstructor
|
||||
val knilType = c.typeOf[KNil]
|
||||
val knil = Ident(knilType.typeSymbol.companion)
|
||||
val kconsTpe = c.typeOf[KCons[Int, KNil, List]]
|
||||
val kcons = kconsTpe.typeSymbol.companion
|
||||
val mTC: Type = mt.asInstanceOf[c.universe.Type]
|
||||
val kconsTC: Type = kconsTpe.typeConstructor
|
||||
|
||||
/** This is the L in the type function [L[x]] ... */
|
||||
val tcVariable: TypeSymbol = newTCVariable(util.initialOwner)
|
||||
/** This is the L in the type function [L[x]] ... */
|
||||
val tcVariable: TypeSymbol = newTCVariable(util.initialOwner)
|
||||
|
||||
/** Instantiates KCons[h, t <: KList[L], L], where L is the type constructor variable */
|
||||
def kconsType(h: Type, t: Type): Type =
|
||||
appliedType(kconsTC, h :: t :: refVar(tcVariable) :: Nil)
|
||||
/** Instantiates KCons[h, t <: KList[L], L], where L is the type constructor variable */
|
||||
def kconsType(h: Type, t: Type): Type =
|
||||
appliedType(kconsTC, h :: t :: refVar(tcVariable) :: Nil)
|
||||
|
||||
def bindKList(prev: ValDef, revBindings: List[ValDef], params: List[ValDef]): List[ValDef] =
|
||||
params match {
|
||||
case (x @ ValDef(mods, name, tpt, _)) :: xs =>
|
||||
val rhs = select(Ident(prev.name), "head")
|
||||
val head = treeCopy.ValDef(x, mods, name, tpt, rhs)
|
||||
util.setSymbol(head, x.symbol)
|
||||
val tail = localValDef(TypeTree(), select(Ident(prev.name), "tail"))
|
||||
val base = head :: revBindings
|
||||
bindKList(tail, if (xs.isEmpty) base else tail :: base, xs)
|
||||
case Nil => revBindings.reverse
|
||||
}
|
||||
def bindKList(prev: ValDef, revBindings: List[ValDef], params: List[ValDef]): List[ValDef] =
|
||||
params match {
|
||||
case (x @ ValDef(mods, name, tpt, _)) :: xs =>
|
||||
val rhs = select(Ident(prev.name), "head")
|
||||
val head = treeCopy.ValDef(x, mods, name, tpt, rhs)
|
||||
util.setSymbol(head, x.symbol)
|
||||
val tail = localValDef(TypeTree(), select(Ident(prev.name), "tail"))
|
||||
val base = head :: revBindings
|
||||
bindKList(tail, if (xs.isEmpty) base else tail :: base, xs)
|
||||
case Nil => revBindings.reverse
|
||||
}
|
||||
|
||||
private[this] def makeKList(revInputs: Inputs[c.universe.type], klist: Tree, klistType: Type): Tree =
|
||||
revInputs match {
|
||||
case in :: tail =>
|
||||
val next = ApplyTree(TypeApply(Ident(kcons), TypeTree(in.tpe) :: TypeTree(klistType) :: TypeTree(mTC) :: Nil), in.expr :: klist :: Nil)
|
||||
makeKList(tail, next, appliedType(kconsTC, in.tpe :: klistType :: mTC :: Nil))
|
||||
case Nil => klist
|
||||
}
|
||||
private[this] def makeKList(revInputs: Inputs[c.universe.type],
|
||||
klist: Tree,
|
||||
klistType: Type): Tree =
|
||||
revInputs match {
|
||||
case in :: tail =>
|
||||
val next = ApplyTree(
|
||||
TypeApply(Ident(kcons),
|
||||
TypeTree(in.tpe) :: TypeTree(klistType) :: TypeTree(mTC) :: Nil),
|
||||
in.expr :: klist :: Nil)
|
||||
makeKList(tail, next, appliedType(kconsTC, in.tpe :: klistType :: mTC :: Nil))
|
||||
case Nil => klist
|
||||
}
|
||||
|
||||
/** The input trees combined in a KList */
|
||||
val klist = makeKList(inputs.reverse, knil, knilType)
|
||||
/** The input trees combined in a KList */
|
||||
val klist = makeKList(inputs.reverse, knil, knilType)
|
||||
|
||||
/**
|
||||
* The input types combined in a KList type. The main concern is tracking the heterogeneous types.
|
||||
* The type constructor is tcVariable, so that it can be applied to [X] X or M later.
|
||||
* When applied to `M`, this type gives the type of the `input` KList.
|
||||
*/
|
||||
val klistType: Type = (inputs :\ knilType)((in, klist) => kconsType(in.tpe, klist))
|
||||
/**
|
||||
* The input types combined in a KList type. The main concern is tracking the heterogeneous types.
|
||||
* The type constructor is tcVariable, so that it can be applied to [X] X or M later.
|
||||
* When applied to `M`, this type gives the type of the `input` KList.
|
||||
*/
|
||||
val klistType: Type = (inputs :\ knilType)((in, klist) => kconsType(in.tpe, klist))
|
||||
|
||||
val representationC = internal.polyType(tcVariable :: Nil, klistType)
|
||||
val input = klist
|
||||
val alistInstance: ctx.universe.Tree = TypeApply(select(Ident(alist), "klist"), TypeTree(representationC) :: Nil)
|
||||
def extract(param: ValDef) = bindKList(param, Nil, inputs.map(_.local))
|
||||
}
|
||||
val representationC = internal.polyType(tcVariable :: Nil, klistType)
|
||||
val input = klist
|
||||
val alistInstance: ctx.universe.Tree =
|
||||
TypeApply(select(Ident(alist), "klist"), TypeTree(representationC) :: Nil)
|
||||
def extract(param: ValDef) = bindKList(param, Nil, inputs.map(_.local))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ import macros._
|
|||
* and `KList` for larger numbers of inputs. This builder cannot handle fewer than 2 inputs.
|
||||
*/
|
||||
object MixedBuilder extends TupleBuilder {
|
||||
def make(c: blackbox.Context)(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] =
|
||||
{
|
||||
val delegate = if (inputs.size > TupleNBuilder.MaxInputs) KListBuilder else TupleNBuilder
|
||||
delegate.make(c)(mt, inputs)
|
||||
}
|
||||
def make(c: blackbox.Context)(mt: c.Type,
|
||||
inputs: Inputs[c.universe.type]): BuilderResult[c.type] = {
|
||||
val delegate = if (inputs.size > TupleNBuilder.MaxInputs) KListBuilder else TupleNBuilder
|
||||
delegate.make(c)(mt, inputs)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,13 @@ import macros._
|
|||
* The returned list of ValDefs should be the ValDefs from `inputs`, but with non-empty right-hand sides.
|
||||
*/
|
||||
trait TupleBuilder {
|
||||
|
||||
/** A convenience alias for a list of inputs (associated with a Universe of type U). */
|
||||
type Inputs[U <: Universe with Singleton] = List[Instance.Input[U]]
|
||||
|
||||
/** Constructs a one-time use Builder for Context `c` and type constructor `tcType`. */
|
||||
def make(c: blackbox.Context)(tcType: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type]
|
||||
def make(c: blackbox.Context)(tcType: c.Type,
|
||||
inputs: Inputs[c.universe.type]): BuilderResult[c.type]
|
||||
}
|
||||
|
||||
trait BuilderResult[C <: blackbox.Context with Singleton] {
|
||||
|
|
@ -52,4 +54,3 @@ trait BuilderResult[C <: blackbox.Context with Singleton] {
|
|||
* non-empty right hand sides. Each `ValDef` may refer to `param` and previous `ValDef`s in the list.*/
|
||||
def extract(param: ValDef): List[ValDef]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,40 +10,47 @@ import macros._
|
|||
* It is limited to tuples of size 2 to `MaxInputs`.
|
||||
*/
|
||||
object TupleNBuilder extends TupleBuilder {
|
||||
|
||||
/** The largest number of inputs that this builder can handle. */
|
||||
final val MaxInputs = 11
|
||||
final val TupleMethodName = "tuple"
|
||||
|
||||
def make(c: blackbox.Context)(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = new BuilderResult[c.type] {
|
||||
val util = ContextUtil[c.type](c)
|
||||
import c.universe._
|
||||
import util._
|
||||
def make(c: blackbox.Context)(mt: c.Type,
|
||||
inputs: Inputs[c.universe.type]): BuilderResult[c.type] =
|
||||
new BuilderResult[c.type] {
|
||||
val util = ContextUtil[c.type](c)
|
||||
import c.universe._
|
||||
import util._
|
||||
|
||||
val global: Global = c.universe.asInstanceOf[Global]
|
||||
val global: Global = c.universe.asInstanceOf[Global]
|
||||
|
||||
val ctx: c.type = c
|
||||
val representationC: PolyType = {
|
||||
val tcVariable: Symbol = newTCVariable(util.initialOwner)
|
||||
val tupleTypeArgs = inputs.map(in => internal.typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type])
|
||||
val tuple = global.definitions.tupleType(tupleTypeArgs)
|
||||
internal.polyType(tcVariable :: Nil, tuple.asInstanceOf[Type])
|
||||
}
|
||||
|
||||
val input: Tree = mkTuple(inputs.map(_.expr))
|
||||
val alistInstance: Tree = {
|
||||
val selectTree = select(Ident(alist), TupleMethodName + inputs.size.toString)
|
||||
TypeApply(selectTree, inputs.map(in => TypeTree(in.tpe)))
|
||||
}
|
||||
def extract(param: ValDef): List[ValDef] = bindTuple(param, Nil, inputs.map(_.local), 1)
|
||||
|
||||
def bindTuple(param: ValDef, revBindings: List[ValDef], params: List[ValDef], i: Int): List[ValDef] =
|
||||
params match {
|
||||
case (x @ ValDef(mods, name, tpt, _)) :: xs =>
|
||||
val rhs = select(Ident(param.name), "_" + i.toString)
|
||||
val newVal = treeCopy.ValDef(x, mods, name, tpt, rhs)
|
||||
util.setSymbol(newVal, x.symbol)
|
||||
bindTuple(param, newVal :: revBindings, xs, i + 1)
|
||||
case Nil => revBindings.reverse
|
||||
val ctx: c.type = c
|
||||
val representationC: PolyType = {
|
||||
val tcVariable: Symbol = newTCVariable(util.initialOwner)
|
||||
val tupleTypeArgs = inputs.map(in =>
|
||||
internal.typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type])
|
||||
val tuple = global.definitions.tupleType(tupleTypeArgs)
|
||||
internal.polyType(tcVariable :: Nil, tuple.asInstanceOf[Type])
|
||||
}
|
||||
}
|
||||
|
||||
val input: Tree = mkTuple(inputs.map(_.expr))
|
||||
val alistInstance: Tree = {
|
||||
val selectTree = select(Ident(alist), TupleMethodName + inputs.size.toString)
|
||||
TypeApply(selectTree, inputs.map(in => TypeTree(in.tpe)))
|
||||
}
|
||||
def extract(param: ValDef): List[ValDef] = bindTuple(param, Nil, inputs.map(_.local), 1)
|
||||
|
||||
def bindTuple(param: ValDef,
|
||||
revBindings: List[ValDef],
|
||||
params: List[ValDef],
|
||||
i: Int): List[ValDef] =
|
||||
params match {
|
||||
case (x @ ValDef(mods, name, tpt, _)) :: xs =>
|
||||
val rhs = select(Ident(param.name), "_" + i.toString)
|
||||
val newVal = treeCopy.ValDef(x, mods, name, tpt, rhs)
|
||||
util.setSymbol(newVal, x.symbol)
|
||||
bindTuple(param, newVal :: revBindings, xs, i + 1)
|
||||
case Nil => revBindings.reverse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue