From d2f019a47a75773eadebf8469e900350040dd951 Mon Sep 17 00:00:00 2001 From: jvican Date: Tue, 23 May 2017 23:53:04 +0200 Subject: [PATCH] Reformat all util-appmacro --- build.sbt | 6 +- .../internal/util/appmacro/ContextUtil.scala | 200 ++++++++------- .../sbt/internal/util/appmacro/Convert.scala | 11 +- .../sbt/internal/util/appmacro/Instance.scala | 235 +++++++++--------- .../internal/util/appmacro/KListBuilder.scala | 102 ++++---- .../internal/util/appmacro/MixedBuilder.scala | 10 +- .../internal/util/appmacro/TupleBuilder.scala | 5 +- .../util/appmacro/TupleNBuilder.scala | 65 ++--- 8 files changed, 337 insertions(+), 297 deletions(-) diff --git a/build.sbt b/build.sbt index 819873bd5..675ac0e8e 100644 --- a/build.sbt +++ b/build.sbt @@ -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")) diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala index b9b968a23..f6dbf5894 100644 --- a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala @@ -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()`. */ - 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(, , )` */ - 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) + } + } } diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Convert.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Convert.scala index 8accb85c6..edb13f54c 100644 --- a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Convert.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Convert.scala @@ -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) } } diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Instance.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Instance.scala index a10fdfb18..e5a2fcbf3 100644 --- a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Instance.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Instance.scala @@ -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)) + } } } diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala index 65b061e66..4a2428912 100644 --- a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala @@ -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)) + } } diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala index cd77f50ae..8688d7376 100644 --- a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala @@ -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) + } } diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala index 1186f3549..0ca8b3ab3 100644 --- a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala @@ -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] } - diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala index 1c5430e4c..bb005a537 100644 --- a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala @@ -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 + } + } }