From a207caae1d1ef7e038eb56991eacf5da8c7f061b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 10 Dec 2020 10:22:29 +0100 Subject: [PATCH] Add reflect MatchCase TypeRepr This represents the `MATCHCASEtype` type in `TastyFormat` and hides the `scala.runtime.MatchCase` internal encoding. --- .../quoted/runtime/impl/QuotesImpl.scala | 23 +++++++++++- library/src/scala/quoted/Quotes.scala | 35 +++++++++++++++++++ .../dotty/dokka/tasty/SyntheticSupport.scala | 10 ------ .../src/dotty/dokka/tasty/TypesSupport.scala | 4 ++- .../tasty-construct-types/Macro_1.scala | 9 +++-- 5 files changed, 67 insertions(+), 14 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index a9348af4f9ae..119c8a0ff2f1 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -1760,7 +1760,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object AppliedTypeTypeTest extends TypeTest[TypeRepr, AppliedType]: def unapply(x: TypeRepr): Option[AppliedType & x.type] = x match - case tpe: (Types.AppliedType & x.type) => Some(tpe) + case tpe: (Types.AppliedType & x.type) if !tpe.tycon.isRef(dotc.core.Symbols.defn.MatchCaseClass) => Some(tpe) case _ => None end AppliedTypeTypeTest @@ -2051,6 +2051,27 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler end extension end TypeLambdaMethods + type MatchCase = dotc.core.Types.AppliedType + + given MatchCaseTypeTest: TypeTest[TypeRepr, MatchCase] with + def unapply(x: TypeRepr): Option[MatchCase & x.type] = x match + case x: (Types.AppliedType & x.type) if x.tycon.isRef(dotc.core.Symbols.defn.MatchCaseClass) => Some(x) + case _ => None + end MatchCaseTypeTest + + object MatchCase extends MatchCaseModule: + def apply(pattern: TypeRepr, rhs: TypeRepr): MatchCase = + Types.AppliedType(dotc.core.Symbols.defn.MatchCaseClass.typeRef, List(pattern, rhs)) + def unapply(x: MatchCase): (TypeRepr, TypeRepr) = (x.pattern, x.rhs) + end MatchCase + + given MatchCaseMethods: MatchCaseMethods with + extension (self: MatchCase) + def pattern: TypeRepr = self.args(0) + def rhs: TypeRepr = self.args(1) + end extension + end MatchCaseMethods + type TypeBounds = dotc.core.Types.TypeBounds object TypeBoundsTypeTest extends TypeTest[TypeRepr, TypeBounds]: diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 21e32ea78348..6713950c956d 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -175,6 +175,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * +- LambdaType -+- MethodOrPoly -+- MethodType * | | +- PolyType * | +- TypeLambda + * +- MatchCase * +- TypeBounds * +- NoPrefix * @@ -2678,6 +2679,40 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => end extension end TypeLambdaMethods + /** Case of a `MatchType` containing pattern `case P => R`. + * + * Note: cases with type bindings are represented nested in a `TypeLambda`. + */ + type MatchCase <: TypeRepr + + /** `TypeTest` that allows testing at runtime in a pattern match if a `TypeRepr` is a `MatchCase` */ + given MatchCaseTypeTest: TypeTest[TypeRepr, MatchCase] + + /** Module object of `type MatchCase` */ + val MatchCase: MatchCaseModule + + /** Methods of the module object `val MatchCase` */ + trait MatchCaseModule { this: MatchCase.type => + /* Create match type case `case => ` */ + def apply(pattern: TypeRepr, rhs: TypeRepr): MatchCase + /* Matches a match type case `case => ` */ + def unapply(x: MatchCase): (TypeRepr, TypeRepr) + } + + /** Makes extension methods on `MatchCase` available without any imports */ + given MatchCaseMethods: MatchCaseMethods + + /** Extension methods of `MatchCase` */ + trait MatchCaseMethods: + extension (self: MatchCase) + /** Pattern `P` of `case P => R` in a `MatchType` */ + def pattern: TypeRepr + /** RHS `R` of `case P => R` in a `MatchType` */ + def rhs: TypeRepr + end extension + end MatchCaseMethods + + // ----- TypeBounds ----------------------------------------------- /** Type bounds */ diff --git a/scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala b/scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala index 9073a31d3943..d2a0dc6c58e2 100644 --- a/scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala @@ -96,13 +96,3 @@ trait SyntheticsSupport: given dotc.core.Contexts.Context = qctx.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx val cSym = c.symbol.asInstanceOf[dotc.core.Symbols.Symbol] cSym.typeRef.appliedTo(cSym.typeParams.map(_.typeRef)).asInstanceOf[TypeRepr] - - object MatchTypeCase: - def unapply(tpe: TypeRepr): Option[(TypeRepr, TypeRepr)] = - tpe match - case AppliedType(t, Seq(from, to)) /*if t == MatchCaseType*/ => - Some((from, to)) - case TypeLambda(paramNames, paramTypes, AppliedType(t, Seq(from, to))) /*if t == MatchCaseType*/ => - Some((from, to)) - case _ => - None diff --git a/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala b/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala index 72adc698fa18..55f8a5005fe3 100644 --- a/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala @@ -240,7 +240,9 @@ trait TypesSupport: case MatchType(bond, sc, cases) => val casesTexts = cases.flatMap { - case MatchTypeCase(from, to) => + case MatchCase(from, to) => + texts(" case ") ++ inner(from) ++ texts(" => ") ++ inner(to) ++ texts("\n") + case TypeLambda(_, _, MatchCase(from, to)) => texts(" case ") ++ inner(from) ++ texts(" => ") ++ inner(to) ++ texts("\n") } inner(sc) ++ texts(" match {\n") ++ casesTexts ++ texts("}") diff --git a/tests/run-macros/tasty-construct-types/Macro_1.scala b/tests/run-macros/tasty-construct-types/Macro_1.scala index f883a6e2ba8b..7e09529f1c3d 100644 --- a/tests/run-macros/tasty-construct-types/Macro_1.scala +++ b/tests/run-macros/tasty-construct-types/Macro_1.scala @@ -36,7 +36,9 @@ object Macros { TypeLambda( List("t"), _ => List(TypeBounds(TypeRepr.of[Nothing], TypeRepr.of[Any])), - tl => TypeRepr.of[scala.runtime.MatchCase].appliedTo(List(TypeRepr.of[List].appliedTo(tl.param(0)), tl.param(0))))) + tl => MatchCase(TypeRepr.of[List].appliedTo(tl.param(0)), tl.param(0))), + MatchCase(TypeRepr.of[Int], TypeRepr.of[Int]) + ) ) assert(x1T =:= TypeRepr.of[1]) @@ -46,7 +48,10 @@ object Macros { assert(x5T =:= TypeRepr.of[RefineMe { type T = Int }]) assert(x6T =:= TypeRepr.of[List[Int]]) assert(x7T =:= TypeRepr.of[7 @TestAnnotation]) - assert(x8T =:= TypeRepr.of[List[8] match { case List[t] => t }]) + assert(x8T =:= TypeRepr.of[List[8] match { + case List[t] => t + case Int => Int + }]) '{ println("Ok")