Skip to content

Fix Reflection wildcard and Typed abstraction #12200

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 54 additions & 4 deletions compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -341,11 +341,19 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler

object TermTypeTest extends TypeTest[Tree, Term]:
def unapply(x: Tree): Option[Term & x.type] = x match
case x: tpd.PatternTree => None
case x: (tpd.Ident & x.type) =>
if x.isTerm && x.name != nme.WILDCARD then Some(x)
else None
case x: (tpd.Typed & x.type) =>
// Matches `Typed` but not `TypedTree`
TypedTypeTest.unapply(x)
case x: (tpd.SeqLiteral & x.type) => Some(x)
case x: (tpd.Inlined & x.type) => Some(x)
case x: (tpd.NamedArg & x.type) => Some(x)
case _ => if x.isTerm then Some(x) else None
case x: tpd.PatternTree => None
case _ =>
if x.isTerm then Some(x)
else None
end TermTypeTest

object Term extends TermModule:
Expand Down Expand Up @@ -429,7 +437,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler

object IdentTypeTest extends TypeTest[Tree, Ident]:
def unapply(x: Tree): Option[Ident & x.type] = x match
case x: (tpd.Ident & x.type) if x.isTerm => Some(x)
case x: (tpd.Ident & x.type) if x.isTerm && x.name != nme.WILDCARD => Some(x)
case _ => None
end IdentTypeTest

Expand Down Expand Up @@ -655,7 +663,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler

object TypedTypeTest extends TypeTest[Tree, Typed]:
def unapply(x: Tree): Option[Typed & x.type] = x match
case x: (tpd.Typed & x.type) => Some(x)
case x: (tpd.Typed & x.type) =>
x.expr match
case TermTypeTest(_) => Some(x)
case _ => None
case _ => None
end TypedTypeTest

Expand Down Expand Up @@ -1406,6 +1417,45 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
end extension
end TypeCaseDefMethods


type WildcardPattern = tpd.Ident

object WildcardPatternTypeTest extends TypeTest[Tree, WildcardPattern]:
def unapply(x: Tree): Option[WildcardPattern & x.type] = x match
case x: (tpd.Ident & x.type) if x.name == nme.WILDCARD => Some(x)
case _ => None
end WildcardPatternTypeTest

object WildcardPattern extends WildcardPatternModule:
def apply(): WildcardPattern =
withDefaultPos(untpd.Ident(nme.WILDCARD).withType(dotc.core.Symbols.defn.AnyType))
def unapply(pattern: WildcardPattern): true = true
end WildcardPattern

type TypedTree = tpd.Typed

object TypedTreeTypeTest extends TypeTest[Tree, TypedTree]:
def unapply(x: Tree): Option[TypedTree & x.type] = x match
case x: (tpd.Typed & x.type) => Some(x)
case _ => None
end TypedTreeTypeTest

object TypedTree extends TypedTreeModule:
def apply(expr: Term, tpt: TypeTree): Typed =
withDefaultPos(tpd.Typed(xCheckMacroValidExpr(expr), tpt))
def copy(original: Tree)(expr: Term, tpt: TypeTree): Typed =
tpd.cpy.Typed(original)(xCheckMacroValidExpr(expr), tpt)
def unapply(x: Typed): (Term, TypeTree) =
(x.expr, x.tpt)
end TypedTree

given TypedTreeMethods: TypedTreeMethods with
extension (self: Typed)
def tree: Tree = self.expr
def tpt: TypeTree = self.tpt
end extension
end TypedTreeMethods

type Bind = tpd.Bind

object BindTypeTest extends TypeTest[Tree, Bind]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,16 @@ object Extractors {
this += "CaseDef(" += pat += ", " += guard += ", " += body += ")"
case TypeCaseDef(pat, body) =>
this += "TypeCaseDef(" += pat += ", " += body += ")"
case WildcardPattern() =>
this += "WildcardPattern()"
case Bind(name, body) =>
this += "Bind(\"" += name += "\", " += body += ")"
case Unapply(fun, implicits, patterns) =>
this += "Unapply(" += fun += ", " ++= implicits += ", " ++= patterns += ")"
case Alternatives(patterns) =>
this += "Alternatives(" ++= patterns += ")"
case TypedTree(tree, tpt) =>
this += "TypedTree(" += tree += ", " += tpt += ")"
}

