-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add minimal prototype example of SIP-61-like macro annotation (#19997)
- Loading branch information
Showing
3 changed files
with
74 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
a3false | ||
a3true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
//> using options -experimental -Yno-experimental | ||
|
||
package example | ||
|
||
import scala.annotation.{experimental, MacroAnnotation, StaticAnnotation} | ||
import scala.quoted._ | ||
import scala.collection.mutable.Map | ||
import scala.compiletime.ops.double | ||
|
||
// TODO make unrollLast the macro annotation and remove unrollHelper | ||
class unrollLast extends StaticAnnotation | ||
|
||
@experimental | ||
class unrollHelper extends MacroAnnotation { | ||
def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] = | ||
import quotes.reflect._ | ||
tree match | ||
case tree: DefDef => transformDefDef(tree) | ||
case _ => report.throwError("unrollHelper can only be applied to a method definition", tree.pos) | ||
|
||
private def transformDefDef(using Quotes)(ddef: quotes.reflect.DefDef): List[quotes.reflect.Definition] = | ||
import quotes.reflect._ | ||
val unrollLastSym = Symbol.requiredClass("example.unrollLast") | ||
ddef.paramss match | ||
case Nil => | ||
report.throwError("unrollHelper must have an @unrollLast parameter", ddef.pos) | ||
case _ :: _ :: _ => | ||
report.throwError("unrollHelper does not yet support multiple parameter lists", ddef.pos) | ||
case TermParamClause(params) :: Nil => | ||
if params.isEmpty then report.throwError("unrollHelper must have an @unrollLast parameter", ddef.pos) | ||
else if params.init.exists(_.symbol.hasAnnotation(unrollLastSym)) || !params.last.symbol.hasAnnotation(unrollLastSym) then | ||
report.throwError("@unrollLast must be on the last parameter", ddef.pos) | ||
List(ddef, makeTelescopedDefDefWithoutLastArgument(ddef.symbol)) | ||
case _ => | ||
report.throwError("unrollHelper does not yet support type parameters", ddef.pos) | ||
|
||
private def makeTelescopedDefDefWithoutLastArgument(using Quotes)(defSym: quotes.reflect.Symbol): quotes.reflect.DefDef = | ||
import quotes.reflect._ | ||
def ddef1Rhs(argss: List[List[Tree]]): Some[Term] = | ||
val defaultArg = defaultGetter(defSym, argss.size + 2) // +1 for 1-based and +1 for the argument that was dropped | ||
val args1 = argss.head.asInstanceOf[List[Term]] :+ defaultArg | ||
Some(Ref(defSym).appliedToArgs(args1)) | ||
val sym1 = makeTelescopedSymbolWithoutLastArgument(defSym) | ||
DefDef(sym1, ddef1Rhs) | ||
|
||
private def makeTelescopedSymbolWithoutLastArgument(using Quotes)(defSym: quotes.reflect.Symbol): quotes.reflect.Symbol = | ||
import quotes.reflect._ | ||
val info1 = defSym.info match | ||
case info: MethodType => MethodType(info.paramNames.init)(_ => info.paramTypes.init, _ => info.resType) | ||
Symbol.newMethod(defSym.owner, defSym.name, info1, Flags.EmptyFlags, Symbol.noSymbol) | ||
|
||
private def defaultGetter(using Quotes)(sym: quotes.reflect.Symbol, idx: Int): quotes.reflect.Term = | ||
import quotes.reflect._ | ||
val getterSym = sym.owner.methodMember(sym.name + "$default$" + idx).head | ||
Ref(getterSym) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
//> using options -experimental -Yno-experimental | ||
|
||
import example.{unrollHelper, unrollLast} | ||
|
||
class Bar: | ||
@unrollHelper | ||
def foo(s: String, n: Int = 1, @unrollLast b: Boolean = true): String = s + n + b | ||
// generates: def foo(s: String, n: Int): String = foo(s, n, this.foo$default$3) | ||
|
||
@main def Test = | ||
import scala.reflect.Selectable.reflectiveSelectable | ||
val bar = new Bar | ||
val oldBar = bar.asInstanceOf[Any { def foo(s : String, n: Int): String }] | ||
|
||
println(bar.foo("a", 3, false)) | ||
println(oldBar.foo("b", 4)) |