From 087e386c9955b16fba3d32ec0f94d7a19ef97aea Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Tue, 31 Jul 2012 11:52:10 -0400 Subject: [PATCH 01/34] task setting macros for :=, +=, ++= also, bump to 2.10.0-M6 --- util/appmacro/ContextUtil.scala | 104 ++++++++++++ util/appmacro/Instance.scala | 260 ++++++++++++++++++++++++++++++ util/appmacro/KListBuilder.scala | 58 +++++++ util/appmacro/MixedBuilder.scala | 16 ++ util/appmacro/TupleBuilder.scala | 56 +++++++ util/appmacro/TupleNBuilder.scala | 51 ++++++ 6 files changed, 545 insertions(+) create mode 100644 util/appmacro/ContextUtil.scala create mode 100644 util/appmacro/Instance.scala create mode 100644 util/appmacro/KListBuilder.scala create mode 100644 util/appmacro/MixedBuilder.scala create mode 100644 util/appmacro/TupleBuilder.scala create mode 100644 util/appmacro/TupleNBuilder.scala diff --git a/util/appmacro/ContextUtil.scala b/util/appmacro/ContextUtil.scala new file mode 100644 index 000000000..31f61c356 --- /dev/null +++ b/util/appmacro/ContextUtil.scala @@ -0,0 +1,104 @@ +package sbt +package appmacro + + import scala.reflect._ + import makro._ + import scala.tools.nsc.Global + +object ContextUtil { + /** Constructs an object with utility methods for operating in the provided macro context `c`. + * Callers should explicitly specify the type parameter as `c.type` in order to preserve the path dependent types. */ + def apply[C <: Context with Singleton](c: C): ContextUtil[C] = new ContextUtil(c) +} + +/** Utility methods for macros. Several methods assume that the context's universe is a full compiler (`scala.tools.nsc.Global`). +* This is not thread safe due to the underlying Context and related data structures not being thread safe. +* Use `ContextUtil[c.type](c)` to construct. */ +final class ContextUtil[C <: Context with Singleton](val ctx: C) +{ + import ctx.universe.{Apply=>ApplyTree,_} + + val alistType = ctx.typeOf[AList[KList]] + val alist: Symbol = alistType.typeSymbol.companionSymbol + val alistTC: Type = alistType.typeConstructor + + /** Modifiers for a local val.*/ + val localModifiers = Modifiers(NoFlags) + + def getPos(sym: Symbol) = if(sym eq null) NoPosition else sym.pos + + /** Constructs a unique term name with the given prefix within this Context. + * (The current implementation uses Context.fresh, which increments*/ + def freshTermName(prefix: String) = newTermName(ctx.fresh("$" + prefix)) + + def typeTree(tpe: Type) = TypeTree().setType(tpe) + + /** Constructs a new, local ValDef with the given Type, a unique name, + * the same position as `sym`, and an empty implementation (no rhs). */ + def freshValDef(tpe: Type, sym: Symbol): ValDef = + { + val vd = localValDef(typeTree(tpe), EmptyTree) + vd setPos getPos(sym) + vd + } + + /** Constructs a ValDef with local modifiers and a unique name. */ + def localValDef(tpt: Tree, rhs: Tree): ValDef = + ValDef(localModifiers, freshTermName("q"), tpt, rhs) + + /** Constructs a tuple value of the right TupleN type from the provided inputs.*/ + def mkTuple(args: List[Tree]): Tree = + { + val global: Global = ctx.universe.asInstanceOf[Global] + global.gen.mkTuple(args.asInstanceOf[List[global.Tree]]).asInstanceOf[ctx.universe.Tree] + } + + /** Creates a new, synthetic type variable with the specified `owner`. */ + def newTypeVariable(owner: Symbol): Symbol = + { + val global: Global = ctx.universe.asInstanceOf[Global] + owner.asInstanceOf[global.Symbol].newSyntheticTypeParam().asInstanceOf[ctx.universe.Symbol] + } + /** The type representing the type constructor `[X] X` */ + val idTC: Type = + { + val tvar = newTypeVariable(NoSymbol) + polyType(tvar :: Nil, refVar(tvar)) + } + /** 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): Symbol = + { + val global: Global = ctx.universe.asInstanceOf[Global] + val tc = owner.asInstanceOf[global.Symbol].newSyntheticTypeParam() + val arg = tc.newSyntheticTypeParam("x", 0L) + tc.setInfo(global.PolyType(arg :: Nil, global.TypeBounds.empty)).asInstanceOf[ctx.universe.Symbol] + } + /** Returns the Symbol that references the statically accessible singleton `i`. */ + 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 => error("Instance must be static (was " + x + ").") + } + /** Constructs a Type that references the given type variable. */ + def refVar(variable: Symbol): Type = typeRef(NoPrefix, variable, Nil) + + /** Returns the symbol for the non-private method named `name` for the class/module `obj`. */ + def method(obj: Symbol, name: String): Symbol = { + val global: Global = ctx.universe.asInstanceOf[Global] + obj.asInstanceOf[global.Symbol].info.nonPrivateMember(global.newTermName(name)).asInstanceOf[ctx.universe.Symbol] + } + + /** Returns a Type representing the type constructor tcp.. For example, given + * `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 global: Global = ctx.universe.asInstanceOf[Global] + 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.isHigherKinded, "Invalid type constructor: " + tc) + tc + } +} \ No newline at end of file diff --git a/util/appmacro/Instance.scala b/util/appmacro/Instance.scala new file mode 100644 index 000000000..05a80b4e8 --- /dev/null +++ b/util/appmacro/Instance.scala @@ -0,0 +1,260 @@ +package sbt +package appmacro + + import Classes.Applicative + import Types.Id + +/** The separate hierarchy from Applicative/Monad is for two reasons. +* +* 1. The type constructor is represented as an abstract type because a TypeTag cannot represent a type constructor directly. +* 2. The applicative interface is uncurried. +*/ +trait Instance +{ + type M[x] + def app[K[L[x]], Z](in: K[M], f: K[Id] => Z)(implicit a: AList[K]): M[Z] + def map[S,T](in: M[S], f: S => T): M[T] + def pure[T](t: () => T): M[T] +} +trait Convert +{ + def apply[T: c.TypeTag](c: scala.reflect.makro.Context)(in: c.Tree): c.Tree +} +trait MonadInstance extends Instance +{ + def flatten[T](in: M[M[T]]): M[T] +} +object InputWrapper +{ + def wrap[T](in: Any): T = error("This method is an implementation detail and should not be referenced.") +} + + import scala.reflect._ + import makro._ + +object Instance +{ + final val DynamicDependencyError = "Illegal dynamic dependency." + final val DynamicReferenceError = "Illegal dynamic reference." + final val ApplyName = "app" + final val FlattenName = "flatten" + final val PureName = "pure" + final val MapName = "map" + final val InstanceTCName = "M" + final val WrapName = "wrap" + + final class Input[U <: Universe with Singleton](val tpe: U#Type, val expr: U#Tree, val local: U#ValDef) + + /** Implementation of a macro that provides a direct syntax for applicative functors and monads. + * It is intended to be used in conjunction with another macro that conditions the inputs. + * + * This method processes the Tree `t` to find inputs of the form `InputWrapper.wrap[T]( input )` + * This form is typically constructed by another macro that pretends to be able to get a value of type `T` + * from a value convertible to `M[T]`. This `wrap(input)` form has two main purposes. + * First, it identifies the inputs that should be transformed. + * Second, it allows the input trees to be wrapped for later conversion into the appropriate `M[T]` type by `convert`. + * This wrapping is necessary because applying the first macro must preserve the original type, + * but it is useful to delay conversion until the outer, second macro is called. The `wrap` method accomplishes this by + * allowing the original `Tree` and `Type` to be hidden behind the raw `T` type. This method will remove the call to `wrap` + * so that it is not actually called at runtime. + * + * Each `input` in each expression of the form `InputWrapper.wrap[T]( input )` is transformed by `convert`. + * This transformation converts the input Tree to a Tree of type `M[T]`. + * The original wrapped expression `wrap(input)` is replaced by a reference to a new local `val $x: T`, where `$x` is a fresh name. + * These converted inputs are passed to `builder` as well as the list of these synthetic `ValDef`s. + * The `TupleBuilder` instance constructs a tuple (Tree) from the inputs and defines the right hand side of the vals + * that unpacks the tuple containing the results of the inputs. + * + * The constructed tuple of inputs and the code that unpacks the results of the inputs are then passed to the `i`, + * which is an implementation of `Instance` that is statically accessible. + * An Instance defines a applicative functor associated with a specific type constructor and, if it implements MonadInstance as well, a monad. + * Typically, it will be either a top-level module or a stable member of a top-level module (such as a val or a nested module). + * The `with Singleton` part of the type verifies some cases at macro compilation time, + * while the full check for static accessibility is done at macro expansion time. + * Note: Ideally, the types would verify that `i: MonadInstance` when `t.isRight`. + * With the various dependent types involved, this is not worth it. + * + * The `t` argument is the argument of the macro that will be transformed as described above. + * If the macro that calls this method is for a multi-input map (app followed by map), + * `t` should be the argument wrapped in Left. + * If this is for multi-input flatMap (app followed by flatMap), + * this should be the argument wrapped in Right. + */ + def contImpl[T: c.TypeTag](c: Context, i: Instance with Singleton, convert: Convert, builder: TupleBuilder)(t: Either[c.Expr[T], c.Expr[i.M[T]]])( + implicit tt: c.TypeTag[T], mt: c.TypeTag[i.M[T]], it: c.TypeTag[i.type]): c.Expr[i.M[T]] = + { + import c.universe.{Apply=>ApplyTree,_} + + import scala.tools.nsc.Global + // Used to access compiler methods not yet exposed via the reflection/macro APIs + val global: Global = c.universe.asInstanceOf[Global] + + val util = ContextUtil[c.type](c) + val mTC: Type = util.extractTC(i, InstanceTCName) + + // the tree for the macro argument + val (tree, treeType) = t match { + case Left(l) => (l.tree, tt.tpe.normalize) + case Right(r) => (r.tree, mt.tpe.normalize) + } + + 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 parameterModifiers = Modifiers(Flag.PARAM) + + val wrapperSym = util.singleton(InputWrapper) + val wrapMethodSymbol = util.method(wrapperSym, WrapName) + def isWrapper(fun: Tree) = fun.symbol == wrapMethodSymbol + + type In = Input[c.universe.type] + var inputs = List[In]() + + // constructs a ValDef with a parameter modifier, a unique name, with the provided Type and with an empty rhs + def freshMethodParameter(tpe: Type): ValDef = + ValDef(parameterModifiers, freshTermName("p"), typeTree(tpe), EmptyTree) + + def freshTermName(prefix: String) = newTermName(c.fresh("$" + prefix)) + def typeTree(tpe: Type) = TypeTree().setType(tpe) + + // constructs a function that applies f to each subtree of the input tree + def visitor(f: Tree => Unit): Tree => Unit = + { + val v: Transformer = new Transformer { + override def transform(tree: Tree): Tree = { f(tree); super.transform(tree) } + } + (tree: Tree) => v.transform(tree) + } + + /* Local definitions in the macro. This is used to ensure + * references are to M instances defined outside of the macro call.*/ + val defs = new collection.mutable.HashSet[Symbol] + + // a reference is illegal if it is to an M instance defined within the scope of the macro call + def illegalReference(sym: Symbol): Boolean = + sym != null && sym != NoSymbol && defs.contains(sym) + + // 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. + val checkQual = visitor { + case s @ ApplyTree(fun, qual :: Nil) => if(isWrapper(fun)) c.error(s.pos, DynamicDependencyError) + case id @ Ident(name) if illegalReference(id.symbol) => c.error(id.pos, DynamicReferenceError) + case _ => () + } + // adds the symbols for all non-Ident subtrees to `defs`. + val defSearch = visitor { + case _: Ident => () + case tree => if(tree.symbol ne null) defs += tree.symbol; + } + + // 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(Select(instance, PureName), typeTree(treeType) :: Nil) + val p = ApplyTree(typeApplied, Function(Nil, body) :: 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(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 = ValDef(parameterModifiers, variable.name, variable.tpt, EmptyTree) + val typeApplied = TypeApply(Select(instance, MapName), variable.tpt :: typeTree(treeType) :: Nil) + val mapped = ApplyTree(typeApplied, input.expr :: Function(param :: Nil, body) :: 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 = freshMethodParameter( appliedType(result.representationC, util.idTC :: Nil) ) + val bindings = result.extract(param) + val f = Function(param :: Nil, Block(bindings, body)) + val ttt = typeTree(treeType) + val typedApp = TypeApply(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): Tree = + { + checkQual(qual) + val vd = util.freshValDef(tpe, qual.symbol) + inputs ::= new Input(tpe, qual, vd) + Ident(vd.name) + } + + // 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(fun, t :: Nil), qual :: Nil) if isWrapper(fun) => + val tag = c.TypeTag(t.tpe) + addType(t.tpe, convert(c)(qual)(tag) ) + case _ => super.transform(tree) + } + } + + // collects all definitions in the tree. used for finding illegal references + defSearch(tree) + + // applies the transformation + // resetting attributes: a) must be local b) must be done + // on the transformed tree and not the wrapped tree or else there are obscure errors + val tr = makeApp( c.resetLocalAttrs(appTransformer.transform(tree)) ) + c.Expr[i.M[T]](tr) + } + + import Types._ + + 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) + def pure[S](s: () => S): M[S] = ap.pure(s()) + } + + 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) + // 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)) + } + } +} diff --git a/util/appmacro/KListBuilder.scala b/util/appmacro/KListBuilder.scala new file mode 100644 index 000000000..5b658ea69 --- /dev/null +++ b/util/appmacro/KListBuilder.scala @@ -0,0 +1,58 @@ +package sbt +package appmacro + + import Types.Id + import scala.tools.nsc.Global + import scala.reflect._ + import makro._ + +/** A `TupleBuilder` that uses a KList as the tuple representation.*/ +object KListBuilder extends TupleBuilder +{ + def make(c: 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.companionSymbol) + val kconsTpe = c.typeOf[KCons[Int,KNil,List]] + val kcons = kconsTpe.typeSymbol.companionSymbol + 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: Symbol = newTCVariable(NoSymbol) + + /** 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 ValDef(mods, name, tpt, _) :: xs => + val head = ValDef(mods, name, tpt, Select(Ident(prev.name), "head")) + 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 + } + + /** The input trees combined in a KList */ + val klist = (inputs :\ (knil: Tree))( (in, klist) => ApplyTree(kcons, in.expr, 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 = PolyType(tcVariable :: Nil, klistType) + val resultType = appliedType(representationC, idTC :: Nil) + val input = klist + val alistInstance = TypeApply(Select(Ident(alist), "klist"), typeTree(representationC) :: Nil) + def extract(param: ValDef) = bindKList(param, Nil, inputs.map(_.local)) + } +} \ No newline at end of file diff --git a/util/appmacro/MixedBuilder.scala b/util/appmacro/MixedBuilder.scala new file mode 100644 index 000000000..593f60382 --- /dev/null +++ b/util/appmacro/MixedBuilder.scala @@ -0,0 +1,16 @@ +package sbt +package appmacro + + import scala.reflect._ + import makro._ + +/** A builder that uses `TupleN` as the representation for small numbers of inputs (up to `TupleNBuilder.MaxInputs`) +* and `KList` for larger numbers of inputs. This builder cannot handle fewer than 2 inputs.*/ +object MixedBuilder extends TupleBuilder +{ + def make(c: 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) + } +} \ No newline at end of file diff --git a/util/appmacro/TupleBuilder.scala b/util/appmacro/TupleBuilder.scala new file mode 100644 index 000000000..f91d3c91c --- /dev/null +++ b/util/appmacro/TupleBuilder.scala @@ -0,0 +1,56 @@ +package sbt +package appmacro + + import Types.Id + import scala.tools.nsc.Global + import scala.reflect._ + import makro._ + +/** +* A `TupleBuilder` abstracts the work of constructing a tuple data structure such as a `TupleN` or `KList` +* and extracting values from it. The `Instance` macro implementation will (roughly) traverse the tree of its argument +* and ultimately obtain a list of expressions with type `M[T]` for different types `T`. +* The macro constructs an `Input` value for each of these expressions that contains the `Type` for `T`, +* the `Tree` for the expression, and a `ValDef` that will hold the value for the input. +* +* `TupleBuilder.apply` is provided with the list of `Input`s and is expected to provide three values in the returned BuilderResult. +* First, it returns the constructed tuple data structure Tree in `input`. +* Next, it provides the type constructor `representationC` that, when applied to M, gives the type of tuple data structure. +* For example, a builder that constructs a `Tuple3` for inputs `M[Int]`, `M[Boolean]`, and `M[String]` +* would provide a Type representing `[L[x]] (L[Int], L[Boolean], L[String])`. The `input` method +* would return a value whose type is that type constructor applied to M, or `(M[Int], M[Boolean], M[String])`. +* +* Finally, the `extract` method provides a list of vals that extract information from the applied input. +* The type of the applied input is the type constructor applied to `Id` (`[X] X`). +* 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: Context)(tcType: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] +} + +trait BuilderResult[C <: Context with Singleton] +{ + val ctx: C + import ctx.universe._ + + /** Represents the higher-order type constructor `[L[x]] ...` where `...` is the + * type of the data structure containing the added expressions, + * except that it is abstracted over the type constructor applied to each heterogeneous part of the type . */ + def representationC: PolyType + + /** The instance of AList for the input. For a `representationC` of `[L[x]]`, this `Tree` should have a `Type` of `AList[L]`*/ + def alistInstance: Tree + + /** Returns the completed value containing all expressions added to the builder. */ + def input: Tree + + /* The list of definitions that extract values from a value of type `$representationC[Id]`. + * The returned value should be identical to the `ValDef`s provided to the `TupleBuilder.make` method but with + * 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/util/appmacro/TupleNBuilder.scala b/util/appmacro/TupleNBuilder.scala new file mode 100644 index 000000000..ddf312f1b --- /dev/null +++ b/util/appmacro/TupleNBuilder.scala @@ -0,0 +1,51 @@ +package sbt +package appmacro + + import Types.Id + import scala.tools.nsc.Global + import scala.reflect._ + import makro._ + +/** A builder that uses a TupleN as the tuple representation. +* 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: 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.{Apply=>ApplyTree,_} + import util._ + + val global: Global = c.universe.asInstanceOf[Global] + val mTC: Type = mt.asInstanceOf[c.universe.Type] + + val ctx: c.type = c + val representationC: PolyType = { + val tcVariable: Symbol = newTCVariable(NoSymbol) + val tupleTypeArgs = inputs.map(in => typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type]) + val tuple = global.definitions.tupleType(tupleTypeArgs) + PolyType(tcVariable :: Nil, tuple.asInstanceOf[Type] ) + } + val resultType = appliedType(representationC, idTC :: Nil) + + val input: Tree = mkTuple(inputs.map(_.expr)) + val alistInstance: Tree = { + val select = Select(Ident(alist), TupleMethodName + inputs.size.toString) + TypeApply(select, 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 ValDef(mods, name, tpt, _) :: xs => + val x = ValDef(mods, name, tpt, Select(Ident(param.name), "_" + i.toString)) + bindTuple(param, x :: revBindings, xs, i+1) + case Nil => revBindings.reverse + } + } +} From a3d6a5d5b39499c3f8bfdc6e0cb1d491b302dfdd Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Wed, 1 Aug 2012 16:20:03 -0400 Subject: [PATCH 02/34] Task macro cleanup * use normal TypeTree constructor * remove unnecessary 'with Singleton' in macro utility * integrate changes suggested by @xeno-by * add refVar back and call asTypeConstructor instead of asType to refer to a type variable --- util/appmacro/ContextUtil.scala | 31 +++++++++++++--------------- util/appmacro/Instance.scala | 34 +++++++++---------------------- util/appmacro/KListBuilder.scala | 4 ++-- util/appmacro/TupleNBuilder.scala | 2 +- 4 files changed, 27 insertions(+), 44 deletions(-) diff --git a/util/appmacro/ContextUtil.scala b/util/appmacro/ContextUtil.scala index 31f61c356..8be859494 100644 --- a/util/appmacro/ContextUtil.scala +++ b/util/appmacro/ContextUtil.scala @@ -14,7 +14,7 @@ object ContextUtil { /** Utility methods for macros. Several methods assume that the context's universe is a full compiler (`scala.tools.nsc.Global`). * This is not thread safe due to the underlying Context and related data structures not being thread safe. * Use `ContextUtil[c.type](c)` to construct. */ -final class ContextUtil[C <: Context with Singleton](val ctx: C) +final class ContextUtil[C <: Context](val ctx: C) { import ctx.universe.{Apply=>ApplyTree,_} @@ -31,13 +31,11 @@ final class ContextUtil[C <: Context with Singleton](val ctx: C) * (The current implementation uses Context.fresh, which increments*/ def freshTermName(prefix: String) = newTermName(ctx.fresh("$" + prefix)) - def typeTree(tpe: Type) = TypeTree().setType(tpe) - /** Constructs a new, local ValDef with the given Type, a unique name, * the same position as `sym`, and an empty implementation (no rhs). */ def freshValDef(tpe: Type, sym: Symbol): ValDef = { - val vd = localValDef(typeTree(tpe), EmptyTree) + val vd = localValDef(TypeTree(tpe), EmptyTree) vd setPos getPos(sym) vd } @@ -54,10 +52,10 @@ final class ContextUtil[C <: Context with Singleton](val ctx: C) } /** Creates a new, synthetic type variable with the specified `owner`. */ - def newTypeVariable(owner: Symbol): Symbol = + def newTypeVariable(owner: Symbol, prefix: String = "T0"): TypeSymbol = { val global: Global = ctx.universe.asInstanceOf[Global] - owner.asInstanceOf[global.Symbol].newSyntheticTypeParam().asInstanceOf[ctx.universe.Symbol] + owner.asInstanceOf[global.Symbol].newSyntheticTypeParam(prefix, 0L).asInstanceOf[ctx.universe.TypeSymbol] } /** The type representing the type constructor `[X] X` */ val idTC: Type = @@ -65,28 +63,27 @@ final class ContextUtil[C <: Context with Singleton](val ctx: C) val tvar = newTypeVariable(NoSymbol) polyType(tvar :: Nil, refVar(tvar)) } + /** A Type that references the given type variable. */ + def refVar(variable: TypeSymbol): Type = variable.asTypeConstructor /** 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): Symbol = + def newTCVariable(owner: Symbol): TypeSymbol = { - val global: Global = ctx.universe.asInstanceOf[Global] - val tc = owner.asInstanceOf[global.Symbol].newSyntheticTypeParam() - val arg = tc.newSyntheticTypeParam("x", 0L) - tc.setInfo(global.PolyType(arg :: Nil, global.TypeBounds.empty)).asInstanceOf[ctx.universe.Symbol] + val tc = newTypeVariable(owner) + val arg = newTypeVariable(tc, "x") + tc.setTypeSignature(PolyType(arg :: Nil, emptyTypeBounds)) + tc } + def emptyTypeBounds: TypeBounds = TypeBounds(definitions.NothingClass.asType, definitions.AnyClass.asType) + /** Returns the Symbol that references the statically accessible singleton `i`. */ 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 => error("Instance must be static (was " + x + ").") } - /** Constructs a Type that references the given type variable. */ - def refVar(variable: Symbol): Type = typeRef(NoPrefix, variable, Nil) /** Returns the symbol for the non-private method named `name` for the class/module `obj`. */ - def method(obj: Symbol, name: String): Symbol = { - val global: Global = ctx.universe.asInstanceOf[Global] - obj.asInstanceOf[global.Symbol].info.nonPrivateMember(global.newTermName(name)).asInstanceOf[ctx.universe.Symbol] - } + def method(obj: Symbol, name: String): Symbol = obj.typeSignature.nonPrivateMember(newTermName(name)) /** Returns a Type representing the type constructor tcp.. For example, given * `object Demo { type M[x] = List[x] }`, the call `extractTC(Demo, "M")` will return a type representing diff --git a/util/appmacro/Instance.scala b/util/appmacro/Instance.scala index 05a80b4e8..6dec0eabc 100644 --- a/util/appmacro/Instance.scala +++ b/util/appmacro/Instance.scala @@ -84,10 +84,6 @@ object Instance implicit tt: c.TypeTag[T], mt: c.TypeTag[i.M[T]], it: c.TypeTag[i.type]): c.Expr[i.M[T]] = { import c.universe.{Apply=>ApplyTree,_} - - import scala.tools.nsc.Global - // Used to access compiler methods not yet exposed via the reflection/macro APIs - val global: Global = c.universe.asInstanceOf[Global] val util = ContextUtil[c.type](c) val mTC: Type = util.extractTC(i, InstanceTCName) @@ -113,19 +109,9 @@ object Instance // constructs a ValDef with a parameter modifier, a unique name, with the provided Type and with an empty rhs def freshMethodParameter(tpe: Type): ValDef = - ValDef(parameterModifiers, freshTermName("p"), typeTree(tpe), EmptyTree) + ValDef(parameterModifiers, freshTermName("p"), TypeTree(tpe), EmptyTree) def freshTermName(prefix: String) = newTermName(c.fresh("$" + prefix)) - def typeTree(tpe: Type) = TypeTree().setType(tpe) - - // constructs a function that applies f to each subtree of the input tree - def visitor(f: Tree => Unit): Tree => Unit = - { - val v: Transformer = new Transformer { - override def transform(tree: Tree): Tree = { f(tree); super.transform(tree) } - } - (tree: Tree) => v.transform(tree) - } /* Local definitions in the macro. This is used to ensure * references are to M instances defined outside of the macro call.*/ @@ -137,13 +123,13 @@ object Instance // 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. - val checkQual = visitor { + val checkQual: Tree => Unit = { case s @ ApplyTree(fun, qual :: Nil) => if(isWrapper(fun)) c.error(s.pos, DynamicDependencyError) case id @ Ident(name) if illegalReference(id.symbol) => c.error(id.pos, DynamicReferenceError) case _ => () } // adds the symbols for all non-Ident subtrees to `defs`. - val defSearch = visitor { + val defSearch: Tree => Unit = { case _: Ident => () case tree => if(tree.symbol ne null) defs += tree.symbol; } @@ -160,7 +146,7 @@ object Instance // no inputs, so construct M[T] via Instance.pure or pure+flatten def pure(body: Tree): Tree = { - val typeApplied = TypeApply(Select(instance, PureName), typeTree(treeType) :: Nil) + val typeApplied = TypeApply(Select(instance, PureName), TypeTree(treeType) :: Nil) val p = ApplyTree(typeApplied, Function(Nil, body) :: Nil) if(t.isLeft) p else flatten(p) } @@ -168,7 +154,7 @@ object Instance // the returned Tree will have type M[T] def flatten(m: Tree): Tree = { - val typedFlatten = TypeApply(Select(instance, FlattenName), typeTree(tt.tpe) :: Nil) + val typedFlatten = TypeApply(Select(instance, FlattenName), TypeTree(tt.tpe) :: Nil) ApplyTree(typedFlatten, m :: Nil) } @@ -177,7 +163,7 @@ object Instance { val variable = input.local val param = ValDef(parameterModifiers, variable.name, variable.tpt, EmptyTree) - val typeApplied = TypeApply(Select(instance, MapName), variable.tpt :: typeTree(treeType) :: Nil) + val typeApplied = TypeApply(Select(instance, MapName), variable.tpt :: TypeTree(treeType) :: Nil) val mapped = ApplyTree(typeApplied, input.expr :: Function(param :: Nil, body) :: Nil) if(t.isLeft) mapped else flatten(mapped) } @@ -189,8 +175,8 @@ object Instance val param = freshMethodParameter( appliedType(result.representationC, util.idTC :: Nil) ) val bindings = result.extract(param) val f = Function(param :: Nil, Block(bindings, body)) - val ttt = typeTree(treeType) - val typedApp = TypeApply(Select(instance, ApplyName), typeTree(result.representationC) :: ttt :: Nil) + val ttt = TypeTree(treeType) + val typedApp = TypeApply(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) } @@ -202,7 +188,7 @@ object Instance // the bound value of the input def addType(tpe: Type, qual: Tree): Tree = { - checkQual(qual) + qual.foreach(checkQual) val vd = util.freshValDef(tpe, qual.symbol) inputs ::= new Input(tpe, qual, vd) Ident(vd.name) @@ -223,7 +209,7 @@ object Instance } // collects all definitions in the tree. used for finding illegal references - defSearch(tree) + tree.foreach(defSearch) // applies the transformation // resetting attributes: a) must be local b) must be done diff --git a/util/appmacro/KListBuilder.scala b/util/appmacro/KListBuilder.scala index 5b658ea69..b57a39449 100644 --- a/util/appmacro/KListBuilder.scala +++ b/util/appmacro/KListBuilder.scala @@ -24,7 +24,7 @@ object KListBuilder extends TupleBuilder val kconsTC: Type = kconsTpe.typeConstructor /** This is the L in the type function [L[x]] ... */ - val tcVariable: Symbol = newTCVariable(NoSymbol) + val tcVariable: TypeSymbol = newTCVariable(NoSymbol) /** Instantiates KCons[h, t <: KList[L], L], where L is the type constructor variable */ def kconsType(h: Type, t: Type): Type = @@ -52,7 +52,7 @@ object KListBuilder extends TupleBuilder val representationC = PolyType(tcVariable :: Nil, klistType) val resultType = appliedType(representationC, idTC :: Nil) val input = klist - val alistInstance = TypeApply(Select(Ident(alist), "klist"), typeTree(representationC) :: Nil) + val alistInstance = TypeApply(Select(Ident(alist), "klist"), TypeTree(representationC) :: Nil) def extract(param: ValDef) = bindKList(param, Nil, inputs.map(_.local)) } } \ No newline at end of file diff --git a/util/appmacro/TupleNBuilder.scala b/util/appmacro/TupleNBuilder.scala index ddf312f1b..4f034adae 100644 --- a/util/appmacro/TupleNBuilder.scala +++ b/util/appmacro/TupleNBuilder.scala @@ -35,7 +35,7 @@ object TupleNBuilder extends TupleBuilder val input: Tree = mkTuple(inputs.map(_.expr)) val alistInstance: Tree = { val select = Select(Ident(alist), TupleMethodName + inputs.size.toString) - TypeApply(select, inputs.map(in => typeTree(in.tpe))) + TypeApply(select, inputs.map(in => TypeTree(in.tpe))) } def extract(param: ValDef): List[ValDef] = bindTuple(param, Nil, inputs.map(_.local), 1) From e967d354486a226c2fe8f0866e53f805e94f3e53 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Mon, 20 Aug 2012 15:55:50 -0400 Subject: [PATCH 03/34] move explicit task/setting macros to Def, move to AbsTypeTag --- util/appmacro/ContextUtil.scala | 12 +++++++----- util/appmacro/Instance.scala | 11 ++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/util/appmacro/ContextUtil.scala b/util/appmacro/ContextUtil.scala index 8be859494..2451c7c98 100644 --- a/util/appmacro/ContextUtil.scala +++ b/util/appmacro/ContextUtil.scala @@ -18,15 +18,17 @@ final class ContextUtil[C <: Context](val ctx: C) { import ctx.universe.{Apply=>ApplyTree,_} - val alistType = ctx.typeOf[AList[KList]] - val alist: Symbol = alistType.typeSymbol.companionSymbol - val alistTC: Type = alistType.typeConstructor + lazy val alistType = ctx.typeOf[AList[KList]] + lazy val alist: Symbol = alistType.typeSymbol.companionSymbol + lazy val alistTC: Type = alistType.typeConstructor /** Modifiers for a local val.*/ - val localModifiers = Modifiers(NoFlags) + lazy val localModifiers = Modifiers(NoFlags) def getPos(sym: Symbol) = if(sym eq null) NoPosition else sym.pos + def atypeOf[T](implicit att: AbsTypeTag[T]): Type = att.tpe + /** Constructs a unique term name with the given prefix within this Context. * (The current implementation uses Context.fresh, which increments*/ def freshTermName(prefix: String) = newTermName(ctx.fresh("$" + prefix)) @@ -58,7 +60,7 @@ final class ContextUtil[C <: Context](val ctx: C) owner.asInstanceOf[global.Symbol].newSyntheticTypeParam(prefix, 0L).asInstanceOf[ctx.universe.TypeSymbol] } /** The type representing the type constructor `[X] X` */ - val idTC: Type = + lazy val idTC: Type = { val tvar = newTypeVariable(NoSymbol) polyType(tvar :: Nil, refVar(tvar)) diff --git a/util/appmacro/Instance.scala b/util/appmacro/Instance.scala index 6dec0eabc..71360c1c7 100644 --- a/util/appmacro/Instance.scala +++ b/util/appmacro/Instance.scala @@ -18,7 +18,7 @@ trait Instance } trait Convert { - def apply[T: c.TypeTag](c: scala.reflect.makro.Context)(in: c.Tree): c.Tree + def apply[T: c.AbsTypeTag](c: scala.reflect.makro.Context)(in: c.Tree): c.Tree } trait MonadInstance extends Instance { @@ -80,18 +80,19 @@ object Instance * If this is for multi-input flatMap (app followed by flatMap), * this should be the argument wrapped in Right. */ - def contImpl[T: c.TypeTag](c: Context, i: Instance with Singleton, convert: Convert, builder: TupleBuilder)(t: Either[c.Expr[T], c.Expr[i.M[T]]])( - implicit tt: c.TypeTag[T], mt: c.TypeTag[i.M[T]], it: c.TypeTag[i.type]): c.Expr[i.M[T]] = + def contImpl[T](c: Context, i: Instance with Singleton, convert: Convert, builder: TupleBuilder)(t: Either[c.Expr[T], c.Expr[i.M[T]]])( + implicit tt: c.AbsTypeTag[T], it: c.TypeTag[i.type]): c.Expr[i.M[T]] = { import c.universe.{Apply=>ApplyTree,_} val util = ContextUtil[c.type](c) val mTC: Type = util.extractTC(i, InstanceTCName) + val mttpe: Type = appliedType(mTC, tt.tpe :: Nil).normalize // the tree for the macro argument val (tree, treeType) = t match { case Left(l) => (l.tree, tt.tpe.normalize) - case Right(r) => (r.tree, mt.tpe.normalize) + case Right(r) => (r.tree, mttpe) } val instanceSym = util.singleton(i) @@ -202,7 +203,7 @@ object Instance tree match { case ApplyTree(TypeApply(fun, t :: Nil), qual :: Nil) if isWrapper(fun) => - val tag = c.TypeTag(t.tpe) + val tag = c.AbsTypeTag(t.tpe) addType(t.tpe, convert(c)(qual)(tag) ) case _ => super.transform(tree) } From f536e6d9acf47b16131c0ea321ee3c52f6ee6042 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Fri, 24 Aug 2012 13:27:34 -0400 Subject: [PATCH 04/34] Scala 2.10.0-M7 --- util/appmacro/ContextUtil.scala | 15 ++++++++++----- util/appmacro/Instance.scala | 4 ++-- util/appmacro/KListBuilder.scala | 2 +- util/appmacro/MixedBuilder.scala | 2 +- util/appmacro/TupleBuilder.scala | 2 +- util/appmacro/TupleNBuilder.scala | 2 +- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/util/appmacro/ContextUtil.scala b/util/appmacro/ContextUtil.scala index 2451c7c98..ad66c84e2 100644 --- a/util/appmacro/ContextUtil.scala +++ b/util/appmacro/ContextUtil.scala @@ -2,7 +2,7 @@ package sbt package appmacro import scala.reflect._ - import makro._ + import macros._ import scala.tools.nsc.Global object ContextUtil { @@ -66,7 +66,7 @@ final class ContextUtil[C <: Context](val ctx: C) polyType(tvar :: Nil, refVar(tvar)) } /** A Type that references the given type variable. */ - def refVar(variable: TypeSymbol): Type = variable.asTypeConstructor + 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 = { @@ -75,7 +75,7 @@ final class ContextUtil[C <: Context](val ctx: C) tc.setTypeSignature(PolyType(arg :: Nil, emptyTypeBounds)) tc } - def emptyTypeBounds: TypeBounds = TypeBounds(definitions.NothingClass.asType, definitions.AnyClass.asType) + def emptyTypeBounds: TypeBounds = TypeBounds(definitions.NothingClass.toType, definitions.AnyClass.toType) /** Returns the Symbol that references the statically accessible singleton `i`. */ def singleton[T <: AnyRef with Singleton](i: T)(implicit it: ctx.TypeTag[i.type]): Symbol = @@ -85,7 +85,12 @@ final class ContextUtil[C <: Context](val ctx: C) } /** Returns the symbol for the non-private method named `name` for the class/module `obj`. */ - def method(obj: Symbol, name: String): Symbol = obj.typeSignature.nonPrivateMember(newTermName(name)) + def method(obj: Symbol, name: String): Symbol = { + val global: Global = ctx.universe.asInstanceOf[Global] + val ts: Type = obj.typeSignature + val m: global.Symbol = ts.asInstanceOf[global.Type].nonPrivateMember(global.newTermName(name)) + m.asInstanceOf[Symbol] + } /** Returns a Type representing the type constructor tcp.. For example, given * `object Demo { type M[x] = List[x] }`, the call `extractTC(Demo, "M")` will return a type representing @@ -97,7 +102,7 @@ final class ContextUtil[C <: Context](val ctx: C) 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.isHigherKinded, "Invalid type constructor: " + tc) + assert(tc != NoType && tc.takesTypeArgs, "Invalid type constructor: " + tc) tc } } \ No newline at end of file diff --git a/util/appmacro/Instance.scala b/util/appmacro/Instance.scala index 71360c1c7..e03a29cb2 100644 --- a/util/appmacro/Instance.scala +++ b/util/appmacro/Instance.scala @@ -18,7 +18,7 @@ trait Instance } trait Convert { - def apply[T: c.AbsTypeTag](c: scala.reflect.makro.Context)(in: c.Tree): c.Tree + def apply[T: c.AbsTypeTag](c: scala.reflect.macros.Context)(in: c.Tree): c.Tree } trait MonadInstance extends Instance { @@ -30,7 +30,7 @@ object InputWrapper } import scala.reflect._ - import makro._ + import macros._ object Instance { diff --git a/util/appmacro/KListBuilder.scala b/util/appmacro/KListBuilder.scala index b57a39449..7ae0696d0 100644 --- a/util/appmacro/KListBuilder.scala +++ b/util/appmacro/KListBuilder.scala @@ -4,7 +4,7 @@ package appmacro import Types.Id import scala.tools.nsc.Global import scala.reflect._ - import makro._ + import macros._ /** A `TupleBuilder` that uses a KList as the tuple representation.*/ object KListBuilder extends TupleBuilder diff --git a/util/appmacro/MixedBuilder.scala b/util/appmacro/MixedBuilder.scala index 593f60382..e58adb2b0 100644 --- a/util/appmacro/MixedBuilder.scala +++ b/util/appmacro/MixedBuilder.scala @@ -2,7 +2,7 @@ package sbt package appmacro import scala.reflect._ - import makro._ + import macros._ /** A builder that uses `TupleN` as the representation for small numbers of inputs (up to `TupleNBuilder.MaxInputs`) * and `KList` for larger numbers of inputs. This builder cannot handle fewer than 2 inputs.*/ diff --git a/util/appmacro/TupleBuilder.scala b/util/appmacro/TupleBuilder.scala index f91d3c91c..f6442cb02 100644 --- a/util/appmacro/TupleBuilder.scala +++ b/util/appmacro/TupleBuilder.scala @@ -4,7 +4,7 @@ package appmacro import Types.Id import scala.tools.nsc.Global import scala.reflect._ - import makro._ + import macros._ /** * A `TupleBuilder` abstracts the work of constructing a tuple data structure such as a `TupleN` or `KList` diff --git a/util/appmacro/TupleNBuilder.scala b/util/appmacro/TupleNBuilder.scala index 4f034adae..c7b9929ab 100644 --- a/util/appmacro/TupleNBuilder.scala +++ b/util/appmacro/TupleNBuilder.scala @@ -4,7 +4,7 @@ package appmacro import Types.Id import scala.tools.nsc.Global import scala.reflect._ - import makro._ + import macros._ /** A builder that uses a TupleN as the tuple representation. * It is limited to tuples of size 2 to `MaxInputs`. */ From c2760ecbddf0be5ca26ab187a60526cf4c05ef6c Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Fri, 21 Sep 2012 16:42:07 -0400 Subject: [PATCH 05/34] AbsTypeTag -> WeakTypeTag and converted more settings --- util/appmacro/ContextUtil.scala | 2 +- util/appmacro/Instance.scala | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/util/appmacro/ContextUtil.scala b/util/appmacro/ContextUtil.scala index ad66c84e2..3fd45207a 100644 --- a/util/appmacro/ContextUtil.scala +++ b/util/appmacro/ContextUtil.scala @@ -27,7 +27,7 @@ final class ContextUtil[C <: Context](val ctx: C) def getPos(sym: Symbol) = if(sym eq null) NoPosition else sym.pos - def atypeOf[T](implicit att: AbsTypeTag[T]): Type = att.tpe + def atypeOf[T](implicit att: WeakTypeTag[T]): Type = att.tpe /** Constructs a unique term name with the given prefix within this Context. * (The current implementation uses Context.fresh, which increments*/ diff --git a/util/appmacro/Instance.scala b/util/appmacro/Instance.scala index e03a29cb2..1dd51e26b 100644 --- a/util/appmacro/Instance.scala +++ b/util/appmacro/Instance.scala @@ -18,7 +18,7 @@ trait Instance } trait Convert { - def apply[T: c.AbsTypeTag](c: scala.reflect.macros.Context)(in: c.Tree): c.Tree + def apply[T: c.WeakTypeTag](c: scala.reflect.macros.Context)(in: c.Tree): c.Tree } trait MonadInstance extends Instance { @@ -81,7 +81,7 @@ object Instance * this should be the argument wrapped in Right. */ def contImpl[T](c: Context, i: Instance with Singleton, convert: Convert, builder: TupleBuilder)(t: Either[c.Expr[T], c.Expr[i.M[T]]])( - implicit tt: c.AbsTypeTag[T], it: c.TypeTag[i.type]): c.Expr[i.M[T]] = + implicit tt: c.WeakTypeTag[T], it: c.TypeTag[i.type]): c.Expr[i.M[T]] = { import c.universe.{Apply=>ApplyTree,_} @@ -203,7 +203,7 @@ object Instance tree match { case ApplyTree(TypeApply(fun, t :: Nil), qual :: Nil) if isWrapper(fun) => - val tag = c.AbsTypeTag(t.tpe) + val tag = c.WeakTypeTag(t.tpe) addType(t.tpe, convert(c)(qual)(tag) ) case _ => super.transform(tree) } From 40a034c3a7cf329c4d8394cf152b28a02b636b9f Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Fri, 2 Nov 2012 14:20:17 -0400 Subject: [PATCH 06/34] InputTask macro Similar to task macros, the parsed value is accessed by calling `parsed` on a Parser[T], Initialize[Parser[T]], or Initialize[State => Parser[T]]. Values of tasks and settings may be accessed as usual via `value`. --- util/appmacro/ContextUtil.scala | 98 ++++++++++++++++++++++++++++++++ util/appmacro/Instance.scala | 99 +++++++++++++-------------------- 2 files changed, 138 insertions(+), 59 deletions(-) diff --git a/util/appmacro/ContextUtil.scala b/util/appmacro/ContextUtil.scala index 3fd45207a..a62846e78 100644 --- a/util/appmacro/ContextUtil.scala +++ b/util/appmacro/ContextUtil.scala @@ -4,11 +4,32 @@ package appmacro import scala.reflect._ import macros._ import scala.tools.nsc.Global + import ContextUtil.{DynamicDependencyError, DynamicReferenceError} object ContextUtil { + final val DynamicDependencyError = "Illegal dynamic dependency" + final val DynamicReferenceError = "Illegal dynamic reference" + /** Constructs an object with utility methods for operating in the provided macro context `c`. * Callers should explicitly specify the type parameter as `c.type` in order to preserve the path dependent types. */ def apply[C <: Context with Singleton](c: C): ContextUtil[C] = new ContextUtil(c) + + + /** Helper for implementing a no-argument macro that is introduced via an implicit. + * This method removes the implicit conversion and evaluates the function `f` on the target of the conversion. + * + * 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, S: c.WeakTypeTag](c: Context)(f: c.Expr[S] => c.Expr[T]): c.Expr[T] = + { + import c.universe._ + c.macroApplication match { + case Select(Apply(_, t :: Nil), _) => f( c.Expr[S](t) ) + case x => unexpectedTree(x) + } + } + + def unexpectedTree[C <: Context](tree: C#Tree): Nothing = error("Unexpected macro application tree (" + tree.getClass + "): " + tree) } /** Utility methods for macros. Several methods assume that the context's universe is a full compiler (`scala.tools.nsc.Global`). @@ -42,6 +63,50 @@ final class ContextUtil[C <: Context](val ctx: C) vd } + /* Tests whether a Tree is a Select on `methodName`. */ + def isWrapper(methodName: String): Tree => Boolean = { + case Select(_, nme) => nme.decoded == methodName + case _ => false + } + + lazy val parameterModifiers = Modifiers(Flag.PARAM) + + /** 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: 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(fun, tpe :: Nil), qual :: Nil) if isWrapper(fun) => () + case tree => + if(tree.symbol ne null) defs += tree.symbol; + super.traverse(tree) + } + } + process.traverse(tree) + defs + } + + /** A reference is illegal if it is to an M instance defined within the scope of the macro call. + * As an approximation, disallow referenced to any local definitions `defs`. */ + def illegalReference(defs: collection.Set[Symbol], sym: Symbol): Boolean = + sym != null && sym != NoSymbol && defs.contains(sym) + + /** 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: Tree => Boolean): Tree => Unit = { + case s @ ApplyTree(TypeApply(fun, tpe :: Nil), qual :: Nil) => if(isWrapper(fun)) ctx.error(s.pos, DynamicDependencyError) + case id @ Ident(name) if illegalReference(defs, id.symbol) => ctx.error(id.pos, DynamicReferenceError + ": " + name) + case _ => () + } + + /** Constructs a ValDef with a parameter modifier, a unique name, with the provided Type and with an empty rhs. */ + def freshMethodParameter(tpe: Type): ValDef = + ValDef(parameterModifiers, freshTermName("p"), TypeTree(tpe), EmptyTree) + /** Constructs a ValDef with local modifiers and a unique name. */ def localValDef(tpt: Tree, rhs: Tree): ValDef = ValDef(localModifiers, freshTermName("q"), tpt, rhs) @@ -75,8 +140,18 @@ final class ContextUtil[C <: Context](val ctx: C) tc.setTypeSignature(PolyType(arg :: Nil, emptyTypeBounds)) tc } + /** >: Nothing <: Any */ def emptyTypeBounds: TypeBounds = TypeBounds(definitions.NothingClass.toType, definitions.AnyClass.toType) + /** Create a Tree that references the `val` represented by `vd`. */ + def refVal(vd: ValDef): Tree = + { + val t = Ident(vd.name) + assert(vd.tpt.tpe != null, "val type is null: " + vd + ", tpt: " + vd.tpt.tpe) + t.setType(vd.tpt.tpe) + t + } + /** Returns the Symbol that references the statically accessible singleton `i`. */ def singleton[T <: AnyRef with Singleton](i: T)(implicit it: ctx.TypeTag[i.type]): Symbol = it.tpe match { @@ -105,4 +180,27 @@ final class ContextUtil[C <: Context](val ctx: C) assert(tc != NoType && tc.takesTypeArgs, "Invalid type constructor: " + tc) tc } + + /** Substitutes wrappers in tree `t` with the result of `subWrapper`. + * A wrapper is a Tree of the form `f[T](v)` for which isWrapper() returns true. + * Typically, `f` is a `Select` or `Ident`. + * The wrapper is replaced with the result of `subWrapper(, )` */ + def transformWrappers(t: Tree, isWrapper: Tree => Boolean, subWrapper: (Type, Tree) => Tree): 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(fun, targ :: Nil), qual :: Nil) if isWrapper(fun) => + assert(qual.tpe != null, "Internal error: null type for wrapped tree with " + qual.getClass + "\n\t" + qual + "\n in " + t) + subWrapper(targ.tpe, qual) + case _ => super.transform(tree) + } + } + + appTransformer.transform(t) + } } \ No newline at end of file diff --git a/util/appmacro/Instance.scala b/util/appmacro/Instance.scala index 1dd51e26b..f70941dd0 100644 --- a/util/appmacro/Instance.scala +++ b/util/appmacro/Instance.scala @@ -24,24 +24,45 @@ trait MonadInstance extends Instance { def flatten[T](in: M[M[T]]): M[T] } -object InputWrapper -{ - def wrap[T](in: Any): T = error("This method is an implementation detail and should not be referenced.") -} import scala.reflect._ import macros._ +object InputWrapper +{ + /** The name of the wrapper method should be obscure. + * Wrapper checking is based solely on this name, so it must not conflict with a user method name. + * The user should never see this method because it is compile-time only and only used internally by the task macro system.*/ + final val WrapName = "wrap_\u2603\u2603" + + // This method should be annotated as compile-time only when that feature is implemented + def wrap_\u2603\u2603[T](in: Any): T = error("This method is an implementation detail and should not be referenced.") + + /** Wraps an arbitrary Tree in a call to the `wrap` method of this module for later processing by an enclosing macro. + * The resulting Tree is the manually constructed version of: + * + * `c.universe.reify { InputWrapper.[T](ts.splice) }` + */ + def wrapKey[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any]): c.Expr[T] = + { + import c.universe.{Apply=>ApplyTree,_} + val util = new ContextUtil[c.type](c) + val iw = util.singleton(InputWrapper) + val tpe = c.weakTypeOf[T] + val nme = newTermName(WrapName).encoded + val tree = ApplyTree(TypeApply(Select(Ident(iw), nme), TypeTree(tpe) :: Nil), ts.tree :: Nil) + tree.setPos(ts.tree.pos) + c.Expr[T](tree) + } +} + object Instance { - final val DynamicDependencyError = "Illegal dynamic dependency." - final val DynamicReferenceError = "Illegal dynamic reference." final val ApplyName = "app" final val FlattenName = "flatten" final val PureName = "pure" final val MapName = "map" final val InstanceTCName = "M" - final val WrapName = "wrap" final class Input[U <: Universe with Singleton](val tpe: U#Type, val expr: U#Tree, val local: U#ValDef) @@ -99,41 +120,14 @@ object Instance // A Tree that references the statically accessible Instance that provides the actual implementations of map, flatMap, ... val instance = Ident(instanceSym) - val parameterModifiers = Modifiers(Flag.PARAM) - - val wrapperSym = util.singleton(InputWrapper) - val wrapMethodSymbol = util.method(wrapperSym, WrapName) - def isWrapper(fun: Tree) = fun.symbol == wrapMethodSymbol + val isWrapper: Tree => Boolean = util.isWrapper(InputWrapper.WrapName) type In = Input[c.universe.type] var inputs = List[In]() - // constructs a ValDef with a parameter modifier, a unique name, with the provided Type and with an empty rhs - def freshMethodParameter(tpe: Type): ValDef = - ValDef(parameterModifiers, freshTermName("p"), TypeTree(tpe), EmptyTree) - - def freshTermName(prefix: String) = newTermName(c.fresh("$" + prefix)) - - /* Local definitions in the macro. This is used to ensure - * references are to M instances defined outside of the macro call.*/ - val defs = new collection.mutable.HashSet[Symbol] - - // a reference is illegal if it is to an M instance defined within the scope of the macro call - def illegalReference(sym: Symbol): Boolean = - sym != null && sym != NoSymbol && defs.contains(sym) - - // 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. - val checkQual: Tree => Unit = { - case s @ ApplyTree(fun, qual :: Nil) => if(isWrapper(fun)) c.error(s.pos, DynamicDependencyError) - case id @ Ident(name) if illegalReference(id.symbol) => c.error(id.pos, DynamicReferenceError) - case _ => () - } - // adds the symbols for all non-Ident subtrees to `defs`. - val defSearch: Tree => Unit = { - case _: Ident => () - case tree => if(tree.symbol ne null) defs += tree.symbol; - } + // Local definitions in the macro. This is used to ensure references are to M instances defined outside of the macro call. + val defs = util.collectDefs(tree, isWrapper) + val checkQual: Tree => Unit = util.checkReferences(defs, isWrapper) // transforms the original tree into calls to the Instance functions pure, map, ..., // resulting in a value of type M[T] @@ -163,7 +157,7 @@ object Instance def single(body: Tree, input: In): Tree = { val variable = input.local - val param = ValDef(parameterModifiers, variable.name, variable.tpt, EmptyTree) + val param = ValDef(util.parameterModifiers, variable.name, variable.tpt, EmptyTree) val typeApplied = TypeApply(Select(instance, MapName), variable.tpt :: TypeTree(treeType) :: Nil) val mapped = ApplyTree(typeApplied, input.expr :: Function(param :: Nil, body) :: Nil) if(t.isLeft) mapped else flatten(mapped) @@ -173,7 +167,7 @@ object Instance def arbArity(body: Tree, inputs: List[In]): Tree = { val result = builder.make(c)(mTC, inputs) - val param = freshMethodParameter( appliedType(result.representationC, util.idTC :: Nil) ) + val param = util.freshMethodParameter( appliedType(result.representationC, util.idTC :: Nil) ) val bindings = result.extract(param) val f = Function(param :: Nil, Block(bindings, body)) val ttt = TypeTree(treeType) @@ -192,30 +186,17 @@ object Instance qual.foreach(checkQual) val vd = util.freshValDef(tpe, qual.symbol) inputs ::= new Input(tpe, qual, vd) - Ident(vd.name) + util.refVal(vd) } - - // the main tree transformer that replaces calls to InputWrapper.wrap(x) with - // plain Idents that reference the actual input value - object appTransformer extends Transformer + def sub(tpe: Type, qual: Tree): Tree = { - override def transform(tree: Tree): Tree = - tree match - { - case ApplyTree(TypeApply(fun, t :: Nil), qual :: Nil) if isWrapper(fun) => - val tag = c.WeakTypeTag(t.tpe) - addType(t.tpe, convert(c)(qual)(tag) ) - case _ => super.transform(tree) - } + val tag = c.WeakTypeTag(tpe) + addType(tpe, convert(c)(qual)(tag) ) } - // collects all definitions in the tree. used for finding illegal references - tree.foreach(defSearch) - // applies the transformation - // resetting attributes: a) must be local b) must be done - // on the transformed tree and not the wrapped tree or else there are obscure errors - val tr = makeApp( c.resetLocalAttrs(appTransformer.transform(tree)) ) + // resetting attributes must be: a) local b) done here and not wider or else there are obscure errors + val tr = makeApp( c.resetLocalAttrs( util.transformWrappers(tree, isWrapper, (tpe, tr) => sub(tpe, tr)) ) ) c.Expr[i.M[T]](tr) } From 41e2fdf647de22e1c4ce8979ccb6d753cbedc28c Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sun, 18 Nov 2012 09:20:24 -0500 Subject: [PATCH 07/34] Explicitly specify type parameters in calls to KCons in KList builder. scalac couldn't infer the type constructor otherwise. --- util/appmacro/KListBuilder.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/util/appmacro/KListBuilder.scala b/util/appmacro/KListBuilder.scala index 7ae0696d0..551566419 100644 --- a/util/appmacro/KListBuilder.scala +++ b/util/appmacro/KListBuilder.scala @@ -41,8 +41,16 @@ object KListBuilder extends TupleBuilder 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 + } + /** The input trees combined in a KList */ - val klist = (inputs :\ (knil: Tree))( (in, klist) => ApplyTree(kcons, in.expr, 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. From e719c5fb2ee3f68679900b7396df28e9798cef1a Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Fri, 7 Dec 2012 10:27:08 -0800 Subject: [PATCH 08/34] Follow source layout convention supported by Eclipse. Moved source files so directory structure follow package structure. That makes it possible to use Scala Eclipse plugin with sbt's source code. --- util/appmacro/{ => src/main/scala/sbt/appmacro}/ContextUtil.scala | 0 util/appmacro/{ => src/main/scala/sbt/appmacro}/Instance.scala | 0 .../appmacro/{ => src/main/scala/sbt/appmacro}/KListBuilder.scala | 0 .../appmacro/{ => src/main/scala/sbt/appmacro}/MixedBuilder.scala | 0 .../appmacro/{ => src/main/scala/sbt/appmacro}/TupleBuilder.scala | 0 .../{ => src/main/scala/sbt/appmacro}/TupleNBuilder.scala | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename util/appmacro/{ => src/main/scala/sbt/appmacro}/ContextUtil.scala (100%) rename util/appmacro/{ => src/main/scala/sbt/appmacro}/Instance.scala (100%) rename util/appmacro/{ => src/main/scala/sbt/appmacro}/KListBuilder.scala (100%) rename util/appmacro/{ => src/main/scala/sbt/appmacro}/MixedBuilder.scala (100%) rename util/appmacro/{ => src/main/scala/sbt/appmacro}/TupleBuilder.scala (100%) rename util/appmacro/{ => src/main/scala/sbt/appmacro}/TupleNBuilder.scala (100%) diff --git a/util/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala similarity index 100% rename from util/appmacro/ContextUtil.scala rename to util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala diff --git a/util/appmacro/Instance.scala b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala similarity index 100% rename from util/appmacro/Instance.scala rename to util/appmacro/src/main/scala/sbt/appmacro/Instance.scala diff --git a/util/appmacro/KListBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala similarity index 100% rename from util/appmacro/KListBuilder.scala rename to util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala diff --git a/util/appmacro/MixedBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala similarity index 100% rename from util/appmacro/MixedBuilder.scala rename to util/appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala diff --git a/util/appmacro/TupleBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala similarity index 100% rename from util/appmacro/TupleBuilder.scala rename to util/appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala diff --git a/util/appmacro/TupleNBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala similarity index 100% rename from util/appmacro/TupleNBuilder.scala rename to util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala From 2929e4e72494cdad23c9a31ddeebac993326252b Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Mon, 28 Jan 2013 17:14:53 -0500 Subject: [PATCH 09/34] Reduce InputTask to the ideal wrapper around 'State => Parser[Initialize[Task[T]]]' Ref #407. --- .../appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index a62846e78..3afc0d617 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -143,6 +143,13 @@ final class ContextUtil[C <: Context](val ctx: C) /** >: Nothing <: Any */ def emptyTypeBounds: TypeBounds = TypeBounds(definitions.NothingClass.toType, definitions.AnyClass.toType) + def functionType(args: List[Type], result: Type): Type = + { + val global: Global = ctx.universe.asInstanceOf[Global] + 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`. */ def refVal(vd: ValDef): Tree = { From f6c73ff4c77e121f764fe43dbd982abecd2cff3f Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Mon, 28 Jan 2013 17:14:53 -0500 Subject: [PATCH 10/34] use standard Context.weakTypeOf --- util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index 3afc0d617..85a2aed50 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -48,8 +48,6 @@ final class ContextUtil[C <: Context](val ctx: C) def getPos(sym: Symbol) = if(sym eq null) NoPosition else sym.pos - def atypeOf[T](implicit att: WeakTypeTag[T]): Type = att.tpe - /** Constructs a unique term name with the given prefix within this Context. * (The current implementation uses Context.fresh, which increments*/ def freshTermName(prefix: String) = newTermName(ctx.fresh("$" + prefix)) From 13e1b00436e14822df7d14c985711cc9249562d5 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Tue, 19 Feb 2013 08:54:40 -0500 Subject: [PATCH 11/34] Use @compileTimeOnly for .value and .parsed methods. Needed to set position on wrapper method for correct error message position. --- .../main/scala/sbt/appmacro/ContextUtil.scala | 4 ++-- .../main/scala/sbt/appmacro/Instance.scala | 20 ++++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index 85a2aed50..41a52003f 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -20,11 +20,11 @@ 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, S: c.WeakTypeTag](c: Context)(f: c.Expr[S] => c.Expr[T]): c.Expr[T] = + def selectMacroImpl[T: c.WeakTypeTag, S: c.WeakTypeTag](c: Context)(f: (c.Expr[S], c.Position) => c.Expr[T]): c.Expr[T] = { import c.universe._ c.macroApplication match { - case Select(Apply(_, t :: Nil), _) => f( c.Expr[S](t) ) + case s @ Select(Apply(_, t :: Nil), tp) => f( c.Expr[S](t), s.pos ) case x => unexpectedTree(x) } } diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala index f70941dd0..49b8bb71a 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala @@ -27,7 +27,9 @@ trait MonadInstance extends Instance import scala.reflect._ import macros._ + import reflect.internal.annotations.compileTimeOnly +// This needs to be moved to main/settings object InputWrapper { /** The name of the wrapper method should be obscure. @@ -35,22 +37,26 @@ object InputWrapper * The user should never see this method because it is compile-time only and only used internally by the task macro system.*/ final val WrapName = "wrap_\u2603\u2603" - // This method should be annotated as compile-time only when that feature is implemented + @compileTimeOnly("`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.") def wrap_\u2603\u2603[T](in: Any): T = error("This method is an implementation detail and should not be referenced.") - /** Wraps an arbitrary Tree in a call to the `wrap` method of this module for later processing by an enclosing macro. + def wrapKey[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = wrapImpl[T,InputWrapper.type](c, InputWrapper, WrapName)(ts, pos) + + /** Wraps an arbitrary Tree in a call to the `.` method of this module for later processing by an enclosing macro. * The resulting Tree is the manually constructed version of: * - * `c.universe.reify { InputWrapper.[T](ts.splice) }` + * `c.universe.reify { .[T](ts.splice) }` */ - def wrapKey[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any]): c.Expr[T] = + def wrapImpl[T: c.WeakTypeTag, S <: AnyRef with Singleton](c: Context, s: S, wrapName: String)(ts: c.Expr[Any], pos: c.Position)(implicit it: c.TypeTag[s.type]): c.Expr[T] = { import c.universe.{Apply=>ApplyTree,_} val util = new ContextUtil[c.type](c) - val iw = util.singleton(InputWrapper) + val iw = util.singleton(s) val tpe = c.weakTypeOf[T] - val nme = newTermName(WrapName).encoded - val tree = ApplyTree(TypeApply(Select(Ident(iw), nme), TypeTree(tpe) :: Nil), ts.tree :: Nil) + val nme = newTermName(wrapName).encoded + val sel = Select(Ident(iw), nme) + sel.setPos(pos) // need to set the position on Select, because that is where the compileTimeOnly check looks + val tree = ApplyTree(TypeApply(sel, TypeTree(tpe) :: Nil), ts.tree :: Nil) tree.setPos(ts.tree.pos) c.Expr[T](tree) } From d426035da3fddcf74789c10fb80f6685ebcd1e54 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Mon, 25 Feb 2013 09:24:04 -0500 Subject: [PATCH 12/34] deprecations --- .../src/main/scala/sbt/appmacro/ContextUtil.scala | 6 ++++-- .../src/main/scala/sbt/appmacro/Instance.scala | 12 ++++++------ .../src/main/scala/sbt/appmacro/KListBuilder.scala | 6 +++--- .../src/main/scala/sbt/appmacro/TupleNBuilder.scala | 6 +++--- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index 41a52003f..b9fe29388 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -29,7 +29,7 @@ object ContextUtil { } } - def unexpectedTree[C <: Context](tree: C#Tree): Nothing = error("Unexpected macro application tree (" + tree.getClass + "): " + tree) + def unexpectedTree[C <: Context](tree: C#Tree): Nothing = sys.error("Unexpected macro application tree (" + tree.getClass + "): " + tree) } /** Utility methods for macros. Several methods assume that the context's universe is a full compiler (`scala.tools.nsc.Global`). @@ -161,9 +161,11 @@ final class ContextUtil[C <: 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 => 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, newTermName(name)) + /** Returns the symbol for the non-private method named `name` for the class/module `obj`. */ def method(obj: Symbol, name: String): Symbol = { val global: Global = ctx.universe.asInstanceOf[Global] diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala index 49b8bb71a..d86fb2a4c 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala @@ -38,7 +38,7 @@ object InputWrapper final val WrapName = "wrap_\u2603\u2603" @compileTimeOnly("`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.") - def wrap_\u2603\u2603[T](in: Any): T = error("This method is an implementation detail and should not be referenced.") + def wrap_\u2603\u2603[T](in: Any): T = sys.error("This method is an implementation detail and should not be referenced.") def wrapKey[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = wrapImpl[T,InputWrapper.type](c, InputWrapper, WrapName)(ts, pos) @@ -54,7 +54,7 @@ object InputWrapper val iw = util.singleton(s) val tpe = c.weakTypeOf[T] val nme = newTermName(wrapName).encoded - val sel = Select(Ident(iw), nme) + val sel = util.select(Ident(iw), nme) sel.setPos(pos) // need to set the position on Select, because that is where the compileTimeOnly check looks val tree = ApplyTree(TypeApply(sel, TypeTree(tpe) :: Nil), ts.tree :: Nil) tree.setPos(ts.tree.pos) @@ -147,7 +147,7 @@ object Instance // no inputs, so construct M[T] via Instance.pure or pure+flatten def pure(body: Tree): Tree = { - val typeApplied = TypeApply(Select(instance, PureName), TypeTree(treeType) :: Nil) + val typeApplied = TypeApply(util.select(instance, PureName), TypeTree(treeType) :: Nil) val p = ApplyTree(typeApplied, Function(Nil, body) :: Nil) if(t.isLeft) p else flatten(p) } @@ -155,7 +155,7 @@ object Instance // the returned Tree will have type M[T] def flatten(m: Tree): Tree = { - val typedFlatten = TypeApply(Select(instance, FlattenName), TypeTree(tt.tpe) :: Nil) + val typedFlatten = TypeApply(util.select(instance, FlattenName), TypeTree(tt.tpe) :: Nil) ApplyTree(typedFlatten, m :: Nil) } @@ -164,7 +164,7 @@ object Instance { val variable = input.local val param = ValDef(util.parameterModifiers, variable.name, variable.tpt, EmptyTree) - val typeApplied = TypeApply(Select(instance, MapName), variable.tpt :: TypeTree(treeType) :: Nil) + val typeApplied = TypeApply(util.select(instance, MapName), variable.tpt :: TypeTree(treeType) :: Nil) val mapped = ApplyTree(typeApplied, input.expr :: Function(param :: Nil, body) :: Nil) if(t.isLeft) mapped else flatten(mapped) } @@ -177,7 +177,7 @@ object Instance val bindings = result.extract(param) val f = Function(param :: Nil, Block(bindings, body)) val ttt = TypeTree(treeType) - val typedApp = TypeApply(Select(instance, ApplyName), TypeTree(result.representationC) :: ttt :: Nil) + 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) } diff --git a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala index 551566419..195123c6c 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala @@ -34,8 +34,8 @@ object KListBuilder extends TupleBuilder params match { case ValDef(mods, name, tpt, _) :: xs => - val head = ValDef(mods, name, tpt, Select(Ident(prev.name), "head")) - val tail = localValDef(TypeTree(), Select(Ident(prev.name), "tail")) + val head = ValDef(mods, name, tpt, select(Ident(prev.name), "head")) + 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 @@ -60,7 +60,7 @@ object KListBuilder extends TupleBuilder val representationC = PolyType(tcVariable :: Nil, klistType) val resultType = appliedType(representationC, idTC :: Nil) val input = klist - val alistInstance = TypeApply(Select(Ident(alist), "klist"), TypeTree(representationC) :: Nil) + val alistInstance = TypeApply(select(Ident(alist), "klist"), TypeTree(representationC) :: Nil) def extract(param: ValDef) = bindKList(param, Nil, inputs.map(_.local)) } } \ No newline at end of file diff --git a/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala index c7b9929ab..805098353 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala @@ -34,8 +34,8 @@ object TupleNBuilder extends TupleBuilder val input: Tree = mkTuple(inputs.map(_.expr)) val alistInstance: Tree = { - val select = Select(Ident(alist), TupleMethodName + inputs.size.toString) - TypeApply(select, inputs.map(in => TypeTree(in.tpe))) + 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) @@ -43,7 +43,7 @@ object TupleNBuilder extends TupleBuilder params match { case ValDef(mods, name, tpt, _) :: xs => - val x = ValDef(mods, name, tpt, Select(Ident(param.name), "_" + i.toString)) + val x = ValDef(mods, name, tpt, select(Ident(param.name), "_" + i.toString)) bindTuple(param, x :: revBindings, xs, i+1) case Nil => revBindings.reverse } From ee5f09d8101437169ed8e7993478e6f97d2a5465 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Fri, 8 Mar 2013 14:23:30 -0500 Subject: [PATCH 13/34] Construct input tasks in multiple steps to allow input task reuse. Fixes #407. --- .../main/scala/sbt/appmacro/ContextUtil.scala | 31 ++++---- .../src/main/scala/sbt/appmacro/Convert.scala | 38 ++++++++++ .../main/scala/sbt/appmacro/Instance.scala | 71 ++++++------------- 3 files changed, 74 insertions(+), 66 deletions(-) create mode 100644 util/appmacro/src/main/scala/sbt/appmacro/Convert.scala diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index b9fe29388..48dd32466 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -20,11 +20,11 @@ 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, S: c.WeakTypeTag](c: Context)(f: (c.Expr[S], c.Position) => c.Expr[T]): c.Expr[T] = + def selectMacroImpl[T: c.WeakTypeTag](c: 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[S](t), s.pos ) + case s @ Select(Apply(_, t :: Nil), tp) => f( c.Expr[Any](t), s.pos ) case x => unexpectedTree(x) } } @@ -61,24 +61,18 @@ final class ContextUtil[C <: Context](val ctx: C) vd } - /* Tests whether a Tree is a Select on `methodName`. */ - def isWrapper(methodName: String): Tree => Boolean = { - case Select(_, nme) => nme.decoded == methodName - case _ => false - } - lazy val parameterModifiers = Modifiers(Flag.PARAM) /** 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: Tree => Boolean): collection.Set[Symbol] = + 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(fun, tpe :: Nil), qual :: Nil) if isWrapper(fun) => () + case ApplyTree(TypeApply(Select(_, nme), tpe :: Nil), qual :: Nil) if isWrapper(nme.decoded, tpe.tpe, qual) => () case tree => if(tree.symbol ne null) defs += tree.symbol; super.traverse(tree) @@ -95,8 +89,9 @@ final class ContextUtil[C <: 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: Tree => Boolean): Tree => Unit = { - case s @ ApplyTree(TypeApply(fun, tpe :: Nil), qual :: Nil) => if(isWrapper(fun)) ctx.error(s.pos, DynamicDependencyError) + 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.decoded, tpe.tpe, qual)) ctx.error(s.pos, DynamicDependencyError) case id @ Ident(name) if illegalReference(defs, id.symbol) => ctx.error(id.pos, DynamicReferenceError + ": " + name) case _ => () } @@ -189,10 +184,10 @@ final class ContextUtil[C <: Context](val ctx: C) } /** Substitutes wrappers in tree `t` with the result of `subWrapper`. - * A wrapper is a Tree of the form `f[T](v)` for which isWrapper() returns true. + * A wrapper is a Tree of the form `f[T](v)` for which isWrapper(, , .target) returns true. * Typically, `f` is a `Select` or `Ident`. * The wrapper is replaced with the result of `subWrapper(, )` */ - def transformWrappers(t: Tree, isWrapper: Tree => Boolean, subWrapper: (Type, Tree) => Tree): Tree = + def transformWrappers(t: Tree, subWrapper: (String, Type, 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 @@ -201,9 +196,11 @@ final class ContextUtil[C <: Context](val ctx: C) override def transform(tree: Tree): Tree = tree match { - case ApplyTree(TypeApply(fun, targ :: Nil), qual :: Nil) if isWrapper(fun) => - assert(qual.tpe != null, "Internal error: null type for wrapped tree with " + qual.getClass + "\n\t" + qual + "\n in " + t) - subWrapper(targ.tpe, qual) + case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) => subWrapper(nme.decoded, targ.tpe, qual) match { + case Converted.Success(t, finalTx) => finalTx(t) + case Converted.Failure(p,m) => ctx.abort(p, m) + case _: Converted.NotApplicable[_] => super.transform(tree) + } case _ => super.transform(tree) } } diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala b/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala new file mode 100644 index 000000000..6dedf776b --- /dev/null +++ b/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala @@ -0,0 +1,38 @@ +package sbt +package appmacro + + import scala.reflect._ + import macros._ + import Types.idFun + +abstract class Convert +{ + def apply[T: c.WeakTypeTag](c: Context)(nme: String, in: c.Tree): Converted[c.type] + def asPredicate(c: Context): (String, c.Type, c.Tree) => Boolean = + (n,tpe,tree) => { + val tag = c.WeakTypeTag(tpe) + apply(c)(n,tree)(tag).isSuccess + } +} +sealed trait Converted[C <: Context with Singleton] { + def isSuccess: Boolean + def transform(f: C#Tree => C#Tree): Converted[C] +} +object Converted { + def NotApplicable[C <: Context with Singleton] = new NotApplicable[C] + final case class Failure[C <: 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) + } + final class NotApplicable[C <: Context with Singleton] extends Converted[C] { + def isSuccess = false + def transform(f: C#Tree => C#Tree): Converted[C] = this + } + final case class Success[C <: 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 <: Context with Singleton](tree: C#Tree): Success[C] = Success(tree, idFun) + } +} \ No newline at end of file diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala index d86fb2a4c..3e8b45cf0 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala @@ -16,10 +16,7 @@ trait Instance def map[S,T](in: M[S], f: S => T): M[T] def pure[T](t: () => T): M[T] } -trait Convert -{ - def apply[T: c.WeakTypeTag](c: scala.reflect.macros.Context)(in: c.Tree): c.Tree -} + trait MonadInstance extends Instance { def flatten[T](in: M[M[T]]): M[T] @@ -29,39 +26,6 @@ trait MonadInstance extends Instance import macros._ import reflect.internal.annotations.compileTimeOnly -// This needs to be moved to main/settings -object InputWrapper -{ - /** The name of the wrapper method should be obscure. - * Wrapper checking is based solely on this name, so it must not conflict with a user method name. - * The user should never see this method because it is compile-time only and only used internally by the task macro system.*/ - final val WrapName = "wrap_\u2603\u2603" - - @compileTimeOnly("`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.") - def wrap_\u2603\u2603[T](in: Any): T = sys.error("This method is an implementation detail and should not be referenced.") - - def wrapKey[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = wrapImpl[T,InputWrapper.type](c, InputWrapper, WrapName)(ts, pos) - - /** Wraps an arbitrary Tree in a call to the `.` method of this module for later processing by an enclosing macro. - * The resulting Tree is the manually constructed version of: - * - * `c.universe.reify { .[T](ts.splice) }` - */ - def wrapImpl[T: c.WeakTypeTag, S <: AnyRef with Singleton](c: Context, s: S, wrapName: String)(ts: c.Expr[Any], pos: c.Position)(implicit it: c.TypeTag[s.type]): c.Expr[T] = - { - import c.universe.{Apply=>ApplyTree,_} - val util = new ContextUtil[c.type](c) - val iw = util.singleton(s) - val tpe = c.weakTypeOf[T] - val nme = newTermName(wrapName).encoded - val sel = util.select(Ident(iw), nme) - sel.setPos(pos) // need to set the position on Select, because that is where the compileTimeOnly check looks - val tree = ApplyTree(TypeApply(sel, TypeTree(tpe) :: Nil), ts.tree :: Nil) - tree.setPos(ts.tree.pos) - c.Expr[T](tree) - } -} - object Instance { final val ApplyName = "app" @@ -71,11 +35,17 @@ object Instance final val InstanceTCName = "M" final class Input[U <: Universe with Singleton](val tpe: U#Type, val expr: U#Tree, val local: U#ValDef) + trait Transform[C <: Context with Singleton, N[_]] { + def apply(in: C#Tree): C#Tree + } + def idTransform[C <: Context with Singleton]: Transform[C,Id] = new Transform[C,Id] { + def apply(in: C#Tree): C#Tree = in + } /** Implementation of a macro that provides a direct syntax for applicative functors and monads. * It is intended to be used in conjunction with another macro that conditions the inputs. * - * This method processes the Tree `t` to find inputs of the form `InputWrapper.wrap[T]( input )` + * This method processes the Tree `t` to find inputs of the form `wrap[T]( input )` * This form is typically constructed by another macro that pretends to be able to get a value of type `T` * from a value convertible to `M[T]`. This `wrap(input)` form has two main purposes. * First, it identifies the inputs that should be transformed. @@ -85,7 +55,7 @@ object Instance * allowing the original `Tree` and `Type` to be hidden behind the raw `T` type. This method will remove the call to `wrap` * so that it is not actually called at runtime. * - * Each `input` in each expression of the form `InputWrapper.wrap[T]( input )` is transformed by `convert`. + * Each `input` in each expression of the form `wrap[T]( input )` is transformed by `convert`. * This transformation converts the input Tree to a Tree of type `M[T]`. * The original wrapped expression `wrap(input)` is replaced by a reference to a new local `val $x: T`, where `$x` is a fresh name. * These converted inputs are passed to `builder` as well as the list of these synthetic `ValDef`s. @@ -107,18 +77,18 @@ object Instance * If this is for multi-input flatMap (app followed by flatMap), * this should be the argument wrapped in Right. */ - def contImpl[T](c: Context, i: Instance with Singleton, convert: Convert, builder: TupleBuilder)(t: Either[c.Expr[T], c.Expr[i.M[T]]])( - implicit tt: c.WeakTypeTag[T], it: c.TypeTag[i.type]): c.Expr[i.M[T]] = + def contImpl[T,N[_]](c: 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, tt.tpe :: Nil).normalize + val mttpe: Type = appliedType(mTC, nt.tpe :: Nil).normalize // the tree for the macro argument val (tree, treeType) = t match { - case Left(l) => (l.tree, tt.tpe.normalize) + case Left(l) => (l.tree, nt.tpe.normalize) case Right(r) => (r.tree, mttpe) } @@ -126,7 +96,7 @@ object Instance // A Tree that references the statically accessible Instance that provides the actual implementations of map, flatMap, ... val instance = Ident(instanceSym) - val isWrapper: Tree => Boolean = util.isWrapper(InputWrapper.WrapName) + val isWrapper: (String, Type, Tree) => Boolean = convert.asPredicate(c) type In = Input[c.universe.type] var inputs = List[In]() @@ -194,16 +164,19 @@ object Instance inputs ::= new Input(tpe, qual, vd) util.refVal(vd) } - def sub(tpe: Type, qual: Tree): Tree = + def sub(name: String, tpe: Type, qual: Tree): Converted[c.type] = { - val tag = c.WeakTypeTag(tpe) - addType(tpe, convert(c)(qual)(tag) ) + val tag = c.WeakTypeTag[T](tpe) + convert[T](c)(name, qual)(tag) transform { tree => + addType(tpe, tree) + } } // applies the transformation + val tx = util.transformWrappers(tree, (n,tpe,t) => sub(n,tpe,t)) // resetting attributes must be: a) local b) done here and not wider or else there are obscure errors - val tr = makeApp( c.resetLocalAttrs( util.transformWrappers(tree, isWrapper, (tpe, tr) => sub(tpe, tr)) ) ) - c.Expr[i.M[T]](tr) + val tr = makeApp( c.resetLocalAttrs( inner(tx) ) ) + c.Expr[i.M[N[T]]](tr) } import Types._ From 9b1cdb7534403bb6a1e2cb591bc17757120539d7 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Wed, 19 Jun 2013 11:53:11 -0400 Subject: [PATCH 14/34] set position on parameter references in task/setting macros --- util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala | 3 ++- util/appmacro/src/main/scala/sbt/appmacro/Instance.scala | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index 48dd32466..dffc5e0c6 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -144,11 +144,12 @@ final class ContextUtil[C <: Context](val ctx: C) } /** Create a Tree that references the `val` represented by `vd`. */ - def refVal(vd: ValDef): Tree = + def refVal(vd: ValDef, pos: Position): Tree = { val t = Ident(vd.name) assert(vd.tpt.tpe != null, "val type is null: " + vd + ", tpt: " + vd.tpt.tpe) t.setType(vd.tpt.tpe) + t.setPos(pos) t } diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala index 3e8b45cf0..5928df8bc 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala @@ -162,7 +162,7 @@ object Instance qual.foreach(checkQual) val vd = util.freshValDef(tpe, qual.symbol) inputs ::= new Input(tpe, qual, vd) - util.refVal(vd) + util.refVal(vd, qual.pos) } def sub(name: String, tpe: Type, qual: Tree): Converted[c.type] = { From ca0d2b05cc38a22c7f302dc3a1164637dcad3719 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Thu, 18 Jul 2013 22:38:16 -0400 Subject: [PATCH 15/34] specify explicit type to work around 2.11 volatile override error --- util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala index 195123c6c..c22825c1b 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala @@ -60,7 +60,7 @@ object KListBuilder extends TupleBuilder val representationC = PolyType(tcVariable :: Nil, klistType) val resultType = appliedType(representationC, idTC :: Nil) val input = klist - val alistInstance = TypeApply(select(Ident(alist), "klist"), TypeTree(representationC) :: Nil) + val alistInstance: ctx.universe.Tree = TypeApply(select(Ident(alist), "klist"), TypeTree(representationC) :: Nil) def extract(param: ValDef) = bindKList(param, Nil, inputs.map(_.local)) } } \ No newline at end of file From 4d7dccb02eca08111b11dd687b70b38db7276eac Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Fri, 22 Nov 2013 13:08:10 -0500 Subject: [PATCH 16/34] Remove the need for resetLocalAttrs. Fixes #994, #952. The fix was made possible by the very helpful information provided by @retronym. This commit does two key things: 1. changes the owner when splicing original trees into new trees 2. ensures the synthetic trees that get spliced into original trees do not need typechecking Given this original source (from Defaults.scala): ... lazy val sourceConfigPaths = Seq( ... unmanagedSourceDirectories := Seq(scalaSource.value, javaSource.value), ... ) ... After expansion of .value, this looks something like: unmanagedSourceDirectories := Seq( InputWrapper.wrapInit[File](scalaSource), InputWrapper.wrapInit[File](javaSource) ) where wrapInit is something like: def wrapInit[T](a: Any): T After expansion of := we have (approximately): unmanagedSourceDirectories <<= Instance.app( (scalaSource, javaSource) ) { $p1: (File, File) => val $q4: File = $p1._1 val $q3: File = $p1._2 Seq($q3, $q4) } So, a) `scalaSource` and `javaSource` are user trees that are spliced into a tuple constructor after being temporarily held in `InputWrapper.wrapInit` b) the constructed tuple `(scalaSource, javaSource)` is passed as an argument to another method call (without going through a val or anything) and shouldn't need owner changing c) the synthetic vals $q3 and $q4 need their owner properly set to the anonymous function d) the references (Idents) $q3 and $q4 are spliced into the user tree `Seq(..., ...)` and their symbols need to be the Symbol for the referenced vals e) generally, treeCopy needs to be used when substituting Trees in order to preserve attributes, like Types and Positions changeOwner is called on the body `Seq($q3, $q4)` with the original owner sourceConfigPaths to be changed to the new anonymous function. In this example, no owners are actually changed, but when the body contains vals or anonymous functions, they will. An example of the compiler crash seen when the symbol of the references is not that of the vals: symbol value $q3 does not exist in sbt.Defaults.sourceConfigPaths$lzycompute at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:49) at scala.tools.nsc.Global.abort(Global.scala:254) at scala.tools.nsc.backend.icode.GenICode$ICodePhase.genLoadIdent$1(GenICode.scala:1038) at scala.tools.nsc.backend.icode.GenICode$ICodePhase.scala$tools$nsc$backend$icode$GenICode$ICodePhase$$genLoad(GenICode.scala:1044) at scala.tools.nsc.backend.icode.GenICode$ICodePhase$$anonfun$genLoadArguments$1.apply(GenICode.scala:1246) at scala.tools.nsc.backend.icode.GenICode$ICodePhase$$anonfun$genLoadArguments$1.apply(GenICode.scala:1244) ... Other problems with the synthetic tree when it is spliced under the original tree often result in type mismatches or some other compiler error that doesn't result in a crash. If the owner is not changed correctly on the original tree that gets spliced under a synthetic tree, one way it can crash the compiler is: java.lang.IllegalArgumentException: Could not find proxy for val $q23: java.io.File in List(value $q23, method apply, anonymous class $anonfun$globalCore$5, value globalCore, object Defaults, package sbt, package ) (currentOwner= value dir ) ... while compiling: /home/mark/code/sbt/main/src/main/scala/sbt/Defaults.scala during phase: global=lambdalift, atPhase=constructors ... last tree to typer: term $outer symbol: value $outer (flags: private[this]) symbol definition: private[this] val $outer: sbt.BuildCommon tpe: symbol owners: value $outer -> anonymous class $anonfun$87 -> value x$298 -> method derive -> class BuildCommon$class -> package sbt context owners: value dir -> value globalCore -> object Defaults -> package sbt ... The problem here is the difference between context owners and the proxy search chain. --- .../main/scala/sbt/appmacro/ContextUtil.scala | 79 +++++++++++++------ .../main/scala/sbt/appmacro/Instance.scala | 53 +++++++------ .../scala/sbt/appmacro/KListBuilder.scala | 8 +- .../scala/sbt/appmacro/TupleNBuilder.scala | 8 +- 4 files changed, 93 insertions(+), 55 deletions(-) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index dffc5e0c6..381674e47 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -35,10 +35,15 @@ object ContextUtil { /** Utility methods for macros. Several methods assume that the context's universe is a full compiler (`scala.tools.nsc.Global`). * This is not thread safe due to the underlying Context and related data structures not being thread safe. * Use `ContextUtil[c.type](c)` to construct. */ -final class ContextUtil[C <: Context](val ctx: C) +final class ContextUtil[C <: Context](val ctx: C) { import ctx.universe.{Apply=>ApplyTree,_} + val powerContext = ctx.asInstanceOf[reflect.macros.runtime.Context] + val global: powerContext.universe.type = powerContext.universe + def callsiteTyper: global.analyzer.Typer = powerContext.callsiteTyper + val initialOwner: Symbol = callsiteTyper.context.owner.asInstanceOf[ctx.universe.Symbol] + lazy val alistType = ctx.typeOf[AList[KList]] lazy val alist: Symbol = alistType.typeSymbol.companionSymbol lazy val alistTC: Type = alistType.typeConstructor @@ -52,12 +57,15 @@ final class ContextUtil[C <: Context](val ctx: C) * (The current implementation uses Context.fresh, which increments*/ def freshTermName(prefix: String) = newTermName(ctx.fresh("$" + prefix)) - /** Constructs a new, local ValDef with the given Type, a unique name, - * the same position as `sym`, and an empty implementation (no rhs). */ - def freshValDef(tpe: Type, sym: Symbol): ValDef = + /** 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 vd = localValDef(TypeTree(tpe), EmptyTree) - vd setPos getPos(sym) + val SYNTHETIC = (1 << 21).toLong.asInstanceOf[FlagSet] + val sym = owner.newTermSymbol(freshTermName("q"), pos, SYNTHETIC) + setInfo(sym, tpe) + val vd = ValDef(sym, EmptyTree) + vd.setPos(pos) vd } @@ -65,7 +73,7 @@ final class ContextUtil[C <: 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] = + 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`. @@ -106,17 +114,17 @@ final class ContextUtil[C <: Context](val ctx: C) /** Constructs a tuple value of the right TupleN type from the provided inputs.*/ def mkTuple(args: List[Tree]): Tree = - { - val global: Global = ctx.universe.asInstanceOf[Global] global.gen.mkTuple(args.asInstanceOf[List[global.Tree]]).asInstanceOf[ctx.universe.Tree] - } + + def setSymbol[Tree](t: Tree, sym: Symbol): Unit = + t.asInstanceOf[global.Tree].setSymbol(sym.asInstanceOf[global.Symbol]) + def setInfo[Tree](sym: Symbol, tpe: Type): Unit = + sym.asInstanceOf[global.Symbol].setInfo(tpe.asInstanceOf[global.Type]) /** Creates a new, synthetic type variable with the specified `owner`. */ def newTypeVariable(owner: Symbol, prefix: String = "T0"): TypeSymbol = - { - val global: Global = ctx.universe.asInstanceOf[Global] owner.asInstanceOf[global.Symbol].newSyntheticTypeParam(prefix, 0L).asInstanceOf[ctx.universe.TypeSymbol] - } + /** The type representing the type constructor `[X] X` */ lazy val idTC: Type = { @@ -136,21 +144,42 @@ final class ContextUtil[C <: Context](val ctx: C) /** >: Nothing <: Any */ def emptyTypeBounds: TypeBounds = 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] + def functionType(args: List[Type], result: Type): Type = { - val global: Global = ctx.universe.asInstanceOf[Global] 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`. */ - def refVal(vd: ValDef, pos: Position): Tree = + /** 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 = { - val t = Ident(vd.name) - assert(vd.tpt.tpe != null, "val type is null: " + vd + ", tpt: " + vd.tpt.tpe) - t.setType(vd.tpt.tpe) - t.setPos(pos) - t + 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]) + + // 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) + { + override def traverse(tree: global.Tree) { + tree match { + case _: global.DefTree => change(tree.symbol.moduleClass) + case _ => + } + super.traverse(tree) + } } /** Returns the Symbol that references the statically accessible singleton `i`. */ @@ -164,7 +193,6 @@ final class ContextUtil[C <: Context](val ctx: C) /** Returns the symbol for the non-private method named `name` for the class/module `obj`. */ def method(obj: Symbol, name: String): Symbol = { - val global: Global = ctx.universe.asInstanceOf[Global] val ts: Type = obj.typeSignature val m: global.Symbol = ts.asInstanceOf[global.Type].nonPrivateMember(global.newTermName(name)) m.asInstanceOf[Symbol] @@ -176,7 +204,6 @@ final class ContextUtil[C <: Context](val ctx: C) **/ def extractTC(tcp: AnyRef with Singleton, name: String)(implicit it: ctx.TypeTag[tcp.type]): ctx.Type = { - val global: Global = ctx.universe.asInstanceOf[Global] val itTpe = it.tpe.asInstanceOf[global.Type] val m = itTpe.nonPrivateMember(global.newTypeName(name)) val tc = itTpe.memberInfo(m).asInstanceOf[ctx.universe.Type] @@ -187,8 +214,8 @@ final class ContextUtil[C <: Context](val ctx: C) /** Substitutes wrappers in tree `t` with the result of `subWrapper`. * A wrapper is a Tree of the form `f[T](v)` for which isWrapper(, , .target) returns true. * Typically, `f` is a `Select` or `Ident`. - * The wrapper is replaced with the result of `subWrapper(, )` */ - def transformWrappers(t: Tree, subWrapper: (String, Type, Tree) => Converted[ctx.type]): Tree = + * 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 @@ -197,7 +224,7 @@ final class ContextUtil[C <: Context](val ctx: C) override def transform(tree: Tree): Tree = tree match { - case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) => subWrapper(nme.decoded, targ.tpe, qual) match { + case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) => subWrapper(nme.decoded, targ.tpe, qual, tree) match { case Converted.Success(t, finalTx) => finalTx(t) case Converted.Failure(p,m) => ctx.abort(p, m) case _: Converted.NotApplicable[_] => super.transform(tree) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala index 5928df8bc..0de166b67 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala @@ -61,7 +61,7 @@ object Instance * These converted inputs are passed to `builder` as well as the list of these synthetic `ValDef`s. * The `TupleBuilder` instance constructs a tuple (Tree) from the inputs and defines the right hand side of the vals * that unpacks the tuple containing the results of the inputs. - * + * * The constructed tuple of inputs and the code that unpacks the results of the inputs are then passed to the `i`, * which is an implementation of `Instance` that is statically accessible. * An Instance defines a applicative functor associated with a specific type constructor and, if it implements MonadInstance as well, a monad. @@ -70,18 +70,18 @@ object Instance * while the full check for static accessibility is done at macro expansion time. * Note: Ideally, the types would verify that `i: MonadInstance` when `t.isRight`. * With the various dependent types involved, this is not worth it. - * + * * The `t` argument is the argument of the macro that will be transformed as described above. * If the macro that calls this method is for a multi-input map (app followed by map), * `t` should be the argument wrapped in Left. - * If this is for multi-input flatMap (app followed by flatMap), + * If this is for multi-input flatMap (app followed by flatMap), * this should be the argument wrapped in Right. */ def contImpl[T,N[_]](c: 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).normalize @@ -91,19 +91,24 @@ object Instance case Left(l) => (l.tree, nt.tpe.normalize) 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, ... + // 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]() - // Local definitions in the macro. This is used to ensure references are to M instances defined outside of the macro call. - val defs = util.collectDefs(tree, isWrapper) - val checkQual: Tree => Unit = util.checkReferences(defs, isWrapper) // transforms the original tree into calls to the Instance functions pure, map, ..., // resulting in a value of type M[T] @@ -118,7 +123,8 @@ object Instance def pure(body: Tree): Tree = { val typeApplied = TypeApply(util.select(instance, PureName), TypeTree(treeType) :: Nil) - val p = ApplyTree(typeApplied, Function(Nil, body) :: 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]] @@ -133,9 +139,10 @@ object Instance def single(body: Tree, input: In): Tree = { val variable = input.local - val param = ValDef(util.parameterModifiers, variable.name, variable.tpt, EmptyTree) + 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 mapped = ApplyTree(typeApplied, input.expr :: Function(param :: Nil, body) :: Nil) + val f = util.createFunction(param :: Nil, body, functionSym) + val mapped = ApplyTree(typeApplied, input.expr :: f :: Nil) if(t.isLeft) mapped else flatten(mapped) } @@ -145,37 +152,37 @@ object Instance val result = builder.make(c)(mTC, inputs) val param = util.freshMethodParameter( appliedType(result.representationC, util.idTC :: Nil) ) val bindings = result.extract(param) - val f = Function(param :: Nil, Block(bindings, body)) + 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, + // 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): Tree = + // 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.symbol) + val vd = util.freshValDef(tpe, qual.symbol.pos, functionSym) inputs ::= new Input(tpe, qual, vd) - util.refVal(vd, qual.pos) + util.refVal(selection, vd) } - def sub(name: String, tpe: Type, qual: Tree): Converted[c.type] = + 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) + addType(tpe, tree, replace) } } // applies the transformation - val tx = util.transformWrappers(tree, (n,tpe,t) => sub(n,tpe,t)) + 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( c.resetLocalAttrs( inner(tx) ) ) + val tr = makeApp( inner(tx) ) c.Expr[i.M[N[T]]](tr) } diff --git a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala index c22825c1b..e9fb207d8 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala @@ -33,8 +33,10 @@ object KListBuilder extends TupleBuilder def bindKList(prev: ValDef, revBindings: List[ValDef], params: List[ValDef]): List[ValDef] = params match { - case ValDef(mods, name, tpt, _) :: xs => - val head = ValDef(mods, name, tpt, select(Ident(prev.name), "head")) + 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) @@ -53,7 +55,7 @@ object KListBuilder extends TupleBuilder 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. + * 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) ) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala index 805098353..89fe31792 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala @@ -42,9 +42,11 @@ object TupleNBuilder extends TupleBuilder def bindTuple(param: ValDef, revBindings: List[ValDef], params: List[ValDef], i: Int): List[ValDef] = params match { - case ValDef(mods, name, tpt, _) :: xs => - val x = ValDef(mods, name, tpt, select(Ident(param.name), "_" + i.toString)) - bindTuple(param, x :: revBindings, xs, i+1) + 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 } } From 10ed2290045b3b2beff99bb2c7e0da47f98fc75b Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 12 Feb 2014 12:59:32 +0100 Subject: [PATCH 17/34] SI-8263 Avoid SOE in Symbol#logicallyEnclosingMember under Scala 2.11 Since the fix for SI-2066, Scala 2.11 calls logicallyEnclosingMember on the `x` in the expansion of the task macro: InitializeInstance.app[[T0[x]](T0[java.io.File], T0[java.io.File]), Seq[java.io.File]] This exposed the fact that SBT has created `T0` with `NoSymbol` as the owner. This led to the a SOE. I will also change the compiler to be more tolerant of this, but we can observe good discipline in the macro and pick a sensible owner. --- util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala | 4 ++-- util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala index e9fb207d8..81d3be06f 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala @@ -24,7 +24,7 @@ object KListBuilder extends TupleBuilder val kconsTC: Type = kconsTpe.typeConstructor /** This is the L in the type function [L[x]] ... */ - val tcVariable: TypeSymbol = newTCVariable(NoSymbol) + 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 = @@ -65,4 +65,4 @@ object KListBuilder extends TupleBuilder val alistInstance: ctx.universe.Tree = TypeApply(select(Ident(alist), "klist"), TypeTree(representationC) :: Nil) def extract(param: ValDef) = bindKList(param, Nil, inputs.map(_.local)) } -} \ No newline at end of file +} diff --git a/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala index 89fe31792..871932b20 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala @@ -25,7 +25,7 @@ object TupleNBuilder extends TupleBuilder val ctx: c.type = c val representationC: PolyType = { - val tcVariable: Symbol = newTCVariable(NoSymbol) + val tcVariable: Symbol = newTCVariable(util.initialOwner) val tupleTypeArgs = inputs.map(in => typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type]) val tuple = global.definitions.tupleType(tupleTypeArgs) PolyType(tcVariable :: Nil, tuple.asInstanceOf[Type] ) From b34aa0e563976b0cc084a35f43a69730662a712a Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 15 Feb 2014 12:59:03 +0100 Subject: [PATCH 18/34] using compat._ to plug source compatibility breakages This commit makes the code source compatible across Scala 2.10.3 and https://github.com/scala/scala/pull/3452, which is proposed for inclusion in Scala 2.11.0-RC1. We only strictly need the incremental compiler to build on Scala 2.11, as that is integrated into the IDE. But we gain valuable insight into compiler regressions by building *all* of SBT with 2.11. We only got there recently (the 0.13 branch of SBT now fully cross compiles with 2.10.3 and 2.11.0-SNAPSHOT), and this aims to keep things that way. Once 2.10 support is dropped, SBT macros will be able to exploit the new reflection APIs in 2.11 to avoid the need for casting to compiler internals, which aren't governed by binary compatibility. This has been prototyped by @xeno-by: https://github.com/sbt/sbt/pull/1121 --- util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala | 4 ++++ util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala | 4 ++++ util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index 381674e47..c0c849fab 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -32,12 +32,16 @@ object ContextUtil { def unexpectedTree[C <: Context](tree: C#Tree): Nothing = sys.error("Unexpected macro application tree (" + tree.getClass + "): " + tree) } +// TODO 2.11 Remove this after dropping 2.10.x support. +private object HasCompat { val compat = ??? }; import HasCompat._ + /** Utility methods for macros. Several methods assume that the context's universe is a full compiler (`scala.tools.nsc.Global`). * This is not thread safe due to the underlying Context and related data structures not being thread safe. * Use `ContextUtil[c.type](c)` to construct. */ final class ContextUtil[C <: Context](val ctx: C) { import ctx.universe.{Apply=>ApplyTree,_} + import compat._ val powerContext = ctx.asInstanceOf[reflect.macros.runtime.Context] val global: powerContext.universe.type = powerContext.universe diff --git a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala index 81d3be06f..d9dbebe42 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala @@ -9,11 +9,15 @@ package appmacro /** A `TupleBuilder` that uses a KList as the tuple representation.*/ object KListBuilder extends TupleBuilder { + // TODO 2.11 Remove this after dropping 2.10.x support. + private object HasCompat { val compat = ??? }; import HasCompat._ + def make(c: 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 compat._ import util._ val knilType = c.typeOf[KNil] diff --git a/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala index 871932b20..28fa581a4 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala @@ -14,10 +14,14 @@ object TupleNBuilder extends TupleBuilder final val MaxInputs = 11 final val TupleMethodName = "tuple" + // TODO 2.11 Remove this after dropping 2.10.x support. + private object HasCompat { val compat = ??? }; import HasCompat._ + def make(c: 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.{Apply=>ApplyTree,_} + import compat._ import util._ val global: Global = c.universe.asInstanceOf[Global] From abb38da063361f10f7c3cac0519cddfd8692caf8 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 7 Mar 2014 17:48:31 +0100 Subject: [PATCH 19/34] Fix task macro's handling of Symbol owners in .value The qualifier of the `.value` call may contain `DefTree`s (e.g. vals, defs) or `Function` trees. When we snip them out of the tree and graft them into a new context, we must also call `changeOwner`, so that the symbol owner structure and the tree structure are coherent. Failure to do so resulted in a crash in the compiler backend. Fixes #1150 --- .../main/scala/sbt/appmacro/ContextUtil.scala | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index c0c849fab..389fd33f8 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -226,17 +226,19 @@ final class ContextUtil[C <: Context](val ctx: C) object appTransformer extends Transformer { override def transform(tree: Tree): Tree = - tree match - { - case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) => subWrapper(nme.decoded, targ.tpe, qual, tree) match { - case Converted.Success(t, finalTx) => finalTx(t) - case Converted.Failure(p,m) => ctx.abort(p, m) - case _: Converted.NotApplicable[_] => super.transform(tree) - } + tree match { + case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) => + changeOwner(qual, currentOwner, initialOwner) // Fixes https://github.com/sbt/sbt/issues/1150 + subWrapper(nme.decoded, targ.tpe, qual, tree) match { + case Converted.Success(t, finalTx) => finalTx(t) + case Converted.Failure(p,m) => ctx.abort(p, m) + case _: Converted.NotApplicable[_] => super.transform(tree) + } case _ => super.transform(tree) } } - - appTransformer.transform(t) + appTransformer.atOwner(initialOwner) { + appTransformer.transform(t) + } } -} \ No newline at end of file +} From 84b79f9bc15ebf6b9af47e8ec72b3c33a67df07e Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Fri, 7 Mar 2014 22:25:29 -0500 Subject: [PATCH 20/34] Revert "Fix task macro's handling of Symbol owners in .value" This reverts commit 3017bfcd07466a2c3aa9bc5fe4760929db8ae0ed. This was causing sbt to be unable to compile. Reverting temporarily until we have a shot at a full fix. --- .../main/scala/sbt/appmacro/ContextUtil.scala | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index 389fd33f8..c0c849fab 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -226,19 +226,17 @@ final class ContextUtil[C <: Context](val ctx: C) object appTransformer extends Transformer { override def transform(tree: Tree): Tree = - tree match { - case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) => - changeOwner(qual, currentOwner, initialOwner) // Fixes https://github.com/sbt/sbt/issues/1150 - subWrapper(nme.decoded, targ.tpe, qual, tree) match { - case Converted.Success(t, finalTx) => finalTx(t) - case Converted.Failure(p,m) => ctx.abort(p, m) - case _: Converted.NotApplicable[_] => super.transform(tree) - } + tree match + { + case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) => subWrapper(nme.decoded, targ.tpe, qual, tree) match { + case Converted.Success(t, finalTx) => 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) - } + + appTransformer.transform(t) } -} +} \ No newline at end of file From ef826584c2da9f09ce36739ece9fd27c939604b3 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 7 Mar 2014 17:48:31 +0100 Subject: [PATCH 21/34] Fix task macro's handling of Symbol owners in .value The qualifier of the `.value` call may contain `DefTree`s (e.g. vals, defs) or `Function` trees. When we snip them out of the tree and graft them into a new context, we must also call `changeOwner`, so that the symbol owner structure and the tree structure are coherent. Failure to do so resulted in a crash in the compiler backend. Fixes #1150 --- .../main/scala/sbt/appmacro/ContextUtil.scala | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index c0c849fab..fe1baa696 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -226,17 +226,20 @@ final class ContextUtil[C <: Context](val ctx: C) object appTransformer extends Transformer { override def transform(tree: Tree): Tree = - tree match - { - case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) => subWrapper(nme.decoded, targ.tpe, qual, tree) match { - case Converted.Success(t, finalTx) => finalTx(t) - case Converted.Failure(p,m) => ctx.abort(p, m) - case _: Converted.NotApplicable[_] => super.transform(tree) - } + tree match { + case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) => + subWrapper(nme.decoded, 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.transform(t) + appTransformer.atOwner(initialOwner) { + appTransformer.transform(t) + } } -} \ No newline at end of file +} From f66830eed9a68c50e168c00dbaf2b134c5adf28a Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 7 Feb 2014 18:05:00 +0100 Subject: [PATCH 22/34] Fix NPE in task macro accessing q"{...}".symbol.pos We shouldn't assume that the qualifier of a `Select` is a `SymTree`; it may be a `Block`. One place that happens is after the transformation of named/defaults applications. That causes the reported `NullPointerException'. In any case, using `qual.symbol.pos` sense here; it yields the position of the defintions *referred to* by `qual`, not the position of `qual` itself. Both problems are easily fixed: use `qual.pos` instead. Fixes #1107 --- util/appmacro/src/main/scala/sbt/appmacro/Instance.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala index 0de166b67..043ad8731 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala @@ -167,7 +167,7 @@ object Instance def addType(tpe: Type, qual: Tree, selection: Tree): Tree = { qual.foreach(checkQual) - val vd = util.freshValDef(tpe, qual.symbol.pos, functionSym) + val vd = util.freshValDef(tpe, qual.pos, functionSym) inputs ::= new Input(tpe, qual, vd) util.refVal(selection, vd) } From 9fe27afe37843d9d5bb6642dd4cde177d62a3b3c Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 1 May 2014 12:50:07 -0400 Subject: [PATCH 23/34] added scalariform --- .../main/scala/sbt/appmacro/ContextUtil.scala | 423 +++++++++--------- .../src/main/scala/sbt/appmacro/Convert.scala | 57 ++- .../main/scala/sbt/appmacro/Instance.scala | 370 ++++++++------- .../scala/sbt/appmacro/KListBuilder.scala | 113 +++-- .../scala/sbt/appmacro/MixedBuilder.scala | 23 +- .../scala/sbt/appmacro/TupleBuilder.scala | 81 ++-- .../scala/sbt/appmacro/TupleNBuilder.scala | 91 ++-- 7 files changed, 584 insertions(+), 574 deletions(-) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index fe1baa696..29a962de7 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -1,245 +1,260 @@ package sbt package appmacro - import scala.reflect._ - import macros._ - import scala.tools.nsc.Global - import ContextUtil.{DynamicDependencyError, DynamicReferenceError} +import scala.reflect._ +import macros._ +import scala.tools.nsc.Global +import ContextUtil.{ DynamicDependencyError, DynamicReferenceError } object ContextUtil { - final val DynamicDependencyError = "Illegal dynamic dependency" - final val DynamicReferenceError = "Illegal dynamic reference" + final val DynamicDependencyError = "Illegal dynamic dependency" + final val DynamicReferenceError = "Illegal dynamic reference" - /** Constructs an object with utility methods for operating in the provided macro context `c`. - * Callers should explicitly specify the type parameter as `c.type` in order to preserve the path dependent types. */ - def apply[C <: Context with Singleton](c: C): ContextUtil[C] = new ContextUtil(c) + /** + * Constructs an object with utility methods for operating in the provided macro context `c`. + * Callers should explicitly specify the type parameter as `c.type` in order to preserve the path dependent types. + */ + def apply[C <: Context with Singleton](c: C): ContextUtil[C] = new ContextUtil(c) + /** + * Helper for implementing a no-argument macro that is introduced via an implicit. + * This method removes the implicit conversion and evaluates the function `f` on the target of the conversion. + * + * 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: 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) + } + } - /** Helper for implementing a no-argument macro that is introduced via an implicit. - * This method removes the implicit conversion and evaluates the function `f` on the target of the conversion. - * - * 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: 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 <: Context](tree: C#Tree): Nothing = sys.error("Unexpected macro application tree (" + tree.getClass + "): " + tree) + def unexpectedTree[C <: Context](tree: C#Tree): Nothing = sys.error("Unexpected macro application tree (" + tree.getClass + "): " + tree) } // TODO 2.11 Remove this after dropping 2.10.x support. private object HasCompat { val compat = ??? }; import HasCompat._ -/** Utility methods for macros. Several methods assume that the context's universe is a full compiler (`scala.tools.nsc.Global`). -* This is not thread safe due to the underlying Context and related data structures not being thread safe. -* Use `ContextUtil[c.type](c)` to construct. */ -final class ContextUtil[C <: Context](val ctx: C) -{ - import ctx.universe.{Apply=>ApplyTree,_} - import compat._ +/** + * Utility methods for macros. Several methods assume that the context's universe is a full compiler (`scala.tools.nsc.Global`). + * This is not thread safe due to the underlying Context and related data structures not being thread safe. + * Use `ContextUtil[c.type](c)` to construct. + */ +final class ContextUtil[C <: Context](val ctx: C) { + import ctx.universe.{ Apply => ApplyTree, _ } + import compat._ - val powerContext = ctx.asInstanceOf[reflect.macros.runtime.Context] - val global: powerContext.universe.type = powerContext.universe - def callsiteTyper: global.analyzer.Typer = powerContext.callsiteTyper - val initialOwner: Symbol = callsiteTyper.context.owner.asInstanceOf[ctx.universe.Symbol] + val powerContext = ctx.asInstanceOf[reflect.macros.runtime.Context] + val global: powerContext.universe.type = powerContext.universe + def callsiteTyper: global.analyzer.Typer = powerContext.callsiteTyper + val initialOwner: Symbol = callsiteTyper.context.owner.asInstanceOf[ctx.universe.Symbol] - lazy val alistType = ctx.typeOf[AList[KList]] - lazy val alist: Symbol = alistType.typeSymbol.companionSymbol - lazy val alistTC: Type = alistType.typeConstructor + lazy val alistType = ctx.typeOf[AList[KList]] + lazy val alist: Symbol = alistType.typeSymbol.companionSymbol + lazy val alistTC: Type = alistType.typeConstructor - /** Modifiers for a local val.*/ - lazy val localModifiers = Modifiers(NoFlags) + /** Modifiers for a local val.*/ + lazy val localModifiers = Modifiers(NoFlags) - def getPos(sym: Symbol) = if(sym eq null) NoPosition else sym.pos + def getPos(sym: Symbol) = if (sym eq null) NoPosition else sym.pos - /** Constructs a unique term name with the given prefix within this Context. - * (The current implementation uses Context.fresh, which increments*/ - def freshTermName(prefix: String) = newTermName(ctx.fresh("$" + prefix)) + /** + * Constructs a unique term name with the given prefix within this Context. + * (The current implementation uses Context.fresh, which increments + */ + def freshTermName(prefix: String) = newTermName(ctx.fresh("$" + prefix)) - /** 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 = ValDef(sym, EmptyTree) - vd.setPos(pos) - vd - } + /** + * 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 = ValDef(sym, EmptyTree) + vd.setPos(pos) + vd + } - lazy val parameterModifiers = Modifiers(Flag.PARAM) + lazy val parameterModifiers = Modifiers(Flag.PARAM) - /** 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.decoded, tpe.tpe, qual) => () - case tree => - if(tree.symbol ne null) defs += tree.symbol; - super.traverse(tree) - } - } - process.traverse(tree) - defs - } + /** + * 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.decoded, tpe.tpe, qual) => () + case tree => + if (tree.symbol ne null) defs += tree.symbol; + super.traverse(tree) + } + } + process.traverse(tree) + defs + } - /** A reference is illegal if it is to an M instance defined within the scope of the macro call. - * As an approximation, disallow referenced to any local definitions `defs`. */ - def illegalReference(defs: collection.Set[Symbol], sym: Symbol): Boolean = - sym != null && sym != NoSymbol && defs.contains(sym) + /** + * A reference is illegal if it is to an M instance defined within the scope of the macro call. + * As an approximation, disallow referenced to any local definitions `defs`. + */ + def illegalReference(defs: collection.Set[Symbol], sym: Symbol): Boolean = + sym != null && sym != NoSymbol && defs.contains(sym) - /** 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 = { - case s @ ApplyTree(TypeApply(Select(_, nme), tpe :: Nil), qual :: Nil) => - if(isWrapper(nme.decoded, tpe.tpe, qual)) ctx.error(s.pos, DynamicDependencyError) - case id @ Ident(name) if illegalReference(defs, id.symbol) => ctx.error(id.pos, DynamicReferenceError + ": " + name) - case _ => () - } + /** + * 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 = { + case s @ ApplyTree(TypeApply(Select(_, nme), tpe :: Nil), qual :: Nil) => + if (isWrapper(nme.decoded, tpe.tpe, qual)) ctx.error(s.pos, DynamicDependencyError) + case id @ Ident(name) if illegalReference(defs, id.symbol) => ctx.error(id.pos, DynamicReferenceError + ": " + name) + case _ => () + } - /** Constructs a ValDef with a parameter modifier, a unique name, with the provided Type and with an empty rhs. */ - def freshMethodParameter(tpe: Type): ValDef = - ValDef(parameterModifiers, freshTermName("p"), TypeTree(tpe), EmptyTree) + /** Constructs a ValDef with a parameter modifier, a unique name, with the provided Type and with an empty rhs. */ + def freshMethodParameter(tpe: Type): ValDef = + ValDef(parameterModifiers, freshTermName("p"), TypeTree(tpe), EmptyTree) - /** Constructs a ValDef with local modifiers and a unique name. */ - def localValDef(tpt: Tree, rhs: Tree): ValDef = - ValDef(localModifiers, freshTermName("q"), tpt, rhs) + /** Constructs a ValDef with local modifiers and a unique name. */ + def localValDef(tpt: Tree, rhs: Tree): ValDef = + ValDef(localModifiers, freshTermName("q"), tpt, rhs) - /** Constructs a tuple value of the right TupleN type from the provided inputs.*/ - def mkTuple(args: List[Tree]): Tree = - global.gen.mkTuple(args.asInstanceOf[List[global.Tree]]).asInstanceOf[ctx.universe.Tree] + /** Constructs a tuple value of the right TupleN type from the provided inputs.*/ + def mkTuple(args: List[Tree]): Tree = + global.gen.mkTuple(args.asInstanceOf[List[global.Tree]]).asInstanceOf[ctx.universe.Tree] - def setSymbol[Tree](t: Tree, sym: Symbol): Unit = - t.asInstanceOf[global.Tree].setSymbol(sym.asInstanceOf[global.Symbol]) - def setInfo[Tree](sym: Symbol, tpe: Type): Unit = - sym.asInstanceOf[global.Symbol].setInfo(tpe.asInstanceOf[global.Type]) + def setSymbol[Tree](t: Tree, sym: Symbol): Unit = + t.asInstanceOf[global.Tree].setSymbol(sym.asInstanceOf[global.Symbol]) + def setInfo[Tree](sym: Symbol, tpe: Type): Unit = + sym.asInstanceOf[global.Symbol].setInfo(tpe.asInstanceOf[global.Type]) - /** 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] + /** 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] - /** The type representing the type constructor `[X] X` */ - lazy val idTC: Type = - { - val tvar = newTypeVariable(NoSymbol) - 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.setTypeSignature(PolyType(arg :: Nil, emptyTypeBounds)) - tc - } - /** >: Nothing <: Any */ - def emptyTypeBounds: TypeBounds = TypeBounds(definitions.NothingClass.toType, definitions.AnyClass.toType) + /** The type representing the type constructor `[X] X` */ + lazy val idTC: Type = + { + val tvar = newTypeVariable(NoSymbol) + 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.setTypeSignature(PolyType(arg :: Nil, emptyTypeBounds)) + tc + } + /** >: Nothing <: Any */ + def emptyTypeBounds: TypeBounds = 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] + /** 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] - 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) + /** 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 - } + /** 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 changeOwner(tree: Tree, prev: Symbol, next: Symbol): Unit = - new ChangeOwnerAndModuleClassTraverser(prev.asInstanceOf[global.Symbol], next.asInstanceOf[global.Symbol]).traverse(tree.asInstanceOf[global.Tree]) + def changeOwner(tree: Tree, prev: Symbol, next: Symbol): Unit = + 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) - { - override def traverse(tree: global.Tree) { - tree match { - case _: global.DefTree => change(tree.symbol.moduleClass) - case _ => - } - super.traverse(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) { + override def traverse(tree: global.Tree) { + tree match { + case _: global.DefTree => change(tree.symbol.moduleClass) + case _ => + } + super.traverse(tree) + } + } - /** Returns the Symbol that references the statically accessible singleton `i`. */ - 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 + ").") - } + /** Returns the Symbol that references the statically accessible singleton `i`. */ + 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 + ").") + } - def select(t: Tree, name: String): Tree = Select(t, newTermName(name)) + def select(t: Tree, name: String): Tree = Select(t, newTermName(name)) - /** Returns the symbol for the non-private method named `name` for the class/module `obj`. */ - def method(obj: Symbol, name: String): Symbol = { - val ts: Type = obj.typeSignature - val m: global.Symbol = ts.asInstanceOf[global.Type].nonPrivateMember(global.newTermName(name)) - m.asInstanceOf[Symbol] - } + /** Returns the symbol for the non-private method named `name` for the class/module `obj`. */ + def method(obj: Symbol, name: String): Symbol = { + val ts: Type = obj.typeSignature + val m: global.Symbol = ts.asInstanceOf[global.Type].nonPrivateMember(global.newTermName(name)) + m.asInstanceOf[Symbol] + } - /** Returns a Type representing the type constructor tcp.. For example, given - * `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 - } + /** + * Returns a Type representing the type constructor tcp.. For example, given + * `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 + } - /** Substitutes wrappers in tree `t` with the result of `subWrapper`. - * A wrapper is a Tree of the form `f[T](v)` for which isWrapper(, , .target) returns true. - * 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.decoded, 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) - } - } + /** + * Substitutes wrappers in tree `t` with the result of `subWrapper`. + * A wrapper is a Tree of the form `f[T](v)` for which isWrapper(, , .target) returns true. + * 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.decoded, 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/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala b/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala index 6dedf776b..3a2e562a6 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala @@ -1,38 +1,37 @@ package sbt package appmacro - import scala.reflect._ - import macros._ - import Types.idFun +import scala.reflect._ +import macros._ +import Types.idFun -abstract class Convert -{ - def apply[T: c.WeakTypeTag](c: Context)(nme: String, in: c.Tree): Converted[c.type] - def asPredicate(c: Context): (String, c.Type, c.Tree) => Boolean = - (n,tpe,tree) => { - val tag = c.WeakTypeTag(tpe) - apply(c)(n,tree)(tag).isSuccess - } +abstract class Convert { + def apply[T: c.WeakTypeTag](c: Context)(nme: String, in: c.Tree): Converted[c.type] + def asPredicate(c: Context): (String, c.Type, c.Tree) => Boolean = + (n, tpe, tree) => { + val tag = c.WeakTypeTag(tpe) + apply(c)(n, tree)(tag).isSuccess + } } sealed trait Converted[C <: Context with Singleton] { - def isSuccess: Boolean - def transform(f: C#Tree => C#Tree): Converted[C] + def isSuccess: Boolean + def transform(f: C#Tree => C#Tree): Converted[C] } object Converted { - def NotApplicable[C <: Context with Singleton] = new NotApplicable[C] - final case class Failure[C <: 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) - } - final class NotApplicable[C <: Context with Singleton] extends Converted[C] { - def isSuccess = false - def transform(f: C#Tree => C#Tree): Converted[C] = this - } - final case class Success[C <: 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 <: Context with Singleton](tree: C#Tree): Success[C] = Success(tree, idFun) - } + def NotApplicable[C <: Context with Singleton] = new NotApplicable[C] + final case class Failure[C <: 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) + } + final class NotApplicable[C <: Context with Singleton] extends Converted[C] { + def isSuccess = false + def transform(f: C#Tree => C#Tree): Converted[C] = this + } + final case class Success[C <: 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 <: Context with Singleton](tree: C#Tree): Success[C] = Success(tree, idFun) + } } \ No newline at end of file diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala index 043ad8731..7a63feca5 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala @@ -1,214 +1,210 @@ package sbt package appmacro - import Classes.Applicative - import Types.Id +import Classes.Applicative +import Types.Id -/** The separate hierarchy from Applicative/Monad is for two reasons. -* -* 1. The type constructor is represented as an abstract type because a TypeTag cannot represent a type constructor directly. -* 2. The applicative interface is uncurried. -*/ -trait Instance -{ - type M[x] - def app[K[L[x]], Z](in: K[M], f: K[Id] => Z)(implicit a: AList[K]): M[Z] - def map[S,T](in: M[S], f: S => T): M[T] - def pure[T](t: () => T): M[T] +/** + * The separate hierarchy from Applicative/Monad is for two reasons. + * + * 1. The type constructor is represented as an abstract type because a TypeTag cannot represent a type constructor directly. + * 2. The applicative interface is uncurried. + */ +trait Instance { + type M[x] + def app[K[L[x]], Z](in: K[M], f: K[Id] => Z)(implicit a: AList[K]): M[Z] + def map[S, T](in: M[S], f: S => T): M[T] + def pure[T](t: () => T): M[T] } -trait MonadInstance extends Instance -{ - def flatten[T](in: M[M[T]]): M[T] +trait MonadInstance extends Instance { + def flatten[T](in: M[M[T]]): M[T] } - import scala.reflect._ - import macros._ - import reflect.internal.annotations.compileTimeOnly +import scala.reflect._ +import macros._ +import reflect.internal.annotations.compileTimeOnly -object Instance -{ - final val ApplyName = "app" - final val FlattenName = "flatten" - final val PureName = "pure" - final val MapName = "map" - final val InstanceTCName = "M" +object Instance { + final val ApplyName = "app" + final val FlattenName = "flatten" + final val PureName = "pure" + 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) - trait Transform[C <: Context with Singleton, N[_]] { - def apply(in: C#Tree): C#Tree - } - def idTransform[C <: Context with Singleton]: Transform[C,Id] = new Transform[C,Id] { - def apply(in: C#Tree): C#Tree = in - } + final class Input[U <: Universe with Singleton](val tpe: U#Type, val expr: U#Tree, val local: U#ValDef) + trait Transform[C <: Context with Singleton, N[_]] { + def apply(in: C#Tree): C#Tree + } + def idTransform[C <: Context with Singleton]: Transform[C, Id] = new Transform[C, Id] { + def apply(in: C#Tree): C#Tree = in + } - /** Implementation of a macro that provides a direct syntax for applicative functors and monads. - * It is intended to be used in conjunction with another macro that conditions the inputs. - * - * This method processes the Tree `t` to find inputs of the form `wrap[T]( input )` - * This form is typically constructed by another macro that pretends to be able to get a value of type `T` - * from a value convertible to `M[T]`. This `wrap(input)` form has two main purposes. - * First, it identifies the inputs that should be transformed. - * Second, it allows the input trees to be wrapped for later conversion into the appropriate `M[T]` type by `convert`. - * This wrapping is necessary because applying the first macro must preserve the original type, - * but it is useful to delay conversion until the outer, second macro is called. The `wrap` method accomplishes this by - * allowing the original `Tree` and `Type` to be hidden behind the raw `T` type. This method will remove the call to `wrap` - * so that it is not actually called at runtime. - * - * Each `input` in each expression of the form `wrap[T]( input )` is transformed by `convert`. - * This transformation converts the input Tree to a Tree of type `M[T]`. - * The original wrapped expression `wrap(input)` is replaced by a reference to a new local `val $x: T`, where `$x` is a fresh name. - * These converted inputs are passed to `builder` as well as the list of these synthetic `ValDef`s. - * The `TupleBuilder` instance constructs a tuple (Tree) from the inputs and defines the right hand side of the vals - * that unpacks the tuple containing the results of the inputs. - * - * The constructed tuple of inputs and the code that unpacks the results of the inputs are then passed to the `i`, - * which is an implementation of `Instance` that is statically accessible. - * An Instance defines a applicative functor associated with a specific type constructor and, if it implements MonadInstance as well, a monad. - * Typically, it will be either a top-level module or a stable member of a top-level module (such as a val or a nested module). - * The `with Singleton` part of the type verifies some cases at macro compilation time, - * while the full check for static accessibility is done at macro expansion time. - * Note: Ideally, the types would verify that `i: MonadInstance` when `t.isRight`. - * With the various dependent types involved, this is not worth it. - * - * The `t` argument is the argument of the macro that will be transformed as described above. - * If the macro that calls this method is for a multi-input map (app followed by map), - * `t` should be the argument wrapped in Left. - * If this is for multi-input flatMap (app followed by flatMap), - * this should be the argument wrapped in Right. - */ - def contImpl[T,N[_]](c: 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,_} + /** + * Implementation of a macro that provides a direct syntax for applicative functors and monads. + * It is intended to be used in conjunction with another macro that conditions the inputs. + * + * This method processes the Tree `t` to find inputs of the form `wrap[T]( input )` + * This form is typically constructed by another macro that pretends to be able to get a value of type `T` + * from a value convertible to `M[T]`. This `wrap(input)` form has two main purposes. + * First, it identifies the inputs that should be transformed. + * Second, it allows the input trees to be wrapped for later conversion into the appropriate `M[T]` type by `convert`. + * This wrapping is necessary because applying the first macro must preserve the original type, + * but it is useful to delay conversion until the outer, second macro is called. The `wrap` method accomplishes this by + * allowing the original `Tree` and `Type` to be hidden behind the raw `T` type. This method will remove the call to `wrap` + * so that it is not actually called at runtime. + * + * Each `input` in each expression of the form `wrap[T]( input )` is transformed by `convert`. + * This transformation converts the input Tree to a Tree of type `M[T]`. + * The original wrapped expression `wrap(input)` is replaced by a reference to a new local `val $x: T`, where `$x` is a fresh name. + * These converted inputs are passed to `builder` as well as the list of these synthetic `ValDef`s. + * The `TupleBuilder` instance constructs a tuple (Tree) from the inputs and defines the right hand side of the vals + * that unpacks the tuple containing the results of the inputs. + * + * The constructed tuple of inputs and the code that unpacks the results of the inputs are then passed to the `i`, + * which is an implementation of `Instance` that is statically accessible. + * An Instance defines a applicative functor associated with a specific type constructor and, if it implements MonadInstance as well, a monad. + * Typically, it will be either a top-level module or a stable member of a top-level module (such as a val or a nested module). + * The `with Singleton` part of the type verifies some cases at macro compilation time, + * while the full check for static accessibility is done at macro expansion time. + * Note: Ideally, the types would verify that `i: MonadInstance` when `t.isRight`. + * With the various dependent types involved, this is not worth it. + * + * The `t` argument is the argument of the macro that will be transformed as described above. + * If the macro that calls this method is for a multi-input map (app followed by map), + * `t` should be the argument wrapped in Left. + * If this is for multi-input flatMap (app followed by flatMap), + * this should be the argument wrapped in Right. + */ + def contImpl[T, N[_]](c: 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).normalize + val util = ContextUtil[c.type](c) + val mTC: Type = util.extractTC(i, InstanceTCName) + val mttpe: Type = appliedType(mTC, nt.tpe :: Nil).normalize - // the tree for the macro argument - val (tree, treeType) = t match { - case Left(l) => (l.tree, nt.tpe.normalize) - 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) + // the tree for the macro argument + val (tree, treeType) = t match { + case Left(l) => (l.tree, nt.tpe.normalize) + 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 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) + 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) + // 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]() + 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) + } - // 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) + } - // 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.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) + } - // 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) + } + } - // 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) + } - // 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._ - import Types._ + 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) + def pure[S](s: () => S): M[S] = ap.pure(s()) + } - 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) - def pure[S](s: () => S): M[S] = ap.pure(s()) - } - - 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) - // 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)) - } - } + 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) + // 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)) + } + } } diff --git a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala index d9dbebe42..b5c2878f3 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala @@ -1,72 +1,71 @@ package sbt package appmacro - import Types.Id - import scala.tools.nsc.Global - import scala.reflect._ - import macros._ +import Types.Id +import scala.tools.nsc.Global +import scala.reflect._ +import macros._ /** A `TupleBuilder` that uses a KList as the tuple representation.*/ -object KListBuilder extends TupleBuilder -{ - // TODO 2.11 Remove this after dropping 2.10.x support. - private object HasCompat { val compat = ??? }; import HasCompat._ +object KListBuilder extends TupleBuilder { + // TODO 2.11 Remove this after dropping 2.10.x support. + private object HasCompat { val compat = ??? }; import HasCompat._ - def make(c: 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 compat._ - import util._ + def make(c: 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 compat._ + import util._ - val knilType = c.typeOf[KNil] - val knil = Ident(knilType.typeSymbol.companionSymbol) - val kconsTpe = c.typeOf[KCons[Int,KNil,List]] - val kcons = kconsTpe.typeSymbol.companionSymbol - val mTC: Type = mt.asInstanceOf[c.universe.Type] - val kconsTC: Type = kconsTpe.typeConstructor + val knilType = c.typeOf[KNil] + val knil = Ident(knilType.typeSymbol.companionSymbol) + val kconsTpe = c.typeOf[KCons[Int, KNil, List]] + val kcons = kconsTpe.typeSymbol.companionSymbol + 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 = PolyType(tcVariable :: Nil, klistType) - val resultType = appliedType(representationC, idTC :: Nil) - 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 = PolyType(tcVariable :: Nil, klistType) + val resultType = appliedType(representationC, idTC :: Nil) + 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/util/appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala index e58adb2b0..019dc8b20 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala @@ -1,16 +1,17 @@ package sbt package appmacro - import scala.reflect._ - import macros._ +import scala.reflect._ +import macros._ -/** A builder that uses `TupleN` as the representation for small numbers of inputs (up to `TupleNBuilder.MaxInputs`) -* and `KList` for larger numbers of inputs. This builder cannot handle fewer than 2 inputs.*/ -object MixedBuilder extends TupleBuilder -{ - def make(c: 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) - } +/** + * A builder that uses `TupleN` as the representation for small numbers of inputs (up to `TupleNBuilder.MaxInputs`) + * and `KList` for larger numbers of inputs. This builder cannot handle fewer than 2 inputs. + */ +object MixedBuilder extends TupleBuilder { + def make(c: 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) + } } \ No newline at end of file diff --git a/util/appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala index f6442cb02..a6ea2d84c 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala @@ -1,56 +1,57 @@ package sbt package appmacro - import Types.Id - import scala.tools.nsc.Global - import scala.reflect._ - import macros._ +import Types.Id +import scala.tools.nsc.Global +import scala.reflect._ +import macros._ -/** -* A `TupleBuilder` abstracts the work of constructing a tuple data structure such as a `TupleN` or `KList` -* and extracting values from it. The `Instance` macro implementation will (roughly) traverse the tree of its argument -* and ultimately obtain a list of expressions with type `M[T]` for different types `T`. -* The macro constructs an `Input` value for each of these expressions that contains the `Type` for `T`, -* the `Tree` for the expression, and a `ValDef` that will hold the value for the input. -* -* `TupleBuilder.apply` is provided with the list of `Input`s and is expected to provide three values in the returned BuilderResult. -* First, it returns the constructed tuple data structure Tree in `input`. -* Next, it provides the type constructor `representationC` that, when applied to M, gives the type of tuple data structure. -* For example, a builder that constructs a `Tuple3` for inputs `M[Int]`, `M[Boolean]`, and `M[String]` -* would provide a Type representing `[L[x]] (L[Int], L[Boolean], L[String])`. The `input` method -* would return a value whose type is that type constructor applied to M, or `(M[Int], M[Boolean], M[String])`. -* -* Finally, the `extract` method provides a list of vals that extract information from the applied input. -* The type of the applied input is the type constructor applied to `Id` (`[X] X`). -* The returned list of ValDefs should be the ValDefs from `inputs`, but with non-empty right-hand sides. -*/ +/** + * A `TupleBuilder` abstracts the work of constructing a tuple data structure such as a `TupleN` or `KList` + * and extracting values from it. The `Instance` macro implementation will (roughly) traverse the tree of its argument + * and ultimately obtain a list of expressions with type `M[T]` for different types `T`. + * The macro constructs an `Input` value for each of these expressions that contains the `Type` for `T`, + * the `Tree` for the expression, and a `ValDef` that will hold the value for the input. + * + * `TupleBuilder.apply` is provided with the list of `Input`s and is expected to provide three values in the returned BuilderResult. + * First, it returns the constructed tuple data structure Tree in `input`. + * Next, it provides the type constructor `representationC` that, when applied to M, gives the type of tuple data structure. + * For example, a builder that constructs a `Tuple3` for inputs `M[Int]`, `M[Boolean]`, and `M[String]` + * would provide a Type representing `[L[x]] (L[Int], L[Boolean], L[String])`. The `input` method + * would return a value whose type is that type constructor applied to M, or `(M[Int], M[Boolean], M[String])`. + * + * Finally, the `extract` method provides a list of vals that extract information from the applied input. + * The type of the applied input is the type constructor applied to `Id` (`[X] X`). + * 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]] + /** 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: Context)(tcType: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] + /** Constructs a one-time use Builder for Context `c` and type constructor `tcType`. */ + def make(c: Context)(tcType: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] } -trait BuilderResult[C <: Context with Singleton] -{ - val ctx: C - import ctx.universe._ +trait BuilderResult[C <: Context with Singleton] { + val ctx: C + import ctx.universe._ - /** Represents the higher-order type constructor `[L[x]] ...` where `...` is the - * type of the data structure containing the added expressions, - * except that it is abstracted over the type constructor applied to each heterogeneous part of the type . */ - def representationC: PolyType + /** + * Represents the higher-order type constructor `[L[x]] ...` where `...` is the + * type of the data structure containing the added expressions, + * except that it is abstracted over the type constructor applied to each heterogeneous part of the type . + */ + def representationC: PolyType - /** The instance of AList for the input. For a `representationC` of `[L[x]]`, this `Tree` should have a `Type` of `AList[L]`*/ - def alistInstance: Tree + /** The instance of AList for the input. For a `representationC` of `[L[x]]`, this `Tree` should have a `Type` of `AList[L]`*/ + def alistInstance: Tree - /** Returns the completed value containing all expressions added to the builder. */ - def input: Tree + /** Returns the completed value containing all expressions added to the builder. */ + def input: Tree - /* The list of definitions that extract values from a value of type `$representationC[Id]`. + /* The list of definitions that extract values from a value of type `$representationC[Id]`. * The returned value should be identical to the `ValDef`s provided to the `TupleBuilder.make` method but with * non-empty right hand sides. Each `ValDef` may refer to `param` and previous `ValDef`s in the list.*/ - def extract(param: ValDef): List[ValDef] + def extract(param: ValDef): List[ValDef] } diff --git a/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala b/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala index 28fa581a4..232174c81 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala @@ -1,57 +1,56 @@ package sbt package appmacro - import Types.Id - import scala.tools.nsc.Global - import scala.reflect._ - import macros._ +import Types.Id +import scala.tools.nsc.Global +import scala.reflect._ +import macros._ -/** A builder that uses a TupleN as the tuple representation. -* 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" +/** + * A builder that uses a TupleN as the tuple representation. + * 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" - // TODO 2.11 Remove this after dropping 2.10.x support. - private object HasCompat { val compat = ??? }; import HasCompat._ + // TODO 2.11 Remove this after dropping 2.10.x support. + private object HasCompat { val compat = ??? }; import HasCompat._ - def make(c: 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.{Apply=>ApplyTree,_} - import compat._ - import util._ + def make(c: 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.{ Apply => ApplyTree, _ } + import compat._ + import util._ - val global: Global = c.universe.asInstanceOf[Global] - val mTC: Type = mt.asInstanceOf[c.universe.Type] + val global: Global = c.universe.asInstanceOf[Global] + val mTC: Type = mt.asInstanceOf[c.universe.Type] - val ctx: c.type = c - val representationC: PolyType = { - val tcVariable: Symbol = newTCVariable(util.initialOwner) - val tupleTypeArgs = inputs.map(in => typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type]) - val tuple = global.definitions.tupleType(tupleTypeArgs) - PolyType(tcVariable :: Nil, tuple.asInstanceOf[Type] ) - } - val resultType = appliedType(representationC, idTC :: Nil) + val ctx: c.type = c + val representationC: PolyType = { + val tcVariable: Symbol = newTCVariable(util.initialOwner) + val tupleTypeArgs = inputs.map(in => typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type]) + val tuple = global.definitions.tupleType(tupleTypeArgs) + PolyType(tcVariable :: Nil, tuple.asInstanceOf[Type]) + } + val resultType = appliedType(representationC, idTC :: Nil) - 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) + 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 - } - } + 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 + } + } } From f1bc128e5145ca8c38299bca3ae3c8950cef61c1 Mon Sep 17 00:00:00 2001 From: Pierre DAL-PRA Date: Mon, 3 Aug 2015 23:13:59 +0200 Subject: [PATCH 24/34] Replace procedure syntax by explicit Unit annotation --- util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index 29a962de7..a2f1e4e47 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -190,7 +190,7 @@ final class ContextUtil[C <: Context](val ctx: C) { // 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) { - override def traverse(tree: global.Tree) { + override def traverse(tree: global.Tree): Unit = { tree match { case _: global.DefTree => change(tree.symbol.moduleClass) case _ => From 527dd51aa0a68e1c1f002887d0321ba95ce7743c Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 20 Aug 2015 00:43:22 -0400 Subject: [PATCH 25/34] move modules around. --- .../util-appmacro}/src/main/scala/sbt/appmacro/ContextUtil.scala | 0 .../util-appmacro}/src/main/scala/sbt/appmacro/Convert.scala | 0 .../util-appmacro}/src/main/scala/sbt/appmacro/Instance.scala | 0 .../util-appmacro}/src/main/scala/sbt/appmacro/KListBuilder.scala | 0 .../util-appmacro}/src/main/scala/sbt/appmacro/MixedBuilder.scala | 0 .../util-appmacro}/src/main/scala/sbt/appmacro/TupleBuilder.scala | 0 .../src/main/scala/sbt/appmacro/TupleNBuilder.scala | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename {util/appmacro => internal/util-appmacro}/src/main/scala/sbt/appmacro/ContextUtil.scala (100%) rename {util/appmacro => internal/util-appmacro}/src/main/scala/sbt/appmacro/Convert.scala (100%) rename {util/appmacro => internal/util-appmacro}/src/main/scala/sbt/appmacro/Instance.scala (100%) rename {util/appmacro => internal/util-appmacro}/src/main/scala/sbt/appmacro/KListBuilder.scala (100%) rename {util/appmacro => internal/util-appmacro}/src/main/scala/sbt/appmacro/MixedBuilder.scala (100%) rename {util/appmacro => internal/util-appmacro}/src/main/scala/sbt/appmacro/TupleBuilder.scala (100%) rename {util/appmacro => internal/util-appmacro}/src/main/scala/sbt/appmacro/TupleNBuilder.scala (100%) diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/internal/util-appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala similarity index 100% rename from util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala rename to internal/util-appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala b/internal/util-appmacro/src/main/scala/sbt/appmacro/Convert.scala similarity index 100% rename from util/appmacro/src/main/scala/sbt/appmacro/Convert.scala rename to internal/util-appmacro/src/main/scala/sbt/appmacro/Convert.scala diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala b/internal/util-appmacro/src/main/scala/sbt/appmacro/Instance.scala similarity index 100% rename from util/appmacro/src/main/scala/sbt/appmacro/Instance.scala rename to internal/util-appmacro/src/main/scala/sbt/appmacro/Instance.scala diff --git a/util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala similarity index 100% rename from util/appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala rename to internal/util-appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala diff --git a/util/appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala similarity index 100% rename from util/appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala rename to internal/util-appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala diff --git a/util/appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala similarity index 100% rename from util/appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala rename to internal/util-appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala diff --git a/util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala similarity index 100% rename from util/appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala rename to internal/util-appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala From 5a769891d5630ca94f1a96e4b45b0b38450fb873 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Mon, 31 Aug 2015 15:25:10 +0200 Subject: [PATCH 26/34] Add recommended compiler flags, fix most of the warnings --- .../src/main/scala/sbt/appmacro/ContextUtil.scala | 10 +++++++--- .../src/main/scala/sbt/appmacro/KListBuilder.scala | 2 +- .../src/main/scala/sbt/appmacro/TupleNBuilder.scala | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/util-appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/internal/util-appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index a2f1e4e47..1db77f24d 100644 --- a/internal/util-appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/internal/util-appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -36,7 +36,7 @@ object ContextUtil { } // TODO 2.11 Remove this after dropping 2.10.x support. -private object HasCompat { val compat = ??? }; import HasCompat._ +private object HasCompat { val compat = this }; import HasCompat._ /** * Utility methods for macros. Several methods assume that the context's universe is a full compiler (`scala.tools.nsc.Global`). @@ -134,10 +134,14 @@ final class ContextUtil[C <: Context](val ctx: C) { def mkTuple(args: List[Tree]): Tree = global.gen.mkTuple(args.asInstanceOf[List[global.Tree]]).asInstanceOf[ctx.universe.Tree] - def setSymbol[Tree](t: Tree, sym: Symbol): Unit = + def setSymbol[Tree](t: Tree, sym: Symbol): Unit = { t.asInstanceOf[global.Tree].setSymbol(sym.asInstanceOf[global.Symbol]) - def setInfo[Tree](sym: Symbol, tpe: Type): Unit = + () + } + def setInfo[Tree](sym: Symbol, tpe: Type): Unit = { sym.asInstanceOf[global.Symbol].setInfo(tpe.asInstanceOf[global.Type]) + () + } /** Creates a new, synthetic type variable with the specified `owner`. */ def newTypeVariable(owner: Symbol, prefix: String = "T0"): TypeSymbol = diff --git a/internal/util-appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala index b5c2878f3..147f622bf 100644 --- a/internal/util-appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala @@ -9,7 +9,7 @@ import macros._ /** A `TupleBuilder` that uses a KList as the tuple representation.*/ object KListBuilder extends TupleBuilder { // TODO 2.11 Remove this after dropping 2.10.x support. - private object HasCompat { val compat = ??? }; import HasCompat._ + private object HasCompat { val compat = this }; import HasCompat._ def make(c: Context)(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = new BuilderResult[c.type] { val ctx: c.type = c diff --git a/internal/util-appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala index 232174c81..ddef0bee3 100644 --- a/internal/util-appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala @@ -16,7 +16,7 @@ object TupleNBuilder extends TupleBuilder { final val TupleMethodName = "tuple" // TODO 2.11 Remove this after dropping 2.10.x support. - private object HasCompat { val compat = ??? }; import HasCompat._ + private object HasCompat { val compat = this }; import HasCompat._ def make(c: Context)(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = new BuilderResult[c.type] { val util = ContextUtil[c.type](c) From 24694faa31adb302a4d841c7ee2004a73583632e Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 4 Sep 2015 12:54:09 -0400 Subject: [PATCH 27/34] Move util into sbt.util.internal package --- .../scala/sbt/{ => util/internal}/appmacro/ContextUtil.scala | 2 +- .../main/scala/sbt/{ => util/internal}/appmacro/Convert.scala | 2 +- .../main/scala/sbt/{ => util/internal}/appmacro/Instance.scala | 2 +- .../scala/sbt/{ => util/internal}/appmacro/KListBuilder.scala | 2 +- .../scala/sbt/{ => util/internal}/appmacro/MixedBuilder.scala | 2 +- .../scala/sbt/{ => util/internal}/appmacro/TupleBuilder.scala | 2 +- .../scala/sbt/{ => util/internal}/appmacro/TupleNBuilder.scala | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename internal/util-appmacro/src/main/scala/sbt/{ => util/internal}/appmacro/ContextUtil.scala (99%) rename internal/util-appmacro/src/main/scala/sbt/{ => util/internal}/appmacro/Convert.scala (98%) rename internal/util-appmacro/src/main/scala/sbt/{ => util/internal}/appmacro/Instance.scala (99%) rename internal/util-appmacro/src/main/scala/sbt/{ => util/internal}/appmacro/KListBuilder.scala (99%) rename internal/util-appmacro/src/main/scala/sbt/{ => util/internal}/appmacro/MixedBuilder.scala (95%) rename internal/util-appmacro/src/main/scala/sbt/{ => util/internal}/appmacro/TupleBuilder.scala (99%) rename internal/util-appmacro/src/main/scala/sbt/{ => util/internal}/appmacro/TupleNBuilder.scala (98%) diff --git a/internal/util-appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/ContextUtil.scala similarity index 99% rename from internal/util-appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala rename to internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/ContextUtil.scala index 1db77f24d..4bdf92dc7 100644 --- a/internal/util-appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/ContextUtil.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal package appmacro import scala.reflect._ diff --git a/internal/util-appmacro/src/main/scala/sbt/appmacro/Convert.scala b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/Convert.scala similarity index 98% rename from internal/util-appmacro/src/main/scala/sbt/appmacro/Convert.scala rename to internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/Convert.scala index 3a2e562a6..ecfa0ea4f 100644 --- a/internal/util-appmacro/src/main/scala/sbt/appmacro/Convert.scala +++ b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/Convert.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal package appmacro import scala.reflect._ diff --git a/internal/util-appmacro/src/main/scala/sbt/appmacro/Instance.scala b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/Instance.scala similarity index 99% rename from internal/util-appmacro/src/main/scala/sbt/appmacro/Instance.scala rename to internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/Instance.scala index 7a63feca5..111fbc3ca 100644 --- a/internal/util-appmacro/src/main/scala/sbt/appmacro/Instance.scala +++ b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/Instance.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal package appmacro import Classes.Applicative diff --git a/internal/util-appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/KListBuilder.scala similarity index 99% rename from internal/util-appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala rename to internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/KListBuilder.scala index 147f622bf..3f0a12e28 100644 --- a/internal/util-appmacro/src/main/scala/sbt/appmacro/KListBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/KListBuilder.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal package appmacro import Types.Id diff --git a/internal/util-appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/MixedBuilder.scala similarity index 95% rename from internal/util-appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala rename to internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/MixedBuilder.scala index 019dc8b20..c99610275 100644 --- a/internal/util-appmacro/src/main/scala/sbt/appmacro/MixedBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/MixedBuilder.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal package appmacro import scala.reflect._ diff --git a/internal/util-appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/TupleBuilder.scala similarity index 99% rename from internal/util-appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala rename to internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/TupleBuilder.scala index a6ea2d84c..ab87ead81 100644 --- a/internal/util-appmacro/src/main/scala/sbt/appmacro/TupleBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/TupleBuilder.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal package appmacro import Types.Id diff --git a/internal/util-appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/TupleNBuilder.scala similarity index 98% rename from internal/util-appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala rename to internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/TupleNBuilder.scala index ddef0bee3..7c582328e 100644 --- a/internal/util-appmacro/src/main/scala/sbt/appmacro/TupleNBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/TupleNBuilder.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal package appmacro import Types.Id From eb6812943ddfa285d409b88675eb611d7619b2be Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 5 Sep 2015 00:51:58 -0400 Subject: [PATCH 28/34] sbt.util.internal -> sbt.internal.util package --- .../{util/internal => internal/util}/appmacro/ContextUtil.scala | 2 +- .../sbt/{util/internal => internal/util}/appmacro/Convert.scala | 2 +- .../{util/internal => internal/util}/appmacro/Instance.scala | 2 +- .../internal => internal/util}/appmacro/KListBuilder.scala | 2 +- .../internal => internal/util}/appmacro/MixedBuilder.scala | 2 +- .../internal => internal/util}/appmacro/TupleBuilder.scala | 2 +- .../internal => internal/util}/appmacro/TupleNBuilder.scala | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename internal/util-appmacro/src/main/scala/sbt/{util/internal => internal/util}/appmacro/ContextUtil.scala (99%) rename internal/util-appmacro/src/main/scala/sbt/{util/internal => internal/util}/appmacro/Convert.scala (98%) rename internal/util-appmacro/src/main/scala/sbt/{util/internal => internal/util}/appmacro/Instance.scala (99%) rename internal/util-appmacro/src/main/scala/sbt/{util/internal => internal/util}/appmacro/KListBuilder.scala (99%) rename internal/util-appmacro/src/main/scala/sbt/{util/internal => internal/util}/appmacro/MixedBuilder.scala (95%) rename internal/util-appmacro/src/main/scala/sbt/{util/internal => internal/util}/appmacro/TupleBuilder.scala (99%) rename internal/util-appmacro/src/main/scala/sbt/{util/internal => internal/util}/appmacro/TupleNBuilder.scala (98%) diff --git a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/ContextUtil.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala similarity index 99% rename from internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/ContextUtil.scala rename to internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala index 4bdf92dc7..e5cd74270 100644 --- a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/ContextUtil.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package appmacro import scala.reflect._ diff --git a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/Convert.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Convert.scala similarity index 98% rename from internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/Convert.scala rename to internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Convert.scala index ecfa0ea4f..1d0ebede1 100644 --- a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/Convert.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Convert.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package appmacro import scala.reflect._ diff --git a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/Instance.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Instance.scala similarity index 99% rename from internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/Instance.scala rename to internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Instance.scala index 111fbc3ca..2eb6f6877 100644 --- a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/Instance.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Instance.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package appmacro import Classes.Applicative diff --git a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/KListBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala similarity index 99% rename from internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/KListBuilder.scala rename to internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala index 3f0a12e28..5d19f5b6c 100644 --- a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/KListBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package appmacro import Types.Id diff --git a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/MixedBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala similarity index 95% rename from internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/MixedBuilder.scala rename to internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala index c99610275..cc2897ae3 100644 --- a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/MixedBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package appmacro import scala.reflect._ diff --git a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/TupleBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala similarity index 99% rename from internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/TupleBuilder.scala rename to internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala index ab87ead81..7ed352457 100644 --- a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/TupleBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package appmacro import Types.Id diff --git a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/TupleNBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala similarity index 98% rename from internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/TupleNBuilder.scala rename to internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala index 7c582328e..c94a781f0 100644 --- a/internal/util-appmacro/src/main/scala/sbt/util/internal/appmacro/TupleNBuilder.scala +++ b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package appmacro import Types.Id From dada85f3e839d6d68eeaa2a2056b4a38a3fa0cb1 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 16 Sep 2015 22:06:13 -0400 Subject: [PATCH 29/34] New house rules --- .../src/main/scala/sbt/internal/util/appmacro/Instance.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 2eb6f6877..aa8eafe27 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 @@ -77,7 +77,9 @@ object Instance { * this should be the argument wrapped in Right. */ def contImpl[T, N[_]](c: 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]]] = + 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, _ } From 1651df2090b8fb0cd82caec59d046d8a0c26ecf5 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sun, 19 Jun 2016 11:14:12 +0100 Subject: [PATCH 30/34] Fix compilation warnings, migrate to blackbox.Context --- .../internal/util/appmacro/ContextUtil.scala | 44 +++++++++---------- .../sbt/internal/util/appmacro/Convert.scala | 18 ++++---- .../sbt/internal/util/appmacro/Instance.scala | 10 ++--- .../internal/util/appmacro/KListBuilder.scala | 12 ++--- .../internal/util/appmacro/MixedBuilder.scala | 4 +- .../internal/util/appmacro/TupleBuilder.scala | 4 +- .../util/appmacro/TupleNBuilder.scala | 10 ++--- 7 files changed, 46 insertions(+), 56 deletions(-) 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 e5cd74270..ee3b56361 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 @@ -14,7 +14,7 @@ object ContextUtil { * Constructs an object with utility methods for operating in the provided macro context `c`. * Callers should explicitly specify the type parameter as `c.type` in order to preserve the path dependent types. */ - def apply[C <: Context with Singleton](c: C): ContextUtil[C] = new ContextUtil(c) + def apply[C <: blackbox.Context with Singleton](c: C): ContextUtil[C] = new ContextUtil(c) /** * Helper for implementing a no-argument macro that is introduced via an implicit. @@ -23,7 +23,7 @@ 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: Context)(f: (c.Expr[Any], c.Position) => c.Expr[T]): c.Expr[T] = + 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 { @@ -32,20 +32,18 @@ object ContextUtil { } } - def unexpectedTree[C <: 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) } -// TODO 2.11 Remove this after dropping 2.10.x support. -private object HasCompat { val compat = this }; import HasCompat._ - /** - * Utility methods for macros. Several methods assume that the context's universe is a full compiler (`scala.tools.nsc.Global`). + * Utility methods for macros. Several methods assume that the context's universe is a full compiler + * (`scala.tools.nsc.Global`). * This is not thread safe due to the underlying Context and related data structures not being thread safe. * Use `ContextUtil[c.type](c)` to construct. */ -final class ContextUtil[C <: Context](val ctx: C) { +final class ContextUtil[C <: blackbox.Context](val ctx: C) { import ctx.universe.{ Apply => ApplyTree, _ } - import compat._ + import internal.decorators._ val powerContext = ctx.asInstanceOf[reflect.macros.runtime.Context] val global: powerContext.universe.type = powerContext.universe @@ -53,7 +51,7 @@ final class ContextUtil[C <: Context](val ctx: C) { val initialOwner: Symbol = callsiteTyper.context.owner.asInstanceOf[ctx.universe.Symbol] lazy val alistType = ctx.typeOf[AList[KList]] - lazy val alist: Symbol = alistType.typeSymbol.companionSymbol + lazy val alist: Symbol = alistType.typeSymbol.companion lazy val alistTC: Type = alistType.typeConstructor /** Modifiers for a local val.*/ @@ -63,9 +61,9 @@ final class ContextUtil[C <: Context](val ctx: C) { /** * Constructs a unique term name with the given prefix within this Context. - * (The current implementation uses Context.fresh, which increments + * (The current implementation uses Context.freshName, which increments */ - def freshTermName(prefix: String) = newTermName(ctx.fresh("$" + prefix)) + def freshTermName(prefix: String) = TermName(ctx.freshName("$" + prefix)) /** * Constructs a new, synthetic, local ValDef Type `tpe`, a unique name, @@ -76,7 +74,7 @@ final class ContextUtil[C <: Context](val ctx: C) { val SYNTHETIC = (1 << 21).toLong.asInstanceOf[FlagSet] val sym = owner.newTermSymbol(freshTermName("q"), pos, SYNTHETIC) setInfo(sym, tpe) - val vd = ValDef(sym, EmptyTree) + val vd = internal.valDef(sym, EmptyTree) vd.setPos(pos) vd } @@ -94,7 +92,7 @@ final class ContextUtil[C <: Context](val ctx: C) { 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.decoded, tpe.tpe, qual) => () + 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) @@ -117,7 +115,7 @@ final class ContextUtil[C <: Context](val ctx: C) { */ 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.decoded, tpe.tpe, qual)) ctx.error(s.pos, DynamicDependencyError) + 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 _ => () } @@ -134,11 +132,11 @@ final class ContextUtil[C <: Context](val ctx: C) { def mkTuple(args: List[Tree]): Tree = global.gen.mkTuple(args.asInstanceOf[List[global.Tree]]).asInstanceOf[ctx.universe.Tree] - def setSymbol[Tree](t: Tree, sym: Symbol): Unit = { + def setSymbol[_Tree](t: _Tree, sym: Symbol): Unit = { t.asInstanceOf[global.Tree].setSymbol(sym.asInstanceOf[global.Symbol]) () } - def setInfo[Tree](sym: Symbol, tpe: Type): Unit = { + def setInfo(sym: Symbol, tpe: Type): Unit = { sym.asInstanceOf[global.Symbol].setInfo(tpe.asInstanceOf[global.Type]) () } @@ -151,7 +149,7 @@ final class ContextUtil[C <: Context](val ctx: C) { lazy val idTC: Type = { val tvar = newTypeVariable(NoSymbol) - polyType(tvar :: Nil, refVar(tvar)) + internal.polyType(tvar :: Nil, refVar(tvar)) } /** A Type that references the given type variable. */ def refVar(variable: TypeSymbol): Type = variable.toTypeConstructor @@ -159,12 +157,12 @@ final class ContextUtil[C <: Context](val ctx: C) { def newTCVariable(owner: Symbol): TypeSymbol = { val tc = newTypeVariable(owner) - val arg = newTypeVariable(tc, "x") - tc.setTypeSignature(PolyType(arg :: Nil, emptyTypeBounds)) + val arg = newTypeVariable(tc, "x"); + tc.setInfo(internal.polyType(arg :: Nil, emptyTypeBounds)) tc } /** >: Nothing <: Any */ - def emptyTypeBounds: TypeBounds = 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 = @@ -210,7 +208,7 @@ final class ContextUtil[C <: Context](val ctx: C) { case x => sys.error("Instance must be static (was " + x + ").") } - def select(t: Tree, name: String): Tree = Select(t, newTermName(name)) + def select(t: Tree, name: String): Tree = Select(t, TermName(name)) /** Returns the symbol for the non-private method named `name` for the class/module `obj`. */ def method(obj: Symbol, name: String): Symbol = { @@ -247,7 +245,7 @@ final class ContextUtil[C <: Context](val ctx: C) { override def transform(tree: Tree): Tree = tree match { case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) => - subWrapper(nme.decoded, targ.tpe, qual, tree) match { + 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) 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 1d0ebede1..8accb85c6 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 @@ -6,32 +6,32 @@ import macros._ import Types.idFun abstract class Convert { - def apply[T: c.WeakTypeTag](c: Context)(nme: String, in: c.Tree): Converted[c.type] - def asPredicate(c: Context): (String, c.Type, c.Tree) => Boolean = + def apply[T: c.WeakTypeTag](c: blackbox.Context)(nme: String, in: c.Tree): Converted[c.type] + def asPredicate(c: blackbox.Context): (String, c.Type, c.Tree) => Boolean = (n, tpe, tree) => { val tag = c.WeakTypeTag(tpe) apply(c)(n, tree)(tag).isSuccess } } -sealed trait Converted[C <: Context with Singleton] { +sealed trait Converted[C <: blackbox.Context with Singleton] { def isSuccess: Boolean def transform(f: C#Tree => C#Tree): Converted[C] } object Converted { - def NotApplicable[C <: Context with Singleton] = new NotApplicable[C] - final case class Failure[C <: Context with Singleton](position: C#Position, message: String) extends Converted[C] { + 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] { def isSuccess = false def transform(f: C#Tree => C#Tree): Converted[C] = new Failure(position, message) } - final class NotApplicable[C <: Context with Singleton] extends Converted[C] { + final class NotApplicable[C <: blackbox.Context with Singleton] extends Converted[C] { def isSuccess = false def transform(f: C#Tree => C#Tree): Converted[C] = this } - final case class Success[C <: 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 <: 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) } -} \ No newline at end of file +} 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 aa8eafe27..3177d59c4 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 @@ -33,10 +33,10 @@ object Instance { final val InstanceTCName = "M" final class Input[U <: Universe with Singleton](val tpe: U#Type, val expr: U#Tree, val local: U#ValDef) - trait Transform[C <: Context with Singleton, N[_]] { + trait Transform[C <: blackbox.Context with Singleton, N[_]] { def apply(in: C#Tree): C#Tree } - def idTransform[C <: Context with Singleton]: Transform[C, Id] = new Transform[C, Id] { + def idTransform[C <: blackbox.Context with Singleton]: Transform[C, Id] = new Transform[C, Id] { def apply(in: C#Tree): C#Tree = in } @@ -76,7 +76,7 @@ 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: Context, i: Instance with Singleton, convert: Convert, builder: TupleBuilder)(t: Either[c.Expr[T], c.Expr[i.M[T]]], inner: Transform[c.type, N])( + 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]]] = @@ -85,11 +85,11 @@ object Instance { val util = ContextUtil[c.type](c) val mTC: Type = util.extractTC(i, InstanceTCName) - val mttpe: Type = appliedType(mTC, nt.tpe :: Nil).normalize + 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.normalize) + 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 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 5d19f5b6c..cab5058cb 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 @@ -8,20 +8,16 @@ import macros._ /** A `TupleBuilder` that uses a KList as the tuple representation.*/ object KListBuilder extends TupleBuilder { - // TODO 2.11 Remove this after dropping 2.10.x support. - private object HasCompat { val compat = this }; import HasCompat._ - - def make(c: Context)(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = new BuilderResult[c.type] { + 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 compat._ import util._ val knilType = c.typeOf[KNil] - val knil = Ident(knilType.typeSymbol.companionSymbol) + val knil = Ident(knilType.typeSymbol.companion) val kconsTpe = c.typeOf[KCons[Int, KNil, List]] - val kcons = kconsTpe.typeSymbol.companionSymbol + val kcons = kconsTpe.typeSymbol.companion val mTC: Type = mt.asInstanceOf[c.universe.Type] val kconsTC: Type = kconsTpe.typeConstructor @@ -62,7 +58,7 @@ object KListBuilder extends TupleBuilder { */ val klistType: Type = (inputs :\ knilType)((in, klist) => kconsType(in.tpe, klist)) - val representationC = PolyType(tcVariable :: Nil, klistType) + val representationC = internal.polyType(tcVariable :: Nil, klistType) val resultType = appliedType(representationC, idTC :: Nil) val input = klist val alistInstance: ctx.universe.Tree = TypeApply(select(Ident(alist), "klist"), TypeTree(representationC) :: Nil) 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 cc2897ae3..cd77f50ae 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: Context)(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = + 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) } -} \ No newline at end of file +} 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 7ed352457..c36baa78a 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 @@ -29,10 +29,10 @@ trait TupleBuilder { 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: 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 <: Context with Singleton] { +trait BuilderResult[C <: blackbox.Context with Singleton] { val ctx: C import ctx.universe._ 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 c94a781f0..f902db25e 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 @@ -15,13 +15,9 @@ object TupleNBuilder extends TupleBuilder { final val MaxInputs = 11 final val TupleMethodName = "tuple" - // TODO 2.11 Remove this after dropping 2.10.x support. - private object HasCompat { val compat = this }; import HasCompat._ - - def make(c: Context)(mt: c.Type, inputs: Inputs[c.universe.type]): BuilderResult[c.type] = new BuilderResult[c.type] { + 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.{ Apply => ApplyTree, _ } - import compat._ import util._ val global: Global = c.universe.asInstanceOf[Global] @@ -30,9 +26,9 @@ object TupleNBuilder extends TupleBuilder { val ctx: c.type = c val representationC: PolyType = { val tcVariable: Symbol = newTCVariable(util.initialOwner) - val tupleTypeArgs = inputs.map(in => typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type]) + val tupleTypeArgs = inputs.map(in => internal.typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type]) val tuple = global.definitions.tupleType(tupleTypeArgs) - PolyType(tcVariable :: Nil, tuple.asInstanceOf[Type]) + internal.polyType(tcVariable :: Nil, tuple.asInstanceOf[Type]) } val resultType = appliedType(representationC, idTC :: Nil) From 37f4e8311e93ef4b0cab06afbcd3accb7e819afc Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sun, 19 Jun 2016 11:42:31 +0100 Subject: [PATCH 31/34] Add -Ywarn-unused & -Ywarn-unused-import, & fix warnings --- .../main/scala/sbt/internal/util/appmacro/ContextUtil.scala | 1 - .../src/main/scala/sbt/internal/util/appmacro/Instance.scala | 1 - .../main/scala/sbt/internal/util/appmacro/KListBuilder.scala | 3 --- .../main/scala/sbt/internal/util/appmacro/TupleBuilder.scala | 2 -- .../scala/sbt/internal/util/appmacro/TupleNBuilder.scala | 5 +---- 5 files changed, 1 insertion(+), 11 deletions(-) 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 ee3b56361..b9b968a23 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 @@ -3,7 +3,6 @@ package appmacro import scala.reflect._ import macros._ -import scala.tools.nsc.Global import ContextUtil.{ DynamicDependencyError, DynamicReferenceError } object ContextUtil { 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 3177d59c4..a10fdfb18 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 @@ -23,7 +23,6 @@ trait MonadInstance extends Instance { import scala.reflect._ import macros._ -import reflect.internal.annotations.compileTimeOnly object Instance { final val ApplyName = "app" 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 cab5058cb..65b061e66 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 @@ -1,8 +1,6 @@ package sbt.internal.util package appmacro -import Types.Id -import scala.tools.nsc.Global import scala.reflect._ import macros._ @@ -59,7 +57,6 @@ object KListBuilder extends TupleBuilder { val klistType: Type = (inputs :\ knilType)((in, klist) => kconsType(in.tpe, klist)) val representationC = internal.polyType(tcVariable :: Nil, klistType) - val resultType = appliedType(representationC, idTC :: Nil) 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/TupleBuilder.scala b/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala index c36baa78a..1186f3549 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 @@ -1,8 +1,6 @@ package sbt.internal.util package appmacro -import Types.Id -import scala.tools.nsc.Global import scala.reflect._ import macros._ 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 f902db25e..1c5430e4c 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 @@ -1,7 +1,6 @@ package sbt.internal.util package appmacro -import Types.Id import scala.tools.nsc.Global import scala.reflect._ import macros._ @@ -17,11 +16,10 @@ object TupleNBuilder extends TupleBuilder { 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.{ Apply => ApplyTree, _ } + import c.universe._ import util._ val global: Global = c.universe.asInstanceOf[Global] - val mTC: Type = mt.asInstanceOf[c.universe.Type] val ctx: c.type = c val representationC: PolyType = { @@ -30,7 +28,6 @@ object TupleNBuilder extends TupleBuilder { val tuple = global.definitions.tupleType(tupleTypeArgs) internal.polyType(tcVariable :: Nil, tuple.asInstanceOf[Type]) } - val resultType = appliedType(representationC, idTC :: Nil) val input: Tree = mkTuple(inputs.map(_.expr)) val alistInstance: Tree = { From ed38fcd695c7e92b3ee76eee51732524d50a01fc Mon Sep 17 00:00:00 2001 From: jvican Date: Tue, 23 May 2017 23:48:20 +0200 Subject: [PATCH 32/34] Move `util-appmacro` to sbt as `core-macros` This commit moves everything behind the old util-appmacro over to sbt/sbt. appmacro has been renamed as core-macros. It is indeed the project that defines the main logic to manipulate macros, and defines interfaces that are then implemented by main-settings. Because of this strong relationship between these two modules, I find that they have to stay together and be able to be modified in one commit. I've realised this while hacking on the macro changes for the usability of sbt. The reason for this change is because the logic of this module is really tight to the implementation of sbt and, I would even say, essentially define the sbt DSL. --- build.sbt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/build.sbt b/build.sbt index a884e5ef7..819873bd5 100644 --- a/build.sbt +++ b/build.sbt @@ -249,6 +249,16 @@ lazy val commandProj = (project in file("main-command")) addSbtCompilerClasspath, addSbtLm) +// 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( + commonSettings, + name := "Core Macros", + libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value + ) + .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")) .dependsOn(commandProj, stdTaskProj) From d2f019a47a75773eadebf8469e900350040dd951 Mon Sep 17 00:00:00 2001 From: jvican Date: Tue, 23 May 2017 23:53:04 +0200 Subject: [PATCH 33/34] 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 + } + } } From 10c737ae2085b6015a7e3993917c620c4a774ba8 Mon Sep 17 00:00:00 2001 From: jvican Date: Tue, 23 May 2017 23:54:22 +0200 Subject: [PATCH 34/34] Move to `core-macros` and depend on it --- build.sbt | 3 +-- .../main/scala/sbt/internal/util/appmacro/ContextUtil.scala | 0 .../src/main/scala/sbt/internal/util/appmacro/Convert.scala | 0 .../src/main/scala/sbt/internal/util/appmacro/Instance.scala | 0 .../main/scala/sbt/internal/util/appmacro/KListBuilder.scala | 0 .../main/scala/sbt/internal/util/appmacro/MixedBuilder.scala | 0 .../main/scala/sbt/internal/util/appmacro/TupleBuilder.scala | 0 .../main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala | 0 8 files changed, 1 insertion(+), 2 deletions(-) rename {internal/util-appmacro => core-macros}/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala (100%) rename {internal/util-appmacro => core-macros}/src/main/scala/sbt/internal/util/appmacro/Convert.scala (100%) rename {internal/util-appmacro => core-macros}/src/main/scala/sbt/internal/util/appmacro/Instance.scala (100%) rename {internal/util-appmacro => core-macros}/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala (100%) rename {internal/util-appmacro => core-macros}/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala (100%) rename {internal/util-appmacro => core-macros}/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala (100%) rename {internal/util-appmacro => core-macros}/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala (100%) diff --git a/build.sbt b/build.sbt index 675ac0e8e..33a4cde61 100644 --- a/build.sbt +++ b/build.sbt @@ -261,14 +261,13 @@ lazy val coreMacrosProj = (project in file("core-macros")) // 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")) - .dependsOn(commandProj, stdTaskProj) + .dependsOn(commandProj, stdTaskProj, coreMacrosProj) .settings( testedBaseSettings, name := "Main Settings" ) .configure( addSbtUtilCache, - addSbtUtilApplyMacro, addSbtCompilerInterface, addSbtUtilRelation, addSbtUtilLogging, diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala similarity index 100% rename from internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala rename to core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Convert.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/Convert.scala similarity index 100% rename from internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Convert.scala rename to core-macros/src/main/scala/sbt/internal/util/appmacro/Convert.scala diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Instance.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/Instance.scala similarity index 100% rename from internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/Instance.scala rename to core-macros/src/main/scala/sbt/internal/util/appmacro/Instance.scala diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala similarity index 100% rename from internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala rename to core-macros/src/main/scala/sbt/internal/util/appmacro/KListBuilder.scala diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala similarity index 100% rename from internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala rename to core-macros/src/main/scala/sbt/internal/util/appmacro/MixedBuilder.scala diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala similarity index 100% rename from internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala rename to core-macros/src/main/scala/sbt/internal/util/appmacro/TupleBuilder.scala diff --git a/internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala similarity index 100% rename from internal/util-appmacro/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala rename to core-macros/src/main/scala/sbt/internal/util/appmacro/TupleNBuilder.scala