Skip to content
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

Introduce MethodTypeKind to quotes reflection API #20249

Merged
merged 2 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
11 changes: 11 additions & 0 deletions compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2204,6 +2204,13 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
case _ => None
end MethodOrPolyTypeTest

type MethodTypeKind = dotc.core.Types.MethodTypeCompanion

object MethodTypeKind extends MethodTypeKindModule:
val Plain: MethodTypeKind = Types.MethodType
val Contextual: MethodTypeKind = Types.ContextualMethodType
val Implicit: MethodTypeKind = Types.ImplicitMethodType

type MethodType = dotc.core.Types.MethodType

object MethodTypeTypeTest extends TypeTest[TypeRepr, MethodType]:
Expand All @@ -2215,6 +2222,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
object MethodType extends MethodTypeModule:
def apply(paramNames: List[String])(paramInfosExp: MethodType => List[TypeRepr], resultTypeExp: MethodType => TypeRepr): MethodType =
Types.MethodType(paramNames.map(_.toTermName))(paramInfosExp, resultTypeExp)
def apply(kind: MethodTypeKind)(paramNames: List[String])(paramInfosExp: MethodType => List[TypeRepr], resultTypeExp: MethodType => TypeRepr): MethodType =
kind.apply(paramNames.map(_.toTermName))(paramInfosExp, resultTypeExp)
def unapply(x: MethodType): (List[String], List[TypeRepr], TypeRepr) =
(x.paramNames.map(_.toString), x.paramTypes, x.resType)
end MethodType
Expand All @@ -2223,6 +2232,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
extension (self: MethodType)
def isErased: Boolean = false
def isImplicit: Boolean = self.isImplicitMethod
def isContextual: Boolean = self.isContextualMethod
nicolasstucki marked this conversation as resolved.
Show resolved Hide resolved
def methodTypeKind: MethodTypeKind = self.companion
def param(idx: Int): TypeRepr = self.newParamRef(idx)

def erasedParams: List[Boolean] = self.erasedParams
Expand Down
25 changes: 24 additions & 1 deletion library/src/scala/quoted/Quotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
* +- MatchCase
* +- TypeBounds
* +- NoPrefix
*
* +- MethodTypeKind
*
* +- Selector -+- SimpleSelector
* +- RenameSelector
Expand Down Expand Up @@ -3234,6 +3236,22 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
/** `TypeTest` that allows testing at runtime in a pattern match if a `TypeRepr` is a `MethodOrPoly` */
given MethodOrPolyTypeTest: TypeTest[TypeRepr, MethodOrPoly]

/** Type which decides on the kind of parameter list represented by `MethodType`. */
type MethodTypeKind
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered making this a concrete, static enum instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, it could work.


/** Module object of `type MethodKind` */
val MethodTypeKind: MethodTypeKindModule

/** Methods of the module object `val MethodKind` */
trait MethodTypeKindModule { this: MethodTypeKind.type =>
/** Represents a parameter list without any implicitness of parameters, like (x1: X1, x2: X2, ...) */
val Plain: MethodTypeKind
/** Represents a parameter list with implicit parameters, like `(implicit X1, ..., Xn)`, `(using X1, ..., Xn)`, `(using x1: X1, ..., xn: Xn)` */
val Implicit: MethodTypeKind
/** Represents a parameter list of a contextual method, like `(using X1, ..., Xn)` or `(using x1: X1, ..., xn: Xn)` */
val Contextual: MethodTypeKind
}

/** Type of the definition of a method taking a single list of parameters. It's return type may be a MethodType. */
type MethodType <: MethodOrPoly

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

Expand All @@ -3255,8 +3274,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
/** Extension methods of `MethodType` */
trait MethodTypeMethods:
extension (self: MethodType)
/** Is this the type of using parameter clause `(implicit X1, ..., Xn)`, `(using X1, ..., Xn)` or `(using x1: X1, ..., xn: Xn)` */
/** Is this the type of parameter clause like `(implicit X1, ..., Xn)`, `(using X1, ..., Xn)` or `(using x1: X1, ..., xn: Xn)` */
def isImplicit: Boolean
/** Is this the type of parameter clause like `(using X1, ..., Xn)` or `(using x1: X1, x2: X2, ... )` */
def isContextual: Boolean
/** Returns a MethodTypeKind object representing the implicitness of the MethodType parameter clause. */
def methodTypeKind: MethodTypeKind
/** Is this the type of erased parameter clause `(erased x1: X1, ..., xn: Xn)` */
@deprecated("Use `hasErasedParams` and `erasedParams`", "3.4")
def isErased: Boolean
Expand Down
7 changes: 6 additions & 1 deletion project/MiMaFilters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ object MiMaFilters {
// Breaking changes since last reference version
Build.mimaPreviousDottyVersion -> // Seq.empty, // We should never break backwards compatibility
Seq(
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolMethods.isSuperAccessor"), // This change is acceptable. See comment in `Breaking changes since last LTS`.
// `ReversedMissingMethodProblem`s are acceptable. See comment in `Breaking changes since last LTS`.
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolMethods.isSuperAccessor"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.MethodTypeKind"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#MethodTypeModule.apply"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#MethodTypeMethods.methodTypeKind"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#MethodTypeMethods.isContextual"),
),

// Breaking changes since last LTS
Expand Down
64 changes: 64 additions & 0 deletions tests/run-macros/reflect-method-type-kind/macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
trait Foo
trait Bar

object Methods:
def implicitMethod(implicit foo: Foo, int: Int): Bar = ???
def contextualMethod(using foo: Foo, int: Int): Bar = ???
def plainMethod(foo: Foo, int: Int): Bar = ???

object Macro:
import scala.quoted._
inline def macroCall(): Unit = ${ macroCallImpl }
def macroCallImpl(using Quotes): Expr[Unit] =
testReadingMethodTypeKind
testCreatingMethodTypeKind
'{()}

def testReadingMethodTypeKind(using Quotes) =
import quotes.reflect._
def getFromMethods(name: String): TypeRepr =
val typeRepr = TypeRepr.of[Methods.type]
val symbol =
typeRepr.typeSymbol.methodMember(name).headOption.getOrElse(
typeRepr.typeSymbol.fieldMember(name)
)
typeRepr.memberType(symbol)

assert(getFromMethods("implicitMethod").asInstanceOf[MethodType].isImplicit)
assert(!getFromMethods("implicitMethod").asInstanceOf[MethodType].isContextual)
assert(getFromMethods("implicitMethod").asInstanceOf[MethodType].methodTypeKind == MethodTypeKind.Implicit)

assert(getFromMethods("contextualMethod").asInstanceOf[MethodType].isImplicit)
assert(getFromMethods("contextualMethod").asInstanceOf[MethodType].isContextual)
assert(getFromMethods("contextualMethod").asInstanceOf[MethodType].methodTypeKind == MethodTypeKind.Contextual)

assert(!getFromMethods("plainMethod").asInstanceOf[MethodType].isImplicit)
assert(!getFromMethods("plainMethod").asInstanceOf[MethodType].isContextual)
assert(getFromMethods("plainMethod").asInstanceOf[MethodType].methodTypeKind == MethodTypeKind.Plain)


def testCreatingMethodTypeKind(using Quotes) =
import quotes.reflect._
val paramTypes = List(TypeRepr.of[Foo], TypeRepr.of[Int])
val resType = TypeRepr.of[Bar]
val implicitMethodType = MethodType.apply(MethodTypeKind.Implicit)(List("foo", "int"))(mt => paramTypes, mt => resType)
assert(implicitMethodType.isImplicit)
assert(!implicitMethodType.isContextual)
assert(implicitMethodType.methodTypeKind == MethodTypeKind.Implicit)
assert(implicitMethodType.methodTypeKind != MethodTypeKind.Contextual)
assert(implicitMethodType.methodTypeKind != MethodTypeKind.Plain)


val contextualMethodType = MethodType.apply(MethodTypeKind.Contextual)(List("foo", "int"))(mt => paramTypes, mt => resType)
assert(contextualMethodType.isImplicit)
assert(contextualMethodType.isContextual)
assert(contextualMethodType.methodTypeKind != MethodTypeKind.Implicit)
assert(contextualMethodType.methodTypeKind == MethodTypeKind.Contextual)
assert(contextualMethodType.methodTypeKind != MethodTypeKind.Plain)

val plainMethodType = MethodType.apply(MethodTypeKind.Plain)(List("foo", "int"))(mt => paramTypes, mt => resType)
assert(!plainMethodType.isContextual)
assert(!plainMethodType.isImplicit)
assert(plainMethodType.methodTypeKind != MethodTypeKind.Implicit)
assert(plainMethodType.methodTypeKind != MethodTypeKind.Contextual)
assert(plainMethodType.methodTypeKind == MethodTypeKind.Plain)
3 changes: 3 additions & 0 deletions tests/run-macros/reflect-method-type-kind/test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object Test:
def main(args: Array[String]): Unit =
Macro.macroCall()
Loading