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._