Skip to content

Commit

Permalink
Add reflect TypeLambda.paramVariances (#17568)
Browse files Browse the repository at this point in the history
Fixes #16734
  • Loading branch information
nicolasstucki authored Oct 13, 2023
2 parents 242ba21 + f86a682 commit 40cee39
Show file tree
Hide file tree
Showing 12 changed files with 298 additions and 0 deletions.
3 changes: 3 additions & 0 deletions compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2246,6 +2246,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
extension (self: TypeLambda)
def param(idx: Int): TypeRepr = self.newParamRef(idx)
def paramBounds: List[TypeBounds] = self.paramInfos
def paramVariances: List[Flags] =
self.typeParams.map(_.paramVariance)
end extension
end TypeLambdaMethods

Expand Down Expand Up @@ -2754,6 +2756,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
}

def isTypeParam: Boolean = self.isTypeParam
def paramVariance: Flags = self.paramVariance
def signature: Signature = self.signature
def moduleClass: Symbol = self.denot.moduleClass
def companionClass: Symbol = self.denot.companionClass
Expand Down
17 changes: 17 additions & 0 deletions library/src/scala/quoted/Quotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3258,8 +3258,16 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
/** Extension methods of `TypeLambda` */
trait TypeLambdaMethods:
extension (self: TypeLambda)
/** Reference to the i-th parameter */
def param(idx: Int) : TypeRepr
/** Type bounds of the i-th parameter */
def paramBounds: List[TypeBounds]
/** Variance flags for the i-th parameter
*
* Variance flags can be one of `Flags.{Covariant, Contravariant, EmptyFlags}`.
*/
@experimental
def paramVariances: List[Flags]
end extension
end TypeLambdaMethods

Expand Down Expand Up @@ -4078,8 +4086,17 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
/** Fields of a case class type -- only the ones declared in primary constructor */
def caseFields: List[Symbol]

/** Is this the symbol of a type parameter */
def isTypeParam: Boolean

/** Variance flags for of this type parameter.
*
* Variance flags can be one of `Flags.{Covariant, Contravariant, EmptyFlags}`.
* If this is not the symbol of a type parameter the result is `Flags.EmptyFlags`.
*/
@experimental
def paramVariance: Flags

/** Signature of this definition */
def signature: Signature

Expand Down
67 changes: 67 additions & 0 deletions tests/run-macros/i16734a.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
[F >: scala.Nothing <: [A >: scala.Nothing <: scala.Any] =>> scala.Any] =>> K1Inv[F]
F
A

[F >: scala.Nothing <: [A >: scala.Nothing <: scala.Any] =>> scala.Any] =>> K1Cov[F]
F
+A

[F >: scala.Nothing <: [A >: scala.Nothing <: scala.Any] =>> scala.Any] =>> K1Con[F]
F
-A

[F >: scala.Nothing <: [A >: scala.Nothing <: scala.Any, B >: scala.Nothing <: scala.Any] =>> scala.Any] =>> K2InvInv[F]
F
A, B

[F >: scala.Nothing <: [A >: scala.Nothing <: scala.Any, B >: scala.Nothing <: scala.Any] =>> scala.Any] =>> K2InvCov[F]
F
A, +B

[F >: scala.Nothing <: [A >: scala.Nothing <: scala.Any, B >: scala.Nothing <: scala.Any] =>> scala.Any] =>> K2InvCon[F]
F
A, -B

[F >: scala.Nothing <: [A >: scala.Nothing <: scala.Any, B >: scala.Nothing <: scala.Any] =>> scala.Any] =>> K2CovInv[F]
F
+A, B

[F >: scala.Nothing <: [A >: scala.Nothing <: scala.Any, B >: scala.Nothing <: scala.Any] =>> scala.Any] =>> K2CovCov[F]
F
+A, +B

[F >: scala.Nothing <: [A >: scala.Nothing <: scala.Any, B >: scala.Nothing <: scala.Any] =>> scala.Any] =>> K2CovCon[F]
F
+A, -B

[F >: scala.Nothing <: [A >: scala.Nothing <: scala.Any, B >: scala.Nothing <: scala.Any] =>> scala.Any] =>> K2ConInv[F]
F
-A, B

[F >: scala.Nothing <: [A >: scala.Nothing <: scala.Any, B >: scala.Nothing <: scala.Any] =>> scala.Any] =>> K2ConCov[F]
F
-A, +B

[F >: scala.Nothing <: [A >: scala.Nothing <: scala.Any, B >: scala.Nothing <: scala.Any] =>> scala.Any] =>> K2ConCon[F]
F
-A, -B