def visitConstant(x: Constant): this.type = x match {
Expand Down
27 changes: 20 additions & 7 deletions compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ object SourceCode {
}
this

case Ident("_") =>
case WildcardPattern() =>
this += "_"

case tree: Ident =>
Expand Down Expand Up @@ -453,6 +453,15 @@ object SourceCode {
printTypeOrAnnots(tpt.tpe)
}
}
case TypedTree(tree1, tpt) =>
printPattern(tree1)
tree1 match
case WildcardPattern() =>
this += ":"
printType(tpt.tpe)
case _ => // Alternatives, Unapply, Bind
this


case Assign(lhs, rhs) =>
printTree(lhs)
Expand Down Expand Up @@ -896,13 +905,13 @@ object SourceCode {
}

private def printPattern(pattern: Tree): this.type = pattern match {
case Ident("_") =>
case WildcardPattern() =>
this += "_"

case Bind(name, Ident("_")) =>
case Bind(name, WildcardPattern()) =>
this += name

case Bind(name, Typed(Ident("_"), tpt)) =>
case Bind(name, TypedTree(WildcardPattern(), tpt)) =>
this += highlightValDef(name) += ": "
printTypeTree(tpt)

Expand All @@ -928,9 +937,13 @@ object SourceCode {
case Alternatives(trees) =>
inParens(printPatterns(trees, " | "))

case Typed(Ident("_"), tpt) =>
this += "_: "
printTypeTree(tpt)
case TypedTree(tree1, tpt) =>
tree1 match
case WildcardPattern() =>
this += "_: "
printTypeTree(tpt)
case _ =>
printPattern(tree1)

case v: Term =>
printTree(v)
Expand Down
82 changes: 71 additions & 11 deletions library/src/scala/quoted/Quotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
* | +- Apply
* | +- TypeApply
* | +- Super
* | +- Typed
* | +- Assign
* | +- Block
* | +- Closure
Expand All @@ -146,7 +145,16 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
* | +- Inlined
* | +- SelectOuter
* | +- While
* | +---+- Typed
* | /
* +- TypedTree +------------------·
* +- WildcardPattern
* +- Bind
* +- Unapply
* +- Alternatives
* |
* +- CaseDef
* +- TypeCaseDef
* |
* +- TypeTree ----+- Inferred
* | +- TypeIdent
Expand All @@ -164,13 +172,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
* |
* +- TypeBoundsTree
* +- WildcardTypeTree
* |
* +- CaseDef
* |
* +- TypeCaseDef
* +- Bind
* +- Unapply
* +- Alternatives
*
* +- ParamClause -+- TypeParamClause
* +- TermParamClause
Expand Down Expand Up @@ -1120,8 +1121,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
/** `TypeTest` that allows testing at runtime in a pattern match if a `Tree` is a `Typed` */
given TypedTypeTest: TypeTest[Tree, Typed]

/** Tree representing a type ascription `x: T` in the source code */
type Typed <: Term
/** Tree representing a type ascription `x: T` in the source code.
*
* Also represents a pattern that contains a term `x`.
* Other `: T` patterns use the more general `TypedTree`.
*/
type Typed <: Term & TypedTree

/** Module object of `type Typed` */
val Typed: TypedModule
Expand Down Expand Up @@ -2049,6 +2054,56 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>

// ----- Patterns ------------------------------------------------

/** Pattern representing a `_` wildcard. */
type WildcardPattern <: Tree

/** `TypeTest` that allows testing at runtime in a pattern match if a `Tree` is a `WildcardPattern` */
given WildcardPatternTypeTest: TypeTest[Tree, WildcardPattern]

/** Module object of `type WildcardPattern` */
val WildcardPattern: WildcardPatternModule

/** Methods of the module object `val WildcardPattern` */
trait WildcardPatternModule { this: WildcardPattern.type =>
def apply(): WildcardPattern
def unapply(pattern: WildcardPattern): true
}

/** `TypeTest` that allows testing at runtime in a pattern match if a `Tree` is a `TypedTree` */
given TypedTreeTypeTest: TypeTest[Tree, TypedTree]

/** Tree representing a type ascription or pattern `x: T` in the source code
*
* The tree `x` may contain a `Constant`, `Ref`, `Wildcard`, `Bind`, `Unapply` or `Alternatives`.
*/
type TypedTree <: Tree

/** Module object of `type TypedTree` */
val TypedTree: TypedTreeModule

/** Methods of the module object `val TypedTree` */
trait TypedTreeModule { this: TypedTree.type =>

/** Create a type ascription `<x: Tree>: <tpt: TypeTree>` */
def apply(expr: Tree, tpt: TypeTree): TypedTree

def copy(original: Tree)(expr: Tree, tpt: TypeTree): TypedTree

/** Matches `<expr: Tree>: <tpt: TypeTree>` */
def unapply(x: TypedTree): (Tree, TypeTree)
}

/** Makes extension methods on `TypedTree` available without any imports */
given TypedTreeMethods: TypedTreeMethods

/** Extension methods of `TypedTree` */
trait TypedTreeMethods:
extension (self: TypedTree)
def tree: Tree
def tpt: TypeTree
end extension
end TypedTreeMethods

/** Pattern representing a `_ @ _` binding. */
type Bind <: Tree

Expand Down Expand Up @@ -4341,9 +4396,11 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
case TypeBoundsTree(lo, hi) => foldTree(foldTree(x, lo)(owner), hi)(owner)
case CaseDef(pat, guard, body) => foldTree(foldTrees(foldTree(x, pat)(owner), guard)(owner), body)(owner)
case TypeCaseDef(pat, body) => foldTree(foldTree(x, pat)(owner), body)(owner)
case WildcardPattern() => x
case Bind(_, body) => foldTree(x, body)(owner)
case Unapply(fun, implicits, patterns) => foldTrees(foldTrees(foldTree(x, fun)(owner), implicits)(owner), patterns)(owner)
case Alternatives(patterns) => foldTrees(x, patterns)(owner)
case TypedTree(tree1, tpt) => foldTree(foldTree(x, tree1)(owner), tpt)(owner)
}
}
end TreeAccumulator
Expand Down Expand Up @@ -4401,12 +4458,15 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
transformCaseDef(tree)(owner)
case tree: TypeCaseDef =>
transformTypeCaseDef(tree)(owner)
case WildcardPattern() => tree
case pattern: Bind =>
Bind.copy(pattern)(pattern.name, pattern.pattern)
case pattern: Unapply =>
Unapply.copy(pattern)(transformTerm(pattern.fun)(owner), transformSubTrees(pattern.implicits)(owner), transformTrees(pattern.patterns)(owner))
case pattern: Alternatives =>
Alternatives.copy(pattern)(transformTrees(pattern.patterns)(owner))
case TypedTree(expr, tpt) =>
TypedTree.copy(tree)(transformTree(expr)(owner), transformTypeTree(tpt)(owner))
}
}

