mirror of https://github.com/sbt/sbt.git
Port Converted
This commit is contained in:
parent
d78c75df39
commit
1ed94bff16
48
build.sbt
48
build.sbt
|
|
@ -181,7 +181,7 @@ val scriptedSbtReduxMimaSettings = Def.settings(mimaPreviousArtifacts := Set())
|
|||
|
||||
lazy val sbtRoot: Project = (project in file("."))
|
||||
// .aggregate(nonRoots: _*)
|
||||
.aggregate(collectionProj)
|
||||
.aggregate(collectionProj, coreMacrosProj)
|
||||
.settings(
|
||||
minimalSettings,
|
||||
onLoadMessage := {
|
||||
|
|
@ -494,7 +494,7 @@ lazy val testingProj = (project in file("testing"))
|
|||
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestItemEvent.copy$default$*"),
|
||||
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestStringEvent.copy"),
|
||||
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestStringEvent.copy$default$1"),
|
||||
//no reason to use
|
||||
// no reason to use
|
||||
exclude[DirectMissingMethodProblem]("sbt.JUnitXmlTestsListener.testSuite"),
|
||||
)
|
||||
)
|
||||
|
|
@ -787,15 +787,8 @@ lazy val commandProj = (project in file("main-command"))
|
|||
lazy val coreMacrosProj = (project in file("core-macros"))
|
||||
.dependsOn(collectionProj)
|
||||
.settings(
|
||||
baseSettings :+ (crossScalaVersions := (scala212 :: scala213 :: Nil)),
|
||||
testedBaseSettings :+ (crossScalaVersions := (scala212 :: scala213 :: Nil)),
|
||||
name := "Core Macros",
|
||||
libraryDependencies += {
|
||||
if (scalaBinaryVersion.value == "3") {
|
||||
"org.scala-lang" % "scala-compiler" % scala213
|
||||
} else {
|
||||
"org.scala-lang" % "scala-compiler" % scalaVersion.value
|
||||
}
|
||||
},
|
||||
SettingKey[Boolean]("exportPipelining") := false,
|
||||
mimaSettings,
|
||||
)
|
||||
|
|
@ -1110,7 +1103,8 @@ lazy val serverTestProj = (project in file("server-test"))
|
|||
|}
|
||||
""".stripMargin
|
||||
}
|
||||
val file = (Test / target).value / "generated" / "src" / "test" / "scala" / "testpkg" / "TestProperties.scala"
|
||||
val file =
|
||||
(Test / target).value / "generated" / "src" / "test" / "scala" / "testpkg" / "TestProperties.scala"
|
||||
IO.write(file, content)
|
||||
file :: Nil
|
||||
},
|
||||
|
|
@ -1373,8 +1367,7 @@ lazy val lowerUtilProjects =
|
|||
lazy val nonRoots = allProjects.map(p => LocalProject(p.id))
|
||||
|
||||
ThisBuild / scriptedBufferLog := true
|
||||
ThisBuild / scriptedPrescripted := { _ =>
|
||||
}
|
||||
ThisBuild / scriptedPrescripted := { _ => }
|
||||
|
||||
def otherRootSettings =
|
||||
Seq(
|
||||
|
|
@ -1442,21 +1435,24 @@ def customCommands: Seq[Setting[_]] = Seq(
|
|||
import extracted._
|
||||
val sv = get(scalaVersion)
|
||||
val projs = structure.allProjectRefs
|
||||
val ioOpt = projs find { case ProjectRef(_, id) => id == "ioRoot"; case _ => false }
|
||||
val ioOpt = projs find { case ProjectRef(_, id) => id == "ioRoot"; case _ => false }
|
||||
val utilOpt = projs find { case ProjectRef(_, id) => id == "utilRoot"; case _ => false }
|
||||
val lmOpt = projs find { case ProjectRef(_, id) => id == "lmRoot"; case _ => false }
|
||||
val lmOpt = projs find { case ProjectRef(_, id) => id == "lmRoot"; case _ => false }
|
||||
val zincOpt = projs find { case ProjectRef(_, id) => id == "zincRoot"; case _ => false }
|
||||
(ioOpt map { case ProjectRef(build, _) => "{" + build.toString + "}/publishLocal" }).toList :::
|
||||
(utilOpt map { case ProjectRef(build, _) => "{" + build.toString + "}/publishLocal" }).toList :::
|
||||
(lmOpt map { case ProjectRef(build, _) => "{" + build.toString + "}/publishLocal" }).toList :::
|
||||
(zincOpt map {
|
||||
case ProjectRef(build, _) =>
|
||||
val zincSv = get((ProjectRef(build, "zinc") / scalaVersion))
|
||||
val csv = get((ProjectRef(build, "compilerBridge") / crossScalaVersions)).toList
|
||||
(csv flatMap { bridgeSv =>
|
||||
s"++$bridgeSv" :: ("{" + build.toString + "}compilerBridge/publishLocal") :: Nil
|
||||
}) :::
|
||||
List(s"++$zincSv", "{" + build.toString + "}/publishLocal")
|
||||
(ioOpt map { case ProjectRef(build, _) => "{" + build.toString + "}/publishLocal" }).toList :::
|
||||
(utilOpt map { case ProjectRef(build, _) =>
|
||||
"{" + build.toString + "}/publishLocal"
|
||||
}).toList :::
|
||||
(lmOpt map { case ProjectRef(build, _) =>
|
||||
"{" + build.toString + "}/publishLocal"
|
||||
}).toList :::
|
||||
(zincOpt map { case ProjectRef(build, _) =>
|
||||
val zincSv = get((ProjectRef(build, "zinc") / scalaVersion))
|
||||
val csv = get((ProjectRef(build, "compilerBridge") / crossScalaVersions)).toList
|
||||
(csv flatMap { bridgeSv =>
|
||||
s"++$bridgeSv" :: ("{" + build.toString + "}compilerBridge/publishLocal") :: Nil
|
||||
}) :::
|
||||
List(s"++$zincSv", "{" + build.toString + "}/publishLocal")
|
||||
}).getOrElse(Nil) :::
|
||||
List(s"++$sv", "publishLocal") :::
|
||||
state
|
||||
|
|
|
|||
|
|
@ -1,338 +1,77 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
package sbt.internal.util.appmacro
|
||||
|
||||
package sbt.internal.util
|
||||
package appmacro
|
||||
import sbt.internal.util.Types.Id
|
||||
import scala.compiletime.summonInline
|
||||
import scala.quoted.*
|
||||
import scala.reflect.TypeTest
|
||||
|
||||
import scala.reflect._
|
||||
import macros._
|
||||
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 <: blackbox.Context with Singleton](c: C): ContextUtil[C] = new ContextUtil(c: 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(<Tree of someValue>)`.
|
||||
*/
|
||||
def selectMacroImpl[T: c.WeakTypeTag](
|
||||
c: blackbox.Context
|
||||
)(f: (c.Expr[Any], c.Position) => c.Expr[T]): c.Expr[T] = {
|
||||
import c.universe._
|
||||
|
||||
c.macroApplication match {
|
||||
case s @ Select(Apply(_, t :: Nil), _) => f(c.Expr[Any](t), s.pos)
|
||||
case a @ Apply(_, t :: Nil) => f(c.Expr[Any](t), a.pos)
|
||||
case x => unexpectedTree(x)
|
||||
}
|
||||
}
|
||||
|
||||
def unexpectedTree[C <: blackbox.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`). 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 <: blackbox.Context](val ctx: C) {
|
||||
import ctx.universe.{ Apply => ApplyTree, _ }
|
||||
import internal.decorators._
|
||||
|
||||
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.companion
|
||||
lazy val alistTC: Type = alistType.typeConstructor
|
||||
|
||||
/** Modifiers for a local val. */
|
||||
lazy 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.freshName, which increments
|
||||
*/
|
||||
def freshTermName(prefix: String) = TermName(ctx.freshName("$" + 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 = internal.valDef(sym, EmptyTree)
|
||||
vd.setPos(pos)
|
||||
vd
|
||||
}
|
||||
|
||||
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.decodedName.toString, 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, mType: Type): Boolean =
|
||||
sym != null && sym != NoSymbol && defs.contains(sym) && {
|
||||
sym match {
|
||||
case m: MethodSymbol => m.returnType.erasure <:< mType
|
||||
case _ => sym.typeSignature <:< mType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 =
|
||||
illegalReference(defs, sym, weakTypeOf[Any])
|
||||
|
||||
type PropertyChecker = (String, Type, Tree) => Boolean
|
||||
|
||||
/**
|
||||
* 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: PropertyChecker,
|
||||
mType: Type
|
||||
): 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, mType) =>
|
||||
ctx.error(id.pos, DynamicReferenceError + ": " + name)
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
@deprecated("Use that variant that specifies the M instance types to exclude", since = "1.3.0")
|
||||
/**
|
||||
* 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: PropertyChecker): Tree => Unit =
|
||||
checkReferences(defs, isWrapper, weakTypeOf[Any])
|
||||
|
||||
/**
|
||||
* 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 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(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]
|
||||
|
||||
/** The type representing the type constructor `[X] X` */
|
||||
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
|
||||
}
|
||||
|
||||
/** >: Nothing <: Any */
|
||||
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]
|
||||
|
||||
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 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): Unit = {
|
||||
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 + ").")
|
||||
}
|
||||
|
||||
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 = {
|
||||
val ts: Type = obj.typeSignature
|
||||
val m: global.Symbol = ts.asInstanceOf[global.Type].nonPrivateMember(global.newTermName(name))
|
||||
m.asInstanceOf[Symbol]
|
||||
}
|
||||
trait ContextUtil[C <: Quotes & scala.Singleton](val qctx: C):
|
||||
import qctx.reflect.*
|
||||
given qctx.type = qctx
|
||||
|
||||
/**
|
||||
* Returns a Type representing the type constructor tcp.<name>. 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]`.
|
||||
* type M[x] = List[x] }`, the call `extractTypeCon(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 extractTypeCon(tcp: AnyRef & scala.Singleton, name: String)(using
|
||||
tcpt: Type[tcp.type]
|
||||
): TypeRepr =
|
||||
val tcpTpe = TypeRepr.of[tcp.type]
|
||||
val fSym = tcpTpe.typeSymbol.declaredType(name).head
|
||||
val typeConTpe: TypeRepr = tcpTpe.memberType(fSym)
|
||||
val hiRepr = typeConTpe match
|
||||
case TypeBounds(low, TypeLambda(_, _, AppliedType(tc, _))) => tc
|
||||
hiRepr
|
||||
|
||||
/**
|
||||
* Substitutes wrappers in tree `t` with the result of `subWrapper`. A wrapper is a Tree of the
|
||||
* form `f[T](v)` for which isWrapper(<Tree of f>, <Underlying Type>, <qual>.target) returns true.
|
||||
* Typically, `f` is a `Select` or `Ident`. The wrapper is replaced with the result of
|
||||
* `subWrapper(<Type of T>, <Tree of v>, <wrapper Tree>)`
|
||||
* Returns a reference given a singleton/termref
|
||||
*/
|
||||
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 extractSingleton[A: Type]: Expr[A] =
|
||||
def termRef(r: TypeRepr)(using rtt: TypeTest[TypeRepr, TermRef]): Ref = r match
|
||||
case rtt(ref) => Ref.term(ref)
|
||||
case _ => sys.error(s"expected termRef but got $r")
|
||||
termRef(TypeRepr.of[A]).asExprOf[A]
|
||||
|
||||
private var counter: Int = -1
|
||||
def freshName(prefix: String): String =
|
||||
counter = counter + 1
|
||||
s"$$${prefix}${counter}"
|
||||
|
||||
/**
|
||||
* Constructs a new, synthetic, local var with type `tpe`, a unique name, initialized to
|
||||
* zero-equivalent (Zero[A]), and owned by `parent`.
|
||||
*/
|
||||
def freshValDef(parent: Symbol, tpe: TypeRepr, rhs: Term): ValDef =
|
||||
tpe.asType match
|
||||
case '[a] =>
|
||||
val sym =
|
||||
Symbol.newVal(
|
||||
parent,
|
||||
freshName("q"),
|
||||
tpe,
|
||||
Flags.Synthetic,
|
||||
Symbol.noSymbol
|
||||
)
|
||||
ValDef(sym, rhs = Some(rhs))
|
||||
|
||||
def typed[A: Type](value: Term): Term =
|
||||
Typed(value, TypeTree.of[A])
|
||||
|
||||
def tupleTypeRepr(param: List[TypeRepr]): TypeRepr =
|
||||
param match
|
||||
case x :: xs => TypeRepr.of[scala.*:].appliedTo(List(x, tupleTypeRepr(xs)))
|
||||
case Nil => TypeRepr.of[EmptyTuple]
|
||||
|
||||
final class Input(
|
||||
val tpe: TypeRepr,
|
||||
val term: Term,
|
||||
val name: String
|
||||
)
|
||||
|
||||
trait TermTransform[F[_]]:
|
||||
def apply(in: Term): Term
|
||||
end TermTransform
|
||||
|
||||
def idTransform: TermTransform[Id] = in => in
|
||||
end ContextUtil
|
||||
|
|
|
|||
|
|
@ -5,47 +5,75 @@
|
|||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.internal.util
|
||||
package appmacro
|
||||
package sbt.internal.util.appmacro
|
||||
|
||||
import scala.reflect._
|
||||
import macros._
|
||||
import Types.idFun
|
||||
import sbt.internal.util.Types
|
||||
import scala.quoted.*
|
||||
|
||||
abstract class Convert {
|
||||
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 <: blackbox.Context with Singleton] {
|
||||
def isSuccess: Boolean
|
||||
def transform(f: C#Tree => C#Tree): Converted[C]
|
||||
}
|
||||
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] {
|
||||
def isSuccess = false
|
||||
def transform(f: C#Tree => C#Tree): Converted[C] = new Failure(position, message)
|
||||
}
|
||||
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 <: 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)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Convert is a glorified partial function to scan through the AST for the purpose of substituting
|
||||
* the matching term with something else.
|
||||
*
|
||||
* This is driven by calling transformWrappers(...) method. The filtering is limited to the shape of
|
||||
* code matched using `appTransformer`, which is a generic function with a single type param and a
|
||||
* single term param like `X.wrapInit[A](...)`.
|
||||
*/
|
||||
trait Convert[C <: Quotes & Singleton](override val qctx: C) extends ContextUtil[C]:
|
||||
// with TupleBuilder[C]:
|
||||
import qctx.reflect.*
|
||||
|
||||
def convert[A: Type](nme: String, in: Term): Converted
|
||||
|
||||
def asPredicate[A]: (String, Type[A], Term) => Boolean =
|
||||
(n, tpe, tree) =>
|
||||
val tag = tpe
|
||||
convert(n, tree)(tag).isSuccess
|
||||
|
||||
/**
|
||||
* Substitutes wrappers in tree `t` with the result of `subWrapper`. A wrapper is a Tree of the
|
||||
* form `f[T](v)` for which isWrapper(<Tree of f>, <Underlying Type>, <qual>.target) returns true.
|
||||
* Typically, `f` is a `Select` or `Ident`. The wrapper is replaced with the result of
|
||||
* `subWrapper(<Type of T>, <Tree of v>, <wrapper Tree>)`
|
||||
*/
|
||||
def transformWrappers(
|
||||
tree: Term,
|
||||
subWrapper: (String, TypeRepr, Term, Term) => Converted,
|
||||
owner: Symbol,
|
||||
): Term =
|
||||
// the main tree transformer that replaces calls to InputWrapper.wrap(x) with
|
||||
// plain Idents that reference the actual input value
|
||||
object appTransformer extends TreeMap:
|
||||
override def transformTerm(tree: Term)(owner: Symbol): Term =
|
||||
tree match
|
||||
case Apply(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) =>
|
||||
subWrapper(nme, targ.tpe, qual, tree) match
|
||||
case Converted.Success(tree, finalTransform) =>
|
||||
finalTransform(tree)
|
||||
case Converted.Failure(position, message) =>
|
||||
report.error(message, position)
|
||||
sys.error("macro error: " + message)
|
||||
case _ =>
|
||||
super.transformTerm(tree)(owner)
|
||||
case _ =>
|
||||
super.transformTerm(tree)(owner)
|
||||
end appTransformer
|
||||
appTransformer.transformTerm(tree)(owner)
|
||||
|
||||
object Converted:
|
||||
def success(tree: Term) = Converted.Success(tree, Types.idFun)
|
||||
|
||||
enum Converted:
|
||||
def isSuccess: Boolean = this match
|
||||
case Success(_, _) => true
|
||||
case _ => false
|
||||
|
||||
def transform(f: Term => Term): Converted = this match
|
||||
case Success(tree, finalTransform) => Success(f(tree), finalTransform)
|
||||
case x: Failure => x
|
||||
case x: NotApplicable => x
|
||||
|
||||
case Success(tree: Term, finalTransform: Term => Term) extends Converted
|
||||
case Failure(position: Position, message: String) extends Converted
|
||||
case NotApplicable() extends Converted
|
||||
end Converted
|
||||
end Convert
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package sbt.internal
|
||||
|
||||
import sbt.internal.util.appmacro.*
|
||||
import verify.*
|
||||
import ConvertTestMacro._
|
||||
|
||||
object ConvertTest extends BasicTestSuite:
|
||||
test("convert") {
|
||||
// assert(someMacro(ConvertTest.wrapInit(1) == 2))
|
||||
assert(someMacro(ConvertTest.wrapInit(1).toString == "Some(2)"))
|
||||
}
|
||||
|
||||
def wrapInitTask[A](a: A): Int = 2
|
||||
def wrapInit[A](a: A): Int = 2
|
||||
end ConvertTest
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package sbt.internal
|
||||
|
||||
import sbt.internal.util.appmacro.*
|
||||
import scala.quoted.*
|
||||
|
||||
object ConvertTestMacro:
|
||||
final val WrapInitName = "wrapInit"
|
||||
final val WrapInitTaskName = "wrapInitTask"
|
||||
|
||||
inline def someMacro(inline expr: Boolean): Boolean =
|
||||
${ someMacroImpl('expr) }
|
||||
|
||||
def someMacroImpl(expr: Expr[Boolean])(using qctx: Quotes) =
|
||||
val convert1: Convert[qctx.type] = new InputInitConvert(qctx)
|
||||
import convert1.qctx.reflect.*
|
||||
def addTypeCon(tpe: TypeRepr, qual: Term, selection: Term): Term =
|
||||
tpe.asType match
|
||||
case '[a] =>
|
||||
'{
|
||||
Option[a](${ selection.asExprOf[a] })
|
||||
}.asTerm
|
||||
def substitute(name: String, tpe: TypeRepr, qual: Term, replace: Term) =
|
||||
convert1.convert[Boolean](name, qual) transform { (tree: Term) =>
|
||||
addTypeCon(tpe, tree, replace)
|
||||
}
|
||||
convert1.transformWrappers(expr.asTerm, substitute, Symbol.spliceOwner).asExprOf[Boolean]
|
||||
|
||||
class InputInitConvert[C <: Quotes & scala.Singleton](override val qctx: C)
|
||||
extends Convert[C](qctx)
|
||||
with ContextUtil[C](qctx):
|
||||
// with TupleBuilder[C](qctx)
|
||||
// with TupleNBuilder[C](qctx):
|
||||
import qctx.reflect.*
|
||||
def convert[A: Type](nme: String, in: Term): Converted =
|
||||
nme match
|
||||
case WrapInitName => Converted.success(in)
|
||||
case WrapInitTaskName => Converted.Failure(in.pos, initTaskErrorMessage)
|
||||
case _ => Converted.NotApplicable()
|
||||
|
||||
private def initTaskErrorMessage = "Internal sbt error: initialize+task wrapper not split"
|
||||
end InputInitConvert
|
||||
end ConvertTestMacro
|
||||
|
|
@ -7,11 +7,12 @@
|
|||
|
||||
package sbt.internal.util
|
||||
|
||||
/*
|
||||
trait TypeFunctions {
|
||||
import TypeFunctions._
|
||||
trait TypeFunctions:
|
||||
type Id[X] = X
|
||||
type NothingK[X] = Nothing
|
||||
|
||||
/*
|
||||
import TypeFunctions._
|
||||
sealed trait Const[A] { type Apply[B] = A }
|
||||
sealed trait ConstK[A] { type l[L[x]] = A }
|
||||
sealed trait Compose[A[_], B[_]] { type Apply[T] = A[B[T]] }
|
||||
|
|
@ -24,8 +25,10 @@ trait TypeFunctions {
|
|||
final val right: Id ~> Right[Nothing, *] =
|
||||
λ[Id ~> AnyRight](Right(_)).setToString("TypeFunctions.right")
|
||||
final val some: Id ~> Some[*] = λ[Id ~> Some](Some(_)).setToString("TypeFunctions.some")
|
||||
final def idFun[T]: T => T = ((t: T) => t).setToString("TypeFunctions.id")
|
||||
final def const[A, B](b: B): A => B = ((_: A) => b).setToString(s"TypeFunctions.const($b)")
|
||||
*/
|
||||
final def idFun[A]: A => A = ((a: A) => a) // .setToString("TypeFunctions.id")
|
||||
final def const[A, B](b: B): A => B = ((_: A) => b) // .setToString(s"TypeFunctions.const($b)")
|
||||
/*
|
||||
final def idK[M[_]]: M ~> M = λ[M ~> M](m => m).setToString("TypeFunctions.idK")
|
||||
|
||||
def nestCon[M[_], N[_], G[_]](f: M ~> N): (M ∙ G)#l ~> (N ∙ G)#l =
|
||||
|
|
@ -35,9 +38,13 @@ trait TypeFunctions {
|
|||
|
||||
type Endo[T] = T => T
|
||||
type ~>|[A[_], B[_]] = A ~> Compose[Option, B]#Apply
|
||||
}
|
||||
*/
|
||||
|
||||
end TypeFunctions
|
||||
|
||||
/*
|
||||
object TypeFunctions extends TypeFunctions:
|
||||
|
||||
object TypeFunctions extends TypeFunctions {
|
||||
private implicit class Ops[T[_], R[_]](val underlying: T ~> R) extends AnyVal {
|
||||
def setToString(string: String): T ~> R = new (T ~> R) {
|
||||
override def apply[U](a: T[U]): R[U] = underlying(a)
|
||||
|
|
@ -54,7 +61,8 @@ object TypeFunctions extends TypeFunctions {
|
|||
override def hashCode: Int = f.hashCode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end TypeFunctions
|
||||
*/
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@
|
|||
|
||||
package sbt.internal.util
|
||||
|
||||
object Types extends Types
|
||||
object Types extends TypeFunctions
|
||||
|
||||
trait Types /* extends TypeFunctions */ {
|
||||
/*
|
||||
trait Types extends TypeFunctions {
|
||||
// val :^: = KCons
|
||||
// type :+:[H, T <: HList] = HCons[H, T]
|
||||
// val :+: = HCons
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue