Skip to content

Commit 00802ab

Browse files
Introduce MethodTypeKind to quotes reflection API (#20249)
It allows to create Contextual and Implicit MethodTypes. MethodTypeKind abstracts away the `MethodTypeCompanion` implementation into a simple enum style choice for a newly added MethodType apply method. The MethodType unapply is kept as it was for source compatibility, instead users are encouraged to use `isImplicit` and `isContextual` methods. Based on #18499 Fixes #18477
2 parents a0513b0 + 4d7e6ad commit 00802ab

File tree

5 files changed

+104
-2
lines changed

5 files changed

+104
-2
lines changed

Diff for: compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

+12
Original file line numberDiff line numberDiff line change
@@ -2215,6 +2215,12 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
22152215
object MethodType extends MethodTypeModule:
22162216
def apply(paramNames: List[String])(paramInfosExp: MethodType => List[TypeRepr], resultTypeExp: MethodType => TypeRepr): MethodType =
22172217
Types.MethodType(paramNames.map(_.toTermName))(paramInfosExp, resultTypeExp)
2218+
def apply(kind: MethodTypeKind)(paramNames: List[String])(paramInfosExp: MethodType => List[TypeRepr], resultTypeExp: MethodType => TypeRepr): MethodType =
2219+
val companion = kind match
2220+
case MethodTypeKind.Contextual => Types.ContextualMethodType
2221+
case MethodTypeKind.Implicit => Types.ImplicitMethodType
2222+
case MethodTypeKind.Plain => Types.MethodType
2223+
companion.apply(paramNames.map(_.toTermName))(paramInfosExp, resultTypeExp)
22182224
def unapply(x: MethodType): (List[String], List[TypeRepr], TypeRepr) =
22192225
(x.paramNames.map(_.toString), x.paramTypes, x.resType)
22202226
end MethodType
@@ -2223,6 +2229,12 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
22232229
extension (self: MethodType)
22242230
def isErased: Boolean = false
22252231
def isImplicit: Boolean = self.isImplicitMethod
2232+
def isContextual: Boolean = self.isContextualMethod
2233+
def methodTypeKind: MethodTypeKind =
2234+
self.companion match
2235+
case Types.ContextualMethodType => MethodTypeKind.Contextual
2236+
case Types.ImplicitMethodType => MethodTypeKind.Implicit
2237+
case _ => MethodTypeKind.Plain
22262238
def param(idx: Int): TypeRepr = self.newParamRef(idx)
22272239

22282240
def erasedParams: List[Boolean] = self.erasedParams

Diff for: library/src/scala/quoted/Quotes.scala

+19-1
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
211211
* +- MatchCase
212212
* +- TypeBounds
213213
* +- NoPrefix
214+
*
215+
* +- MethodTypeKind -+- Contextual
216+
* +- Implicit
217+
* +- Plain
214218
*
215219
* +- Selector -+- SimpleSelector
216220
* +- RenameSelector
@@ -3234,6 +3238,15 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
32343238
/** `TypeTest` that allows testing at runtime in a pattern match if a `TypeRepr` is a `MethodOrPoly` */
32353239
given MethodOrPolyTypeTest: TypeTest[TypeRepr, MethodOrPoly]
32363240

3241+
/** Type which decides on the kind of parameter list represented by `MethodType`. */
3242+
enum MethodTypeKind:
3243+
/** Represents a parameter list without any implicitness of parameters, like (x1: X1, x2: X2, ...) */
3244+
case Plain
3245+
/** Represents a parameter list with implicit parameters, like `(implicit X1, ..., Xn)`, `(using X1, ..., Xn)`, `(using x1: X1, ..., xn: Xn)` */
3246+
case Implicit
3247+
/** Represents a parameter list of a contextual method, like `(using X1, ..., Xn)` or `(using x1: X1, ..., xn: Xn)` */
3248+
case Contextual
3249+
32373250
/** Type of the definition of a method taking a single list of parameters. It's return type may be a MethodType. */
32383251
type MethodType <: MethodOrPoly
32393252

@@ -3246,6 +3259,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
32463259
/** Methods of the module object `val MethodType` */
32473260
trait MethodTypeModule { this: MethodType.type =>
32483261
def apply(paramNames: List[String])(paramInfosExp: MethodType => List[TypeRepr], resultTypeExp: MethodType => TypeRepr): MethodType
3262+
def apply(kind: MethodTypeKind)(paramNames: List[String])(paramInfosExp: MethodType => List[TypeRepr], resultTypeExp: MethodType => TypeRepr): MethodType
32493263
def unapply(x: MethodType): (List[String], List[TypeRepr], TypeRepr)
32503264
}
32513265

@@ -3255,8 +3269,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
32553269
/** Extension methods of `MethodType` */
32563270
trait MethodTypeMethods:
32573271
extension (self: MethodType)
3258-
/** Is this the type of using parameter clause `(implicit X1, ..., Xn)`, `(using X1, ..., Xn)` or `(using x1: X1, ..., xn: Xn)` */
3272+
/** Is this the type of parameter clause like `(implicit X1, ..., Xn)`, `(using X1, ..., Xn)` or `(using x1: X1, ..., xn: Xn)` */
32593273
def isImplicit: Boolean
3274+
/** Is this the type of parameter clause like `(using X1, ..., Xn)` or `(using x1: X1, x2: X2, ... )` */
3275+
def isContextual: Boolean
3276+
/** Returns a MethodTypeKind object representing the implicitness of the MethodType parameter clause. */
3277+
def methodTypeKind: MethodTypeKind
32603278
/** Is this the type of erased parameter clause `(erased x1: X1, ..., xn: Xn)` */
32613279
@deprecated("Use `hasErasedParams` and `erasedParams`", "3.4")
32623280
def isErased: Boolean

Diff for: project/MiMaFilters.scala

+6-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,12 @@ object MiMaFilters {
5050
// Breaking changes since last reference version
5151
Build.mimaPreviousDottyVersion -> // Seq.empty, // We should never break backwards compatibility
5252
Seq(
53-
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolMethods.isSuperAccessor"), // This change is acceptable. See comment in `Breaking changes since last LTS`.
53+
// `ReversedMissingMethodProblem`s are acceptable. See comment in `Breaking changes since last LTS`.
54+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolMethods.isSuperAccessor"),
55+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.MethodTypeKind"),
56+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#MethodTypeModule.apply"),
57+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#MethodTypeMethods.methodTypeKind"),
58+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#MethodTypeMethods.isContextual"),
5459
),
5560

5661
// Breaking changes since last LTS
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
trait Foo
2+
trait Bar
3+
4+
object Methods:
5+
def implicitMethod(implicit foo: Foo, int: Int): Bar = ???
6+
def contextualMethod(using foo: Foo, int: Int): Bar = ???
7+
def plainMethod(foo: Foo, int: Int): Bar = ???
8+
9+
object Macro:
10+
import scala.quoted._
11+
inline def macroCall(): Unit = ${ macroCallImpl }
12+
def macroCallImpl(using Quotes): Expr[Unit] =
13+
testReadingMethodTypeKind
14+
testCreatingMethodTypeKind
15+
'{()}
16+
17+
def testReadingMethodTypeKind(using Quotes) =
18+
import quotes.reflect._
19+
def getFromMethods(name: String): TypeRepr =
20+
val typeRepr = TypeRepr.of[Methods.type]
21+
val symbol =
22+
typeRepr.typeSymbol.methodMember(name).headOption.getOrElse(
23+
typeRepr.typeSymbol.fieldMember(name)
24+
)
25+
typeRepr.memberType(symbol)
26+
27+
assert(getFromMethods("implicitMethod").asInstanceOf[MethodType].isImplicit)
28+
assert(!getFromMethods("implicitMethod").asInstanceOf[MethodType].isContextual)
29+
assert(getFromMethods("implicitMethod").asInstanceOf[MethodType].methodTypeKind == MethodTypeKind.Implicit)
30+
31+
assert(getFromMethods("contextualMethod").asInstanceOf[MethodType].isImplicit)
32+
assert(getFromMethods("contextualMethod").asInstanceOf[MethodType].isContextual)
33+
assert(getFromMethods("contextualMethod").asInstanceOf[MethodType].methodTypeKind == MethodTypeKind.Contextual)
34+
35+
assert(!getFromMethods("plainMethod").asInstanceOf[MethodType].isImplicit)
36+
assert(!getFromMethods("plainMethod").asInstanceOf[MethodType].isContextual)
37+
assert(getFromMethods("plainMethod").asInstanceOf[MethodType].methodTypeKind == MethodTypeKind.Plain)
38+
39+
40+
def testCreatingMethodTypeKind(using Quotes) =
41+
import quotes.reflect._
42+
val paramTypes = List(TypeRepr.of[Foo], TypeRepr.of[Int])
43+
val resType = TypeRepr.of[Bar]
44+
val implicitMethodType = MethodType.apply(MethodTypeKind.Implicit)(List("foo", "int"))(mt => paramTypes, mt => resType)
45+
assert(implicitMethodType.isImplicit)
46+
assert(!implicitMethodType.isContextual)
47+
assert(implicitMethodType.methodTypeKind == MethodTypeKind.Implicit)
48+
assert(implicitMethodType.methodTypeKind != MethodTypeKind.Contextual)
49+
assert(implicitMethodType.methodTypeKind != MethodTypeKind.Plain)
50+
51+
52+
val contextualMethodType = MethodType.apply(MethodTypeKind.Contextual)(List("foo", "int"))(mt => paramTypes, mt => resType)
53+
assert(contextualMethodType.isImplicit)
54+
assert(contextualMethodType.isContextual)
55+
assert(contextualMethodType.methodTypeKind != MethodTypeKind.Implicit)
56+
assert(contextualMethodType.methodTypeKind == MethodTypeKind.Contextual)
57+
assert(contextualMethodType.methodTypeKind != MethodTypeKind.Plain)
58+
59+
val plainMethodType = MethodType.apply(MethodTypeKind.Plain)(List("foo", "int"))(mt => paramTypes, mt => resType)
60+
assert(!plainMethodType.isContextual)
61+
assert(!plainMethodType.isImplicit)
62+
assert(plainMethodType.methodTypeKind != MethodTypeKind.Implicit)
63+
assert(plainMethodType.methodTypeKind != MethodTypeKind.Contextual)
64+
assert(plainMethodType.methodTypeKind == MethodTypeKind.Plain)
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object Test:
2+
def main(args: Array[String]): Unit =
3+
Macro.macroCall()

0 commit comments

Comments
 (0)