[G >: scala.Nothing <: [A >: scala.Nothing <: scala.Any, B >: scala.Nothing <: scala.Any, C >: scala.Nothing <: scala.Any, D >: scala.Nothing <: [X1 >: scala.Nothing <: scala.Any, Y1 >: scala.Nothing <: scala.Any, Z1 >: scala.Nothing <: scala.Any] =>> scala.Any, E >: scala.Nothing <: [X2 >: scala.Nothing <: scala.Any, Y2 >: scala.Nothing <: scala.Any, Z2 >: scala.Nothing <: scala.Any] =>> scala.Any, F >: scala.Nothing <: [X3 >: scala.Nothing <: scala.Any, Y3 >: scala.Nothing <: scala.Any, Z3 >: scala.Nothing <: scala.Any] =>> scala.Any] =>> scala.Any] =>> KFunky[G]
G
A, +B, -C, D, +E, -F
X1, +Y1, -Z1
X2, +Y2, -Z2
X3, +Y3, -Z3

[A >: scala.Nothing <: scala.Any, F >: scala.Nothing <: [B >: scala.Nothing <: scala.Any] =>> scala.Any] =>> F[A]
A, +F
B

[A >: scala.Nothing <: scala.Any, F >: scala.Nothing <: [B >: scala.Nothing <: scala.Any] =>> scala.Any] =>> F[A]
+A, +F
+B

[A >: scala.Nothing <: scala.Any, F >: scala.Nothing <: [B >: scala.Nothing <: scala.Any] =>> scala.Any] =>> F[A]
-A, +F
-B

21 changes: 21 additions & 0 deletions tests/run-macros/i16734a/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import scala.quoted.*

inline def variances[A <: AnyKind]: String =
${variancesImpl[A]}

def variancesImpl[A <: AnyKind: Type](using Quotes): Expr[String] =
import quotes.reflect.*
def loop(tpe: TypeRepr): List[String] =
tpe match
case tpe: TypeLambda =>
tpe.paramNames.zip(tpe.paramVariances).map { (name, variance) =>
if variance == Flags.Covariant then "+" + name
else if variance == Flags.Contravariant then "-" + name
else name
}.mkString(", ") :: tpe.paramTypes.flatMap(loop)
case tpe: TypeBounds =>
loop(tpe.low) ++ loop(tpe.hi)
case _ =>
Nil
val res = (Type.show[A] :: loop(TypeRepr.of[A])).mkString("", "\n", "\n")
Expr(res)
34 changes: 34 additions & 0 deletions tests/run-macros/i16734a/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
trait K1Inv[F[A]]
trait K1Cov[F[+A]]
trait K1Con[F[-A]]

trait K2InvInv[F[A, B]]
trait K2InvCov[F[A, +B]]
trait K2InvCon[F[A, -B]]
trait K2CovInv[F[+A, B]]
trait K2CovCov[F[+A, +B]]
trait K2CovCon[F[+A, -B]]
trait K2ConInv[F[-A, B]]
trait K2ConCov[F[-A, +B]]
trait K2ConCon[F[-A, -B]]


trait KFunky[G[A, +B, -C, D[X1, +Y1, -Z1], +E[X2, +Y2, -Z2], -F[X3, +Y3, -Z3]]]

@main def Test =
println(variances[K1Inv])
println(variances[K1Cov])
println(variances[K1Con])
println(variances[K2InvInv])
println(variances[K2InvCov])
println(variances[K2InvCon])
println(variances[K2CovInv])
println(variances[K2CovCov])
println(variances[K2CovCon])
println(variances[K2ConInv])
println(variances[K2ConCov])
println(variances[K2ConCon])
println(variances[KFunky])
println(variances[[A, F[B]] =>> F[A]])
println(variances[[A, F[+B]] =>> F[A]])
println(variances[[A, F[-B]] =>> F[A]])
36 changes: 36 additions & 0 deletions tests/run-macros/i16734b.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
type F1Inv
A

type F1Cov
+A

type F1Con
-A

type F2InvInv
A, B

type F2InvCov
A, +B

type F2InvCon
A, -B

type F2CovInv
+A, B

type F2CovCov
+A, +B

type F2CovCon
+A, -B

type F2ConInv
-A, B

type F2ConCov
-A, +B

type F2ConCon
-A, -B

14 changes: 14 additions & 0 deletions tests/run-macros/i16734b/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import scala.quoted.*

