@@ -5,6 +5,8 @@ import mill.api.internal
55import scala .annotation .compileTimeOnly
66import scala .reflect .macros .blackbox .Context
77
8+ import scala .quoted .*
9+
810/**
911 * A generic Applicative-functor macro: translates calls to
1012 *
@@ -24,7 +26,7 @@ object Applicative {
2426 def apply [T ](t : M [T ]): T
2527 }
2628 object ApplyHandler {
27- // @compileTimeOnly("Target#apply() can only be used with a T{...} block")
29+ @ compileTimeOnly(" Target#apply() can only be used with a T{...} block" )
2830 implicit def defaultApplyHandler [M [+ _]]: ApplyHandler [M ] = ???
2931 }
3032 trait Applyable [M [+ _], + T ] {
@@ -39,73 +41,102 @@ object Applicative {
3941 def traverseCtx [I , R ](xs : Seq [W [I ]])(f : (IndexedSeq [I ], Ctx ) => Z [R ]): T [R ]
4042 }
4143
42- def impl [M [_], T : c. WeakTypeTag , Ctx : c. WeakTypeTag ]( c : Context )( t : c. Expr [T ] ): c. Expr [M [T ]] = {
43- // impl0(c)(t.tree)(implicitly[c.WeakTypeTag[T]], implicitly[c.WeakTypeTag[Ctx]])
44- ???
44+ def impl [M [_]: Type , Q [_] : Type , Z [_] : Type , T : Type , Ctx : Type ]( using Quotes )( caller : Expr [ Applyer [ Q , M , Z , Ctx ]], t : Expr [Z [ T ]] ): Expr [M [T ]] = {
45+ import quotes . reflect . *
46+ impl0( using quotes)(caller.asTerm, t.asTerm)( using Type .of[ M ], Type .of[ Q ], Type .of[ Z ], Type .of[ T ], Type .of[ Ctx ])
4547 }
46- def impl0 [M [_], T : c.WeakTypeTag , Ctx : c.WeakTypeTag ](c : Context )(t : c.Tree ): c.Expr [M [T ]] = {
47- ???
48- // import c.universe._
49- // def rec(t: Tree): Iterator[c.Tree] = Iterator(t) ++ t.children.flatMap(rec(_))
50-
51- // val exprs = collection.mutable.Buffer.empty[c.Tree]
52- // val targetApplySym = typeOf[Applyable[Nothing, _]].member(TermName("apply"))
53-
54- // val itemsName = c.freshName(TermName("items"))
55- // val itemsSym = c.internal.newTermSymbol(c.internal.enclosingOwner, itemsName)
56- // c.internal.setFlag(itemsSym, (1L << 44).asInstanceOf[c.universe.FlagSet])
57- // c.internal.setInfo(itemsSym, typeOf[Seq[Any]])
58- // // Derived from @olafurpg's
59- // // https://gist.github.com/olafurpg/596d62f87bf3360a29488b725fbc7608
60- // val defs = rec(t).filter(_.isDef).map(_.symbol).toSet
61-
62- // val ctxName = TermName(c.freshName("ctx"))
63- // val ctxSym = c.internal.newTermSymbol(c.internal.enclosingOwner, ctxName)
64- // c.internal.setInfo(ctxSym, weakTypeOf[Ctx])
65-
66- // val transformed = c.internal.typingTransform(t) {
67- // case (t @ q"$fun.apply()($handler)", api) if t.symbol == targetApplySym =>
68- // val localDefs = rec(fun).filter(_.isDef).map(_.symbol).toSet
69- // val banned = rec(t).filter(x => defs(x.symbol) && !localDefs(x.symbol))
70-
71- // if (banned.hasNext) {
72- // val banned0 = banned.next()
73- // c.abort(
74- // banned0.pos,
75- // "Target#apply() call cannot use `" + banned0.symbol + "` defined within the T{...} block"
76- // )
77- // }
78- // val tempName = c.freshName(TermName("tmp"))
79- // val tempSym = c.internal.newTermSymbol(c.internal.enclosingOwner, tempName)
80- // c.internal.setInfo(tempSym, t.tpe)
81- // val tempIdent = Ident(tempSym)
82- // c.internal.setType(tempIdent, t.tpe)
83- // c.internal.setFlag(tempSym, (1L << 44).asInstanceOf[c.universe.FlagSet])
84- // val itemsIdent = Ident(itemsSym)
85- // exprs.append(q"$fun")
86- // c.typecheck(q"$itemsIdent(${exprs.size - 1}).asInstanceOf[${t.tpe}]")
87- // case (t, api)
88- // if t.symbol != null
89- // && t.symbol.annotations.exists(_.tree.tpe =:= typeOf[mill.api.Ctx.ImplicitStub]) =>
90- // val tempIdent = Ident(ctxSym)
91- // c.internal.setType(tempIdent, t.tpe)
92- // c.internal.setFlag(ctxSym, (1L << 44).asInstanceOf[c.universe.FlagSet])
93- // tempIdent
94-
95- // case (t, api) => api.default(t)
96- // }
97-
98- // val ctxBinding = c.internal.valDef(ctxSym)
99-
100- // val itemsBinding = c.internal.valDef(itemsSym)
101- // val callback = c.typecheck(q"{(${itemsBinding}, ${ctxBinding}) => $transformed}")
102-
103- // val res =
104- // q"${c.prefix}.traverseCtx[_root_.scala.Any, ${weakTypeOf[T]}](${exprs.toList}){ $callback }"
105-
106- // c.internal.changeOwner(transformed, c.internal.enclosingOwner, callback.symbol)
107-
108- // c.Expr[M[T]](res)
48+ def impl0 [M [_]: Type , Q [_]: Type , Z [_]: Type , T : Type , Ctx : Type ](using Quotes )(caller : quotes.reflect.Tree , t : quotes.reflect.Tree ): Expr [M [T ]] = {
49+ import quotes .reflect .*
50+
51+ val targetApplySym = TypeRepr .of[Applyable [Nothing , ? ]].typeSymbol.methodMember(" apply" ).head
52+
53+ // Derived from @olafurpg's
54+ // https://gist.github.com/olafurpg/596d62f87bf3360a29488b725fbc7608
55+
56+ def extractDefs (tree : Tree ): Set [Symbol ] =
57+ new TreeAccumulator [Set [Symbol ]] {
58+ override def foldTree (x : Set [Symbol ], tree : Tree )(owner : Symbol ): Set [Symbol ] = tree match
59+ case tree : Definition => foldOverTree(x + tree.symbol, tree)(owner)
60+ case tree => foldOverTree(x, tree)(owner)
61+ }.foldTree(Set .empty, tree)(Symbol .spliceOwner)
62+
63+ def visitAllTrees (tree : Tree )(f : Tree => Unit ): Unit =
64+ new TreeTraverser {
65+ override def traverseTree (tree : Tree )(owner : Symbol ): Unit =
66+ f(tree)
67+ traverseTreeChildren(tree)(owner)
68+ }.traverseTree(tree)(Symbol .spliceOwner)
69+
70+ val defs = extractDefs(t)
71+
72+ var hasErrors = false
73+
74+ def transformed (itemsRef : Expr [IndexedSeq [Any ]], ctxRef : Expr [Ctx ]): (Expr [Z [T ]], Expr [List [Q [Any ]]]) = {
75+ val exprs = collection.mutable.Buffer .empty[Tree ]
76+ val treeMap = new TreeMap {
77+
78+ override def transformTerm (tree : Term )(owner : Symbol ): Term = tree match
79+ // case t @ '{$fun.apply()($handler)}
80+ case t @ Apply (Apply (sel @ Select (fun, " apply" ), Nil ), List (handler)) if sel.symbol == targetApplySym =>
81+ val localDefs = extractDefs(fun)
82+ visitAllTrees(t){ x =>
83+ val sym = x.symbol
84+ if (sym != Symbol .noSymbol && defs(sym) && ! localDefs(sym)) {
85+ hasErrors = true
86+ report.error(
87+ " Target#apply() call cannot use `" + x.symbol + " ` defined within the T{...} block" ,
88+ x.pos
89+ )
90+ }
91+ }
92+
93+ t.tpe.asType match
94+ case ' [tt] =>
95+ // val tempName = c.freshName(TermName("tmp"))
96+ // val tempSym = c.internal.newTermSymbol(c.internal.enclosingOwner, tempName)
97+ // c.internal.setInfo(tempSym, t.tpe)
98+ // val tempIdent = Ident(tempSym)
99+ // c.internal.setType(tempIdent, t.tpe)
100+ // c.internal.setFlag(tempSym, (1L << 44).asInstanceOf[c.universe.FlagSet])
101+ // val itemsIdent = Ident(itemsSym)
102+ // exprs.append(q"$fun")
103+ exprs += fun
104+ ' {$itemsRef($ {Expr (exprs.size - 1 )}).asInstanceOf [tt]}.asTerm
105+ case t if t.symbol.exists
106+ && t.symbol.annotations.exists(_.tpe =:= TypeRepr .of[mill.api.Ctx .ImplicitStub ]) =>
107+ // val tempIdent = Ident(ctxSym)
108+ // c.internal.setType(tempIdent, t.tpe)
109+ // c.internal.setFlag(ctxSym, (1L << 44).asInstanceOf[c.universe.FlagSet])
110+ // tempIdent
111+ ctxRef.asTerm
112+
113+ case t => super .transformTerm(t)(owner)
114+ end transformTerm
115+ }
116+
117+ val newBody = treeMap.transformTree(t)(Symbol .spliceOwner).asExprOf[Z [T ]]
118+ val exprsList = Expr .ofList(exprs.toList.map(_.asExprOf[Q [Any ]]))
119+ (newBody, exprsList)
120+ }
121+
122+ val callerExpr = caller.asExprOf[Applyer [Q , M , Z , Ctx ]]
123+
124+ val (callback, exprsList) = {
125+ var exprsExpr : Expr [List [Q [Any ]]] | Null = null
126+ val cb = ' { (items : IndexedSeq [Any ], ctx : Ctx ) =>
127+ $ {
128+ val (body, exprs) = transformed(' items , ' ctx )
129+ exprsExpr = exprs
130+ body
131+ }
132+ }
133+ (cb, exprsExpr.nn)
134+ }
135+
136+ if hasErrors then
137+ ' {throw new RuntimeException (" stub implementation - macro expansion had errors" )}
138+ else
139+ ' {$ {callerExpr}.traverseCtx[Any , T ]($exprsList)($callback)}
109140 }
110141
111142}
0 commit comments