diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index c7f9b53c606d..350a011294e6 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -1881,6 +1881,47 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler end extension end MatchTypeMethods + type MatchTypeCase = dotc.core.Types.Type + + object MatchTypeCaseTypeTest extends TypeTest[TypeRepr, MatchTypeCase]: + def unapply(x: TypeRepr): Option[MatchTypeCase & x.type] = + MatchTypeCase.unapply(x.asInstanceOf[MatchTypeCase]).map(_ => x.asInstanceOf[MatchTypeCase & x.type]) + end MatchTypeCaseTypeTest + + object MatchTypeCase extends MatchTypeCaseModule: + def apply(pattern: TypeRepr, body: TypeRepr): MatchTypeCase = + Types.AppliedType(ctx.definitions.MatchCaseClass.typeRef, List(pattern, body)) + + def apply( + paramNames: List[String], + boundsFn: TypeLambda => List[TypeBounds], + patternFn: TypeLambda => TypeRepr, + bodyFn: TypeLambda => TypeRepr, + ): MatchTypeCase = + reflect.TypeLambda(paramNames, boundsFn, tl => MatchTypeCase(patternFn(tl), bodyFn(tl))) + + def unapply(x: MatchTypeCase): Option[(List[String], List[TypeBounds], TypeRepr, TypeRepr)] = x match + case AppliedType(tycon, Seq(from, to)) if tycon.isRef(ctx.definitions.MatchCaseClass) => + Some((Nil, Nil, from, to)) + case TypeLambda(paramNames, paramBounds, AppliedType(tycon, Seq(from, to))) if tycon.isRef(ctx.definitions.MatchCaseClass) => + Some((paramNames.map(_.toString), paramBounds, from, to)) + case _ => + None + end MatchTypeCase + + given MatchTypeCaseMethods: MatchTypeCaseMethods with + extension (self: MatchTypeCase): + def paramNames: List[String] = unwrap(self)(0) + def paramBounds: List[TypeBounds] = unwrap(self)(1) + def pattern: TypeRepr = unwrap(self)(2) + def body: TypeRepr = unwrap(self)(3) + end extension + + private def unwrap(x: MatchTypeCase) = + // should never throw within limits of the API + MatchTypeCase.unapply(x).get + end MatchTypeCaseMethods + type ByNameType = dotc.core.Types.ExprType object ByNameTypeTypeTest extends TypeTest[TypeRepr, ByNameType]: diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 387520dd0b72..f6420ac4c496 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -2442,7 +2442,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Methods of the module object `val MatchType` */ trait MatchTypeModule { this: MatchType.type => def apply(bound: TypeRepr, scrutinee: TypeRepr, cases: List[TypeRepr]): MatchType - def unapply(x: MatchType): Option[(TypeRepr, TypeRepr, List[TypeRepr])] + def unapply(x: MatchType): Option[(TypeRepr, TypeRepr, List[MatchTypeCase])] } /** Makes extension methods on `MatchType` available without any imports */ @@ -2453,10 +2453,46 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => extension (self: MatchType): def bound: TypeRepr def scrutinee: TypeRepr - def cases: List[TypeRepr] + def cases: List[MatchTypeCase] end extension end MatchTypeMethods + /** Case of a match type `case U => S`. */ + type MatchTypeCase <: TypeRepr + + /** `TypeTest` that allows testing at runtime in a pattern match if a `TypeRepr` is a `MatchTypeCase` */ + given MatchTypeCaseTypeTest: TypeTest[TypeRepr, MatchTypeCase] + + /** Module object of `case U => S` */ + val MatchTypeCase: MatchTypeCaseModule + + /** Methods of the module object of `case U => S` */ + trait MatchTypeCaseModule { this: MatchTypeCase.type => + def apply(pattern: TypeRepr, body: TypeRepr): MatchTypeCase + + def apply( + paramNames: List[String], + boundsFn: TypeLambda => List[TypeBounds], + patternFn: TypeLambda => TypeRepr, + bodyFn: TypeLambda => TypeRepr, + ): MatchTypeCase + + def unapply(x: MatchTypeCase): Option[(List[String], List[TypeBounds], TypeRepr, TypeRepr)] + } + + /** Makes extension methods on `MatchTypeCase` available without any imports */ + given MatchTypeCaseMethods: MatchTypeCaseMethods + + /** Extension methods of `MatchTypeCase` */ + trait MatchTypeCaseMethods: + extension (self: MatchTypeCase): + def paramNames: List[String] + def paramBounds: List[TypeBounds] + def pattern: TypeRepr + def body: TypeRepr + end extension + end MatchTypeCaseMethods + /** Type of a by by name parameter */ type ByNameType <: TypeRepr diff --git a/scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala b/scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala index 0924c62f54a0..ad81476d7179 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 a25fc15ec2f3..70f3a4f35bf0 100644 --- a/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala @@ -240,7 +240,7 @@ trait TypesSupport: case MatchType(bond, sc, cases) => val casesTexts = cases.flatMap { - case MatchTypeCase(from, to) => + case MatchTypeCase(_, _, 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 70f2f753ec65..c7455fc6e668 100644 --- a/tests/run-macros/tasty-construct-types/Macro_1.scala +++ b/tests/run-macros/tasty-construct-types/Macro_1.scala @@ -33,10 +33,17 @@ object Macros { TypeRepr.of[Int], TypeRepr.of[List[8]], List( - TypeLambda( + MatchTypeCase( 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 => TypeRepr.of[List].appliedTo(tl.param(0)), + tl => tl.param(0), + ), + MatchTypeCase( + TypeRepr.of[Int], + TypeRepr.of[Int], + ) + ) ) assert(x1T =:= TypeRepr.of[1]) @@ -46,7 +53,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")