Skip to content

Commit 8f6f3c1

Browse files
committed
Fix #5965: Make scala.quoted.Type poly-kinded
1 parent b885862 commit 8f6f3c1

File tree

10 files changed

+167
-36
lines changed

10 files changed

+167
-36
lines changed

compiler/src/dotty/tools/dotc/tastyreflect/QuotedOpsImpl.scala

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,32 @@ trait QuotedOpsImpl extends scala.tasty.reflect.QuotedOps with CoreImpl {
1414
def unseal(implicit ctx: Context): Term = PickledQuotes.quotedExprToTree(x)
1515
}
1616

17-
def QuotedTypeDeco[T](x: scala.quoted.Type[T]): QuotedTypeAPI = new QuotedTypeAPI {
18-
def unseal(implicit ctx: Context): TypeTree = PickledQuotes.quotedTypeToTree(x)
19-
}
20-
21-
def TermToQuoteDeco(term: Term): TermToQuotedAPI = new TermToQuotedAPI {
22-
23-
def seal[T: scala.quoted.Type](implicit ctx: Context): scala.quoted.Expr[T] = {
24-
25-
val expectedType = QuotedTypeDeco(implicitly[scala.quoted.Type[T]]).unseal.tpe
26-
27-
def etaExpand(term: Term): Term = term.tpe.widen match {
28-
case mtpe: Types.MethodType if !mtpe.isParamDependent =>
29-
val closureResType = mtpe.resType match {
30-
case t: Types.MethodType => t.toFunctionType()
31-
case t => t
32-
}
33-
val closureTpe = Types.MethodType(mtpe.paramNames, mtpe.paramInfos, closureResType)
34-
val closureMethod = ctx.newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, closureTpe)
35-
tpd.Closure(closureMethod, tss => etaExpand(new tpd.TreeOps(term).appliedToArgs(tss.head)))
36-
case _ => term
37-
}
17+
protected def unsealType(tpe: quoted.Type[_])(implicit ctx: Context): TypeTree =
18+
PickledQuotes.quotedTypeToTree(tpe)
19+
20+
protected def sealTerm(term: Term, tpt: TypeTree)(implicit ctx: Context): scala.quoted.Expr[_] = {
21+
def etaExpand(term: Term): Term = term.tpe.widen match {
22+
case mtpe: Types.MethodType if !mtpe.isParamDependent =>
23+
val closureResType = mtpe.resType match {
24+
case t: Types.MethodType => t.toFunctionType()
25+
case t => t
26+
}
27+
val closureTpe = Types.MethodType(mtpe.paramNames, mtpe.paramInfos, closureResType)
28+
val closureMethod = ctx.newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, closureTpe)
29+
tpd.Closure(closureMethod, tss => etaExpand(new tpd.TreeOps(term).appliedToArgs(tss.head)))
30+
case _ => term
31+
}
3832

39-
val expanded = etaExpand(term)
40-
if (expanded.tpe <:< expectedType) {
41-
new scala.quoted.Exprs.TastyTreeExpr(expanded).asInstanceOf[scala.quoted.Expr[T]]
42-
} else {
43-
throw new scala.tasty.TastyTypecheckError(
44-
s"""Term: ${term.show}
45-
|did not conform to type: ${expectedType.show}
46-
|""".stripMargin
47-
)
48-
}
33+
val expectedType = tpt.tpe
34+
val expanded = etaExpand(term)
35+
if (expanded.tpe <:< expectedType) {
36+
new scala.quoted.Exprs.TastyTreeExpr(expanded)
37+
} else {
38+
throw new scala.tasty.TastyTypecheckError(
39+
s"""Term: ${term.show}
40+
|did not conform to type: ${expectedType.show}
41+
|""".stripMargin
42+
)
4943
}
5044
}
5145

compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,14 @@ class ReifyQuotes extends MacroTransform {
352352
transformSplice(spliceTree)
353353

354354
case tree: TypeTree if tree.tpe.typeSymbol.isSplice =>
355-
val splicedType = tree.tpe.stripTypeVar.asInstanceOf[TypeRef].prefix.termSymbol
356-
transformSplice(ref(splicedType).select(tpnme.splice).withSpan(tree.span))
355+
tree.tpe.stripTypeVar match {
356+
case tp: AppliedType =>
357+
val splicedType = tp.tycon.asInstanceOf[TypeRef].prefix.termSymbol
358+
AppliedTypeTree(transformSplice(ref(splicedType).select(tpnme.splice)), tp.args.map(TypeTree(_))).withSpan(tree.span)
359+
case tp: TypeRef =>
360+
val splicedType = tp.prefix.termSymbol
361+
transformSplice(ref(splicedType).select(tpnme.splice).withSpan(tree.span))
362+
}
357363

358364
case tree: RefTree if isCaptured(tree.symbol, level) =>
359365
val t = capturers(tree.symbol).apply(tree)

compiler/test/dotty/tools/vulpix/TestConfiguration.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ object TestConfiguration {
4747
val yCheckOptions = Array("-Ycheck:all")
4848

4949
val commonOptions = checkOptions ++ noCheckOptions ++ yCheckOptions
50-
val defaultOptions = TestFlags(basicClasspath, commonOptions)
50+
val defaultOptions = TestFlags(basicClasspath, commonOptions) and "-Ykind-polymorphism" // FIXME remove "-Ykind-polymorphism"
5151
val withCompilerOptions =
5252
defaultOptions.withClasspath(withCompilerClasspath).withRunClasspath(withCompilerClasspath)
5353
val allowDeepSubtypes = defaultOptions without "-Yno-deep-subtypes"
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package scala.quoted
2+
3+
import scala.quoted.Types.TaggedType
4+
import scala.reflect.ClassTag
5+
import scala.runtime.quoted.Unpickler.Pickled
6+
7+
sealed abstract class Type[T <: AnyKind] {
8+
type `$splice` = T
9+
}
10+
11+
/** Some basic type tags, currently incomplete */
12+
object Type {
13+
/** A term quote is desugared by the compiler into a call to this method */
14+
def apply[T <: AnyKind]: Type[T] =
15+
throw new Error("Internal error: this method call should have been replaced by the compiler")
16+
17+
implicit def UnitTag: Type[Unit] = new TaggedType[Unit]
18+
implicit def BooleanTag: Type[Boolean] = new TaggedType[Boolean]
19+
implicit def ByteTag: Type[Byte] = new TaggedType[Byte]
20+
implicit def CharTag: Type[Char] = new TaggedType[Char]
21+
implicit def ShortTag: Type[Short] = new TaggedType[Short]
22+
implicit def IntTag: Type[Int] = new TaggedType[Int]
23+
implicit def LongTag: Type[Long] = new TaggedType[Long]
24+
implicit def FloatTag: Type[Float] = new TaggedType[Float]
25+
implicit def DoubleTag: Type[Double] = new TaggedType[Double]
26+
}
27+
28+
/** All implementations of Type[T].
29+
* These should never be used directly.
30+
*/
31+
object Types {
32+
/** A Type backed by a pickled TASTY tree */
33+
final class TastyType[T](val tasty: Pickled, val args: Seq[Any]) extends Type[T] {
34+
override def toString(): String = s"Type(<pickled tasty>)"
35+
}
36+
37+
/** An Type backed by a value */
38+
final class TaggedType[T](implicit val ct: ClassTag[T]) extends Type[T] {
39+
override def toString: String = s"Type($ct)"
40+
}
41+
42+
/** An Type backed by a tree */
43+
final class TreeType[Tree](val typeTree: Tree) extends quoted.Type[Any] {
44+
override def toString: String = s"Type(<tasty tree>)"
45+
}
46+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package scala.tasty.reflect
2+
3+
/** Extension methods on scala.quoted.{Expr|Type} to convert to scala.tasty.Tasty objects */
4+
trait QuotedOps extends Core {
5+
6+
trait QuotedExprAPI {
7+
/** View this expression `Expr[T]` as a `Term` */
8+
def unseal(implicit ctx: Context): Term
9+
}
10+
implicit def QuotedExprDeco[T](expr: quoted.Expr[T]): QuotedExprAPI
11+
12+
implicit class QuotedTypeDeco[T <: AnyKind](tpe: quoted.Type[T]) {
13+
/** View this expression `Type[T]` as a `TypeTree` */
14+
def unseal(implicit ctx: Context): TypeTree = unsealType(tpe)
15+
}
16+
17+
protected def unsealType(tpe: quoted.Type[_])(implicit ctx: Context): TypeTree
18+
19+
implicit class TermToQuoteDeco(term: Term) {
20+
/** Convert `Term` to an `Expr[T]` and check that it conforms to `T` */
21+
def seal[T: scala.quoted.Type](implicit ctx: Context): scala.quoted.Expr[T] =
22+
sealTerm(term, unsealType(implicitly[scala.quoted.Type[T]])).asInstanceOf[scala.quoted.Expr[T]]
23+
}
24+
25+
protected def sealTerm(term: Term, tpt: TypeTree)(implicit ctx: Context): scala.quoted.Expr[_]
26+
27+
trait TypeToQuotedAPI {
28+
/** Convert `Type` to an `quoted.Type[T]` */
29+
def seal(implicit ctx: Context): scala.quoted.Type[_]
30+
}
31+
implicit def TypeToQuoteDeco(tpe: Type): TypeToQuotedAPI
32+
}

library/src/scala/tasty/reflect/QuotedOps.scala renamed to library/src-non-bootstrapped/scala/tasty/reflect/QuotedOps.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ trait QuotedOps extends Core {
1313
/** View this expression `Type[T]` as a `TypeTree` */
1414
def unseal(implicit ctx: Context): TypeTree
1515
}
16-
implicit def QuotedTypeDeco[T](tpe: quoted.Type[T]): QuotedTypeAPI
16+
implicit def QuotedTypeDeco[T](tpe: quoted.Type[T]): QuotedTypeAPI = new QuotedTypeAPI {
17+
override def unseal(implicit ctx: Context): TypeTree = unsealType(tpe)
18+
}
19+
20+
protected def unsealType(tpe: quoted.Type[_])(implicit ctx: Context): TypeTree
1721

1822
trait TermToQuotedAPI {
1923
/** Convert `Term` to an `Expr[T]` and check that it conforms to `T` */

project/Build.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1135,11 +1135,17 @@ object Build {
11351135
def asDottyCompiler(implicit mode: Mode): Project = project.withCommonSettings.
11361136
dependsOn(`dotty-interfaces`).
11371137
dependsOn(dottyLibrary).
1138-
settings(dottyCompilerSettings)
1138+
settings(dottyCompilerSettings).
1139+
bootstrappedSettings(
1140+
// To support kind polymorphism of scala.quote.Type
1141+
scalacOptions in Compile += "-Ykind-polymorphism"
1142+
)
11391143

11401144
def asDottyLibrary(implicit mode: Mode): Project = project.withCommonSettings.
11411145
settings(dottyLibrarySettings).
11421146
bootstrappedSettings(
1147+
// To support kind polymorphism of scala.quote.Type
1148+
scalacOptions in Compile += "-Ykind-polymorphism",
11431149
// Needed so that the library sources are visible when `dotty.tools.dotc.core.Definitions#init` is called.
11441150
scalacOptions in Compile ++= Seq("-sourcepath", (scalaSource in Compile).value.getAbsolutePath)
11451151
)

tests/run-with-compiler/i5965.check

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
val y: collection.immutable.List[scala.Int] = scala.List.apply[scala.Int](1, 2, 3)
3+
(y: collection.immutable.List[evidence$1$_~$1])
4+
}
5+
List(1, 2, 3)
6+
{
7+
val y: scala.Option[scala.Int] = scala.Option.apply[scala.Int](4)
8+
(y: scala.Option[evidence$1$_~$1])
9+
}
10+
Some(4)
11+
{
12+
val y: collection.immutable.Map[scala.Int, scala.Int] = scala.Predef.Map.apply[scala.Int, scala.Int](scala.Predef.ArrowAssoc[scala.Int](4).->[scala.Int](1))
13+
(y: collection.immutable.Map[scala.Int, evidence$1$_~$1])
14+
}
15+
Map(4 -> 1)

tests/run-with-compiler/i5965.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import scala.quoted._
2+
3+
import scala.tasty._
4+
5+
object Test {
6+
7+
implicit val toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make
8+
9+
def main(args: Array[String]): Unit = {
10+
'[List]
11+
val list = bound('{List(1, 2, 3)})
12+
println(list.show)
13+
println(list.run)
14+
15+
val opt = bound('{Option(4)})
16+
println(opt.show)
17+
println(opt.run)
18+
19+
val map = bound('{Map(4 -> 1)})
20+
println(map.show)
21+
println(map.run)
22+
}
23+
24+
def bound[T: Type, S[_]: Type](x: Expr[S[T]]): Expr[S[T]] = '{
25+
val y: S[T] = $x
26+
y
27+
}
28+
}

0 commit comments

Comments
 (0)