Expand Down Expand Up @@ -4457,7 +4517,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
case New(tpt) =>
New.copy(tree)(transformTypeTree(tpt)(owner))
case Typed(expr, tpt) =>
Typed.copy(tree)(/*FIXME #12222: transformTerm(expr)(owner)*/transformTree(expr)(owner).asInstanceOf[Term], transformTypeTree(tpt)(owner))
Typed.copy(tree)(transformTerm(expr)(owner), transformTypeTree(tpt)(owner))
case tree: NamedArg =>
NamedArg.copy(tree)(tree.name, transformTerm(tree.value)(owner))
case Assign(lhs, rhs) =>
Expand Down
5 changes: 5 additions & 0 deletions project/MiMaFilters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@ object MiMaFilters {
exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SourceFileMethods.getJPath"),
exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SourceFileMethods.name"),
exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SourceFileMethods.path"),
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.WildcardPatternTypeTest"),
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.WildcardPattern"),
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.TypedTreeTypeTest"),
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.TypedTree"),
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.TypedTreeMethods"),
)
}
18 changes: 9 additions & 9 deletions tests/pos-macros/i11401/X_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class SLSelect[S]:

def fold[S](s0:S)(step: (S,SLSelect[S])=> S): S = {
???
}
}

def fold_async[S](s0:S)(step: (S,SLSelect[S])=> Future[S]): Future[S] = {
???
Expand All @@ -27,7 +27,7 @@ class SLSelect[S]:
await(s0.onRead(ch)(f).runAsync())

def runAsync(): Future[S] = ???



object X:
Expand All @@ -36,30 +36,30 @@ object X:
processImpl[T]('f)
}

def processImpl[T:Type](t:Expr[T])(using Quotes):Expr[Future[T]] =
def processImpl[T:Type](t:Expr[T])(using Quotes):Expr[Future[T]] =
import quotes.reflect._
val r = processTree[T](t.asTerm)
r.asExprOf[Future[T]]

def processTree[T:Type](using Quotes)(t: quotes.reflect.Term):quotes.reflect.Term =

def processTree[T:Type](using Quotes)(t: quotes.reflect.Term):quotes.reflect.Term =
import quotes.reflect._
val r: Term = t match
case Inlined(_,List(),body) => processTree(body)
case Inlined(d,bindings,body) =>
case Inlined(d,bindings,body) =>
Inlined(d,bindings,processTree[T](body))
case Block(stats,expr) => Block(stats,processTree(expr))
case Apply(Apply(TypeApply(Select(x,"fold"),targs),List(state)),List(fun)) =>
val nFun = processLambda[T](fun)
val nFun = processLambda[T](fun)
Apply(Apply(TypeApply(Select.unique(x,"fold_async"),targs),List(state)),List(nFun))
case Apply(TypeApply(Ident("await"),targs),List(body)) => body
case Typed(x,tp) => Typed(processTree(x), Inferred(TypeRepr.of[Future].appliedTo(tp.tpe)) )
case _ => throw new RuntimeException(s"tree not recoginized: $t")
val checker = new TreeMap() {}
checker.transformTerm(r)(Symbol.spliceOwner)
r
def processLambda[T:Type](using Quotes)(fun: quotes.reflect.Term):quotes.reflect.Term =

def processLambda[T:Type](using Quotes)(fun: quotes.reflect.Term):quotes.reflect.Term =
import quotes.reflect._

def changeArgs(oldArgs:List[Tree], newArgs:List[Tree], body:Term, owner: Symbol):Term =
Expand Down
13 changes: 13 additions & 0 deletions tests/pos-macros/i12188b/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import scala.quoted.*

object MatchTest {
inline def test[T](inline obj: Any): Unit = ${testImpl('obj)}

def testImpl[T](objExpr: Expr[T])(using Quotes): Expr[Unit] = {
import quotes.reflect.*
// test that the extractors work
val Inlined(None, Nil, Block(Nil, Match(param @ Ident("a"), List(CaseDef(Literal(IntConstant(1)), None, Block(Nil, Literal(UnitConstant()))), CaseDef(WildcardPattern(), None, Block(Nil, Literal(UnitConstant()))))))) = objExpr.asTerm
// test that the constructors work
Block(Nil, Match(param, List(CaseDef(Literal(IntConstant(1)), None, Block(Nil, Literal(UnitConstant()))), CaseDef(WildcardPattern(), None, Block(Nil, Literal(UnitConstant())))))).asExprOf[Unit]
}
}
Loading