Description
Compiler version
3.3.2-RC1-bin-20230602-57c2b66-NIGHTLY
Minimized code
#16565 introduced checks for the flags when creating new symbols using the Quotes API. However, this makes it difficult to copy flags from existing symbols under expansion to new symbols. Below is a (not very useful but minimized) example that empties the body of DefDef
nodes and changes the return type to Unit
. It works under Scala 3.3.0 but fails in Scala 3.3.1 due to the check and the fact that DefDef
the macro encounters has more flags than the check allows; in the example these are Flags.Artifact
and Flags.Synthetic
.
Overall, I would advocate for allowing the flags with the comment "Flags that could be allowed", plus Flags.Artifact
. I think there is no reason to be overly restrictive.
Also, the code worked in 3.3.0; it would be nice to make 3.3.1 not break it.
Example
import scala.annotation.experimental
import scala.quoted.*
@experimental
inline def makeUnit(inline expr: Unit): Unit = ${ makeUnitImpl('expr) }
@experimental
def makeUnitImpl(expr: Expr[Unit])(using Quotes): Expr[Unit] =
import quotes.reflect.*
object transformer extends TreeMap:
override def transformTerm(term: Term)(owner: Symbol) = term match
case Lambda(_, _) =>
val Block(List(lambda: DefDef), closure: Closure) = term: @unchecked
val transformedLambda = transformSubTrees(List(lambda))(owner).head
Block(List(transformedLambda), transformTerm(Closure(Ref(transformedLambda.symbol), closure.tpeOpt))(owner))
case _ =>
super.transformTerm(term)(owner)
override def transformStatement(stat: Statement)(owner: Symbol) = stat match
case DefDef(name, List(TermParamClause(_)), _, rhs) =>
val MethodType(paramNames, paramTypes, resultType) = stat.symbol.info: @unchecked
val info = MethodType(paramNames)(_ => paramTypes, _ => TypeRepr.of[Unit])
val privateWithin = if stat.symbol.flags is Flags.Protected then stat.symbol.protectedWithin else stat.symbol.privateWithin
//
// this is the part where we want to use the same flags as the existing symbol `stat.symbol.flags`
//
val symbol = Symbol.newMethod(owner, name, info, stat.symbol.flags, privateWithin.fold(Symbol.noSymbol) { _.typeSymbol })
DefDef(symbol, paramss => rhs map { _ => Literal(UnitConstant()) })
case _ =>
super.transformStatement(stat)(owner)
transformer.transformTerm(expr.asTerm)(Symbol.spliceOwner).asExprOf[Unit]
import scala.annotation.experimental
import scala.quoted.*
@experimental
def test = makeUnit((x: Int) => x)