Skip to content

Commit

Permalink
Add TypedOrTest as super type of Typed in reflection
Browse files Browse the repository at this point in the history
TypeOrTest can match or construct type tests or ascriptions `x: T` in expressions or patterns.
Unlike `Typed`, it contains a `Tree` instead of a `Term` which might be one of the patterns trees.

Fixes #12222
  • Loading branch information
nicolasstucki committed Aug 24, 2021
1 parent 2ea3a87 commit 0ad5be5
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 16 deletions.
32 changes: 31 additions & 1 deletion compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
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 x: (tpd.Typed & x.type) =>
TypedTypeTest.unapply(x) // Matches `Typed` but not `TypedOrTest`
case _ => if x.isTerm then Some(x) else None
end TermTypeTest

Expand Down Expand Up @@ -655,7 +657,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 All @@ -675,6 +680,31 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
end extension
end TypedMethods

type TypedOrTest = tpd.Typed

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

object TypedOrTest extends TypedOrTestModule:
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 TypedOrTest

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


type Assign = tpd.Assign

object AssignTypeTest extends TypeTest[Tree, Assign]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ object Extractors {
this += "Unapply(" += fun += ", " ++= implicits += ", " ++= patterns += ")"
case Alternatives(patterns) =>
this += "Alternatives(" ++= patterns += ")"
case TypedOrTest(tree, tpt) =>
this += "TypedOrTest(" += tree += ", " += tpt += ")"
}

def visitConstant(x: Constant): this.type = x match {
Expand Down
10 changes: 7 additions & 3 deletions compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -928,9 +928,13 @@ object SourceCode {
case Alternatives(trees) =>
inParens(printPatterns(trees, " | "))

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

case v: Term =>
printTree(v)
Expand Down
58 changes: 47 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,15 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
* | +- Inlined
* | +- SelectOuter
* | +- While
* | +---+- Typed
* | /
* +- TypedOrTest +----------------·
* +- Bind
* +- Unapply
* +- Alternatives
* |
* +- CaseDef
* +- TypeCaseDef
* |
* +- TypeTree ----+- Inferred
* | +- TypeIdent
Expand All @@ -164,13 +171,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
* |
* +- TypeBoundsTree
* +- WildcardTypeTree
* |
* +- CaseDef
* |
* +- TypeCaseDef
* +- Bind
* +- Unapply
* +- Alternatives
*
* +- ParamClause -+- TypeParamClause
* +- TermParamClause
Expand Down Expand Up @@ -1120,8 +1120,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 `TypedOrTest`.
*/
type Typed <: Term & TypedOrTest

/** Module object of `type Typed` */
val Typed: TypedModule
Expand Down Expand Up @@ -1568,6 +1572,38 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
end extension
end WhileMethods

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

/** Tree representing a type ascription or type test pattern `x: T` in the source code. */
type TypedOrTest <: Tree

/** Module object of `type TypedOrTest` */
val TypedOrTest: TypedOrTestModule

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

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

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

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

/** Makes extension methods on `TypedOrTest` available without any imports */
given TypedOrTestMethods: TypedOrTestMethods

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

// ----- TypeTrees ------------------------------------------------

/** Type tree representing a type written in the source */
Expand Down Expand Up @@ -4456,7 +4492,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
3 changes: 3 additions & 0 deletions project/MiMaFilters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ object MiMaFilters {
exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SourceFileMethods.path"),
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#UnapplyModule.apply"),
exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#UnapplyModule.apply"),
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.TypedOrTestTypeTest"),
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.TypedOrTest"),
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.TypedOrTestMethods"),
)
}
2 changes: 1 addition & 1 deletion tests/run-staging/i5161.check
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
run : Some(2)
show : scala.Tuple2.apply[scala.Option[scala.Int], scala.Option[scala.Int]](scala.Some.apply[scala.Int](1), scala.Some.apply[scala.Int](1)) match {
case scala.Tuple2((scala.Some(x): scala.Some[scala.Int]), (scala.Some(y): scala.Some[scala.Int])) =>
case scala.Tuple2(scala.Some(x), scala.Some(y)) =>
scala.Some.apply[scala.Int](x.+(y))
case _ =>
scala.None
Expand Down

0 comments on commit 0ad5be5

Please sign in to comment.