inline def typeVariances[A <: AnyKind]: String =
${variancesImpl[A]}

def variancesImpl[A <: AnyKind: Type](using Quotes): Expr[String] =
import quotes.reflect.*
val TypeBounds(_, tl: TypeLambda) = TypeRepr.of[A].typeSymbol.info: @unchecked
val variances = tl.paramNames.zip(tl.paramVariances).map { (name, variance) =>
if variance == Flags.Covariant then "+" + name
else if variance == Flags.Contravariant then "-" + name
else name
}.mkString(", ")
Expr(TypeRepr.of[A].typeSymbol.toString() + "\n" + variances + "\n")
27 changes: 27 additions & 0 deletions tests/run-macros/i16734b/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
type F1Inv[A]
type F1Cov[+A]
type F1Con[-A]

type F2InvInv[A, B]
type F2InvCov[A, +B]
type F2InvCon[A, -B]
type F2CovInv[+A, B]
type F2CovCov[+A, +B]
type F2CovCon[+A, -B]
type F2ConInv[-A, B]
type F2ConCov[-A, +B]
type F2ConCon[-A, -B]

@main def Test =
println(typeVariances[F1Inv])
println(typeVariances[F1Cov])
println(typeVariances[F1Con])
println(typeVariances[F2InvInv])
println(typeVariances[F2InvCov])
println(typeVariances[F2InvCon])
println(typeVariances[F2CovInv])
println(typeVariances[F2CovCov])
println(typeVariances[F2CovCon])
println(typeVariances[F2ConInv])
println(typeVariances[F2ConCov])
println(typeVariances[F2ConCon])
36 changes: 36 additions & 0 deletions tests/run-macros/i16734c.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class C1Inv
A

class C1Cov
+A

class C1Con
-A

class C2InvInv
A, B

class C2InvCov
A, +B

class C2InvCon
A, -B

class C2CovInv
+A, B

class C2CovCov
+A, +B

class C2CovCon
+A, -B

class C2ConInv
-A, B

class C2ConCov
-A, +B

class C2ConCon
-A, -B

14 changes: 14 additions & 0 deletions tests/run-macros/i16734c/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import scala.quoted.*

inline def classVariances[A <: AnyKind]: String =
${variancesImpl[A]}

def variancesImpl[A <: AnyKind: Type](using Quotes): Expr[String] =
import quotes.reflect.*
val variances = TypeRepr.of[A].typeSymbol.typeMembers.filter(_.isTypeParam).map { sym =>
if sym.paramVariance == Flags.Covariant then "+" + sym.name
else if sym.paramVariance == Flags.Contravariant then "-" + sym.name
else sym.name
}
val res = variances.mkString(TypeRepr.of[A].typeSymbol.toString + "\n", ", ", "\n")
Expr(res)
27 changes: 27 additions & 0 deletions tests/run-macros/i16734c/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class C1Inv[A] { type T }
class C1Cov[+A] { type T }
class C1Con[-A] { type T }

class C2InvInv[A, B] { type T }
class C2InvCov[A, +B] { type T }
class C2InvCon[A, -B] { type T }
class C2CovInv[+A, B] { type T }
class C2CovCov[+A, +B] { type T }
class C2CovCon[+A, -B] { type T }
class C2ConInv[-A, B] { type T }
class C2ConCov[-A, +B] { type T }
class C2ConCon[-A, -B] { type T }

@main def Test =
println(classVariances[C1Inv])
println(classVariances[C1Cov])
println(classVariances[C1Con])
println(classVariances[C2InvInv])
println(classVariances[C2InvCov])
println(classVariances[C2InvCon])
println(classVariances[C2CovInv])
println(classVariances[C2CovCov])
println(classVariances[C2CovCon])
println(classVariances[C2ConInv])
println(classVariances[C2ConCov])
println(classVariances[C2ConCon])
2 changes: 2 additions & 0 deletions tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ val experimentalDefinitionInLibrary = Set(
"scala.annotation.init$.region",

//// New APIs: Quotes
"scala.quoted.Quotes.reflectModule.TypeLambdaMethods.paramVariances",
"scala.quoted.Quotes.reflectModule.SymbolMethods.paramVariance",
// Can be stabilized in 3.4.0 (unsure) or later
"scala.quoted.Quotes.reflectModule.CompilationInfoModule.XmacroSettings",
"scala.quoted.Quotes.reflectModule.FlagsModule.JavaAnnotation",
Expand Down

0 comments on commit 40cee39

Please sign in to comment.