diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index bc4f84c147c8..14b4f0d8c12f 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -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 diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 15df4a08e1f0..8fc6c1a22f95 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -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 diff --git a/tests/run-macros/i16734a.check b/tests/run-macros/i16734a.check new file mode 100644 index 000000000000..512f197f28a6 --- /dev/null +++ b/tests/run-macros/i16734a.check @@ -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 + diff --git a/tests/run-macros/i16734a/Macro_1.scala b/tests/run-macros/i16734a/Macro_1.scala new file mode 100644 index 000000000000..eedbe2d0e6a1 --- /dev/null +++ b/tests/run-macros/i16734a/Macro_1.scala @@ -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) diff --git a/tests/run-macros/i16734a/Test_2.scala b/tests/run-macros/i16734a/Test_2.scala new file mode 100644 index 000000000000..c6b02c145841 --- /dev/null +++ b/tests/run-macros/i16734a/Test_2.scala @@ -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]]) diff --git a/tests/run-macros/i16734b.check b/tests/run-macros/i16734b.check new file mode 100644 index 000000000000..b894ffba4fc4 --- /dev/null +++ b/tests/run-macros/i16734b.check @@ -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 + diff --git a/tests/run-macros/i16734b/Macro_1.scala b/tests/run-macros/i16734b/Macro_1.scala new file mode 100644 index 000000000000..f1e8e12d308d --- /dev/null +++ b/tests/run-macros/i16734b/Macro_1.scala @@ -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") diff --git a/tests/run-macros/i16734b/Test_2.scala b/tests/run-macros/i16734b/Test_2.scala new file mode 100644 index 000000000000..f5481aaf96cf --- /dev/null +++ b/tests/run-macros/i16734b/Test_2.scala @@ -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]) diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index d30a58954bc4..c80a3bf77ad2 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -69,6 +69,7 @@ val experimentalDefinitionInLibrary = Set( //// New APIs: Quotes "scala.quoted.Quotes.reflectModule.FlagsModule.AbsOverride", + "scala.quoted.Quotes.reflectModule.TypeLambdaMethods.paramVariances", // Can be stabilized in 3.4.0 (unsure) or later "scala.quoted.Quotes.reflectModule.CompilationInfoModule.XmacroSettings", "scala.quoted.Quotes.reflectModule.FlagsModule.JavaAnnotation",