Skip to content

Fix #6585: implement Type.isFunctionType in tasty reflect API #6586

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

Merged
merged 2 commits into from
Jun 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 14 additions & 0 deletions compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,20 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util.
def Type_derivesFrom(self: Type)(cls: ClassDefSymbol)(implicit ctx: Context): Boolean =
self.derivesFrom(cls)

def Type_isFunctionType(self: Type)(implicit ctx: Context): Boolean =
defn.isFunctionType(self)

def Type_isImplicitFunctionType(self: Type)(implicit ctx: Context): Boolean =
defn.isImplicitFunctionType(self)

def Type_isErasedFunctionType(self: Type)(implicit ctx: Context): Boolean =
defn.isErasedFunctionType(self)

def Type_isDependentFunctionType(self: Type)(implicit ctx: Context): Boolean = {
val tpNoRefinement = self.dropDependentRefinement
tpNoRefinement != self && defn.isNonRefinedFunction(tpNoRefinement)
}

type ConstantType = Types.ConstantType

def matchConstantType(tpe: TypeOrBounds)(implicit ctx: Context): Option[ConstantType] = tpe match {
Expand Down
30 changes: 30 additions & 0 deletions library/src/scala/tasty/reflect/Kernel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,36 @@ trait Kernel {
/** Is this type an instance of a non-bottom subclass of the given class `cls`? */
def Type_derivesFrom(self: Type)(cls: ClassDefSymbol)(implicit ctx: Context): Boolean

/** Is this type a function type?
*
* @return true if the dealised type of `self` without refinement is `FunctionN[T1, T2, ..., Tn]`
*
* @note The function
*
* - returns true for `given Int => Int` and `erased Int => Int`
* - returns false for `List[Int]`, despite that `List[Int] <:< Int => Int`.
*/
def Type_isFunctionType(self: Type)(implicit ctx: Context): Boolean

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The specification of the method may raise some concerns. However, in the setting of meta-programming, this is can be justified, as it's well-known that reflection breaks referential transparency. E.g. 1 + 1 == 2, but '{1 + 1} != '2. For this method, the semantic reasoning breaks at the type level -- an intended effect that's useful in meta-programming.


/** Is this type an implicit function type?
*
* @see `Type_isFunctionType`
*/
def Type_isImplicitFunctionType(self: Type)(implicit ctx: Context): Boolean

/** Is this type an erased function type?
*
* @see `Type_isFunctionType`
*/
def Type_isErasedFunctionType(self: Type)(implicit ctx: Context): Boolean

/** Is this type a dependent function type?
*
* @see `Type_isFunctionType`
*/
def Type_isDependentFunctionType(self: Type)(implicit ctx: Context): Boolean

/** A singleton type representing a known constant value */
type ConstantType <: Type

Expand Down
28 changes: 28 additions & 0 deletions library/src/scala/tasty/reflect/TypeOrBoundsOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,34 @@ trait TypeOrBoundsOps extends Core {
def derivesFrom(cls: ClassDefSymbol)(implicit ctx: Context): Boolean =
kernel.Type_derivesFrom(self)(cls)

/** Is this type a function type?
*
* @return true if the dealised type of `self` without refinement is `FunctionN[T1, T2, ..., Tn]`
*
* @note The function
*
* - returns true for `given Int => Int` and `erased Int => Int`
* - returns false for `List[Int]`, despite that `List[Int] <:< Int => Int`.
*/
def isFunctionType(implicit ctx: Context): Boolean = kernel.Type_isFunctionType(self)

/** Is this type an implicit function type?
*
* @see `isFunctionType`
*/
def isImplicitFunctionType(implicit ctx: Context): Boolean = kernel.Type_isImplicitFunctionType(self)

/** Is this type an erased function type?
*
* @see `isFunctionType`
*/
def isErasedFunctionType(implicit ctx: Context): Boolean = kernel.Type_isErasedFunctionType(self)

/** Is this type a dependent function type?
*
* @see `isFunctionType`
*/
def isDependentFunctionType(implicit ctx: Context): Boolean = kernel.Type_isDependentFunctionType(self)
}

object IsType {
Expand Down
34 changes: 34 additions & 0 deletions tests/run-macros/reflect-isFunctionType/macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import scala.quoted._
import scala.tasty._


inline def isFunctionType[T:Type]: Boolean = ${ isFunctionTypeImpl('[T]) }

def isFunctionTypeImpl[T](tp: Type[T])(implicit refl: Reflection): Expr[Boolean] = {
import refl._
tp.unseal.tpe.isFunctionType.toExpr
}


inline def isImplicitFunctionType[T:Type]: Boolean = ${ isImplicitFunctionTypeImpl('[T]) }

def isImplicitFunctionTypeImpl[T](tp: Type[T])(implicit refl: Reflection): Expr[Boolean] = {
import refl._
tp.unseal.tpe.isImplicitFunctionType.toExpr
}


inline def isErasedFunctionType[T:Type]: Boolean = ${ isErasedFunctionTypeImpl('[T]) }

def isErasedFunctionTypeImpl[T](tp: Type[T])(implicit refl: Reflection): Expr[Boolean] = {
import refl._
tp.unseal.tpe.isErasedFunctionType.toExpr
}

inline def isDependentFunctionType[T:Type]: Boolean = ${ isDependentFunctionTypeImpl('[T]) }

def isDependentFunctionTypeImpl[T](tp: Type[T])(implicit refl: Reflection): Expr[Boolean] = {
import refl._
tp.unseal.tpe.isDependentFunctionType.toExpr
}

64 changes: 64 additions & 0 deletions tests/run-macros/reflect-isFunctionType/test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
trait Box {
type T
}

object Test {
def main(args: Array[String]): Unit = {
assert(!isFunctionType[Option[Int]])
assert(!isFunctionType[String])

assert(!isFunctionType[List[Int]])
assert(!isFunctionType[Set[Int]])

// TODO: compiler failed to synthesize Type[T]
// type T = given Set[Int] => Int
// assert(isFunctionType[T])

// TODO: compiler failed to synthesize Type[T]
// type T = Int => Int
// assert(isFunctionType[T])

assert(isFunctionType[(b: Box) => b.T])
assert(isFunctionType[() => Int])
assert(isFunctionType[(Int) => Int])
assert(isFunctionType[(Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])
assert(isFunctionType[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int])

assert(isDependentFunctionType[(b: Box) => b.T])
assert(!isDependentFunctionType[Int => Int])
// type A = (b: Box) => b.T
// assert(isDependentFunctionType[A])

assert(isImplicitFunctionType[given Int => Int])
assert(!isImplicitFunctionType[Int => Int])
// type B = given Set[Int] => Int
// assert(isImplicitFunctionType[B])

assert(isErasedFunctionType[erased Int => Int])
assert(!isErasedFunctionType[Int => Int])
// type C = erased Set[Int] => Int
// assert(isErasedFunctionType[C])
}
}