From 7c7df3b2911100fbe24a9d722c1eb6fbb1381bf1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 17 Jan 2018 21:50:11 +0100 Subject: [PATCH 1/5] Fix #3847: Splice type splices in TypeTrees --- .../tools/dotc/transform/ReifyQuotes.scala | 49 ++++++++++++++++--- project/Build.scala | 2 +- tests/run-with-compiler/i3847-b.check | 3 ++ tests/run-with-compiler/i3847-b.scala | 19 +++++++ tests/run-with-compiler/i3847.check | 3 ++ tests/run-with-compiler/i3847.scala | 19 +++++++ 6 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 tests/run-with-compiler/i3847-b.check create mode 100644 tests/run-with-compiler/i3847-b.scala create mode 100644 tests/run-with-compiler/i3847.check create mode 100644 tests/run-with-compiler/i3847.scala diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index b65ebec78600..f9f6835945e6 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -113,6 +113,8 @@ class ReifyQuotes extends MacroTransformWithImplicits { */ val importedTags = new mutable.LinkedHashMap[TypeRef, Tree]() + val explicitTags = new mutable.LinkedHashSet[TypeRef]() + /** A stack of entered symbols, to be unwound after scope exit */ var enteredSyms: List[Symbol] = Nil @@ -129,7 +131,7 @@ class ReifyQuotes extends MacroTransformWithImplicits { * as splices to `embedded`. */ private def addTags(expr: Tree)(implicit ctx: Context): Tree = - if (importedTags.isEmpty) expr + if (importedTags.isEmpty && explicitTags.isEmpty) expr else { val itags = importedTags.toList val typeDefs = for ((tref, tag) <- itags) yield { @@ -138,13 +140,43 @@ class ReifyQuotes extends MacroTransformWithImplicits { val original = tref.symbol.asType val local = original.copy( owner = ctx.owner, + name = (original.name + "$$").toTypeName, flags = Synthetic, - info = TypeAlias(tag.tpe.select(tpnme.UNARY_~))) - ctx.typeAssigner.assignType(untpd.TypeDef(original.name, alias), local) + info = TypeAlias(tag.tpe.select(tpnme.UNARY_~))).asType + + ctx.typeAssigner.assignType(untpd.TypeDef(local.name, alias), local) } importedTags.clear() - Block(typeDefs, - new TreeTypeMap(substFrom = itags.map(_._1.symbol), substTo = typeDefs.map(_.symbol)) + + + val explicitTypeDefs = for (tref <- explicitTags) yield { + val tag = ref(tref.prefix.termSymbol) + val rhs = transform(tag.select(tpnme.UNARY_~)) + + val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs) + + val local = ctx.newSymbol( + owner = ctx.owner, + name = UniqueName.fresh("ttt".toTermName).toTypeName, + flags = Synthetic, + info = TypeAlias(tag.tpe.select(tpnme.UNARY_~)), + coord = tref.prefix.termSymbol.coord).asType + + (tref, ctx.typeAssigner.assignType(untpd.TypeDef(local.name, alias), local)) + } + val map: Map[Type, Type] = explicitTypeDefs.map(x => (x._1, x._2.symbol.typeRef)).toMap + val tMap = new TypeMap() { + override def apply(tp: Type): Type = { + if (map.contains(tp)) + map.apply(tp) + else + mapOver(tp) + } + } + + Block(typeDefs ++ explicitTypeDefs.map(_._2), + new TreeTypeMap(typeMap = tMap, + substFrom = itags.map(_._1.symbol), substTo = typeDefs.map(_.symbol)) .apply(expr)) } @@ -226,8 +258,11 @@ class ReifyQuotes extends MacroTransformWithImplicits { def checkType(pos: Position)(implicit ctx: Context): TypeAccumulator[Unit] = new TypeAccumulator[Unit] { def apply(acc: Unit, tp: Type): Unit = reporting.trace(i"check type level $tp at $level") { tp match { - case tp: NamedType if tp.symbol.isSplice => - if (inQuote) outer.checkType(pos).foldOver(acc, tp) + case tp: TypeRef if tp.symbol.isSplice => + if (inQuote) { + explicitTags += tp + outer.checkType(pos).foldOver(acc, tp) + } else { if (tp.isTerm) spliceOutsideQuotes(pos) tp diff --git a/project/Build.scala b/project/Build.scala index 4d7f1ad1d627..3d482e9cf05f 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -64,7 +64,7 @@ object Build { val agentOptions = List( - // "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" +// "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" // "-agentpath:/home/dark/opt/yjp-2013-build-13072/bin/linux-x86-64/libyjpagent.so" // "-agentpath:/Applications/YourKit_Java_Profiler_2015_build_15052.app/Contents/Resources/bin/mac/libyjpagent.jnilib", // "-XX:+HeapDumpOnOutOfMemoryError", "-Xmx1g", "-Xss2m" diff --git a/tests/run-with-compiler/i3847-b.check b/tests/run-with-compiler/i3847-b.check new file mode 100644 index 000000000000..7773032b1d97 --- /dev/null +++ b/tests/run-with-compiler/i3847-b.check @@ -0,0 +1,3 @@ +{ + dotty.runtime.Arrays.newGenericArray[Int](3)(reflect.ClassTag.Int) +} diff --git a/tests/run-with-compiler/i3847-b.scala b/tests/run-with-compiler/i3847-b.scala new file mode 100644 index 000000000000..cc437542dcd7 --- /dev/null +++ b/tests/run-with-compiler/i3847-b.scala @@ -0,0 +1,19 @@ +import dotty.tools.dotc.quoted.Toolbox._ +import scala.quoted._ +import scala.reflect.ClassTag + +object Arrays { + implicit def ArrayIsLiftable[T: Liftable](implicit t: Type[T], ct: Expr[ClassTag[T]]): Liftable[Array[List[T]]] = (arr: Array[List[T]]) => '{ + new Array[List[~t]](3) + // TODO add elements + } +} + +object Test { + def main(args: Array[String]): Unit = { + import Arrays._ + implicit val ct: Expr[ClassTag[Int]] = '(ClassTag.Int) + val arr: Expr[Array[List[Int]]] = Array[List[Int]](List(1, 2, 3)) + println(arr.show) + } +} \ No newline at end of file diff --git a/tests/run-with-compiler/i3847.check b/tests/run-with-compiler/i3847.check new file mode 100644 index 000000000000..7773032b1d97 --- /dev/null +++ b/tests/run-with-compiler/i3847.check @@ -0,0 +1,3 @@ +{ + dotty.runtime.Arrays.newGenericArray[Int](3)(reflect.ClassTag.Int) +} diff --git a/tests/run-with-compiler/i3847.scala b/tests/run-with-compiler/i3847.scala new file mode 100644 index 000000000000..6ffbd1bcb625 --- /dev/null +++ b/tests/run-with-compiler/i3847.scala @@ -0,0 +1,19 @@ +import dotty.tools.dotc.quoted.Toolbox._ +import scala.quoted._ +import scala.reflect.ClassTag + +object Arrays { + implicit def ArrayIsLiftable[T: Liftable](implicit t: Type[T], ct: Expr[ClassTag[T]]): Liftable[Array[T]] = (arr: Array[T]) => '{ + new Array[~t](~(arr.length: Expr[Int]))(~ct) + // TODO add elements + } +} + +object Test { + def main(args: Array[String]): Unit = { + import Arrays._ + implicit val ct: Expr[ClassTag[Int]] = '(ClassTag.Int) + val arr: Expr[Array[Int]] = Array[Int](1, 2, 3) + println(arr.show) + } +} \ No newline at end of file From 93da8c26d7145eb93bb5a0e5c47f55da7b76465e Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Wed, 14 Mar 2018 17:57:42 +0100 Subject: [PATCH 2/5] Fix #3847: handles correctly splicing of (possibly) nested parameter types --- .../tools/dotc/transform/ReifyQuotes.scala | 76 +++++++++++-------- project/Build.scala | 2 +- tests/run-with-compiler/i3847-b.check | 4 +- tests/run-with-compiler/i3847-b.scala | 4 +- tests/run-with-compiler/i3847.check | 4 +- 5 files changed, 51 insertions(+), 39 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index f9f6835945e6..a8309e9c72f5 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -109,10 +109,15 @@ class ReifyQuotes extends MacroTransformWithImplicits { def inSplice = outer != null && !inQuote /** A map from type ref T to expressions of type `quoted.Type[T]`". - * These will be turned into splices using `addTags` + * These will be turned into splices using `addTags` and represent type variables + * that can be possibly healed. */ val importedTags = new mutable.LinkedHashMap[TypeRef, Tree]() + /** A map from type ref T to expressions of type `quoted.Type[T]`" like `importedTags` + * These will be turned into splices using `addTags` and represent types spliced + * explicitly. + */ val explicitTags = new mutable.LinkedHashSet[TypeRef]() /** A stack of entered symbols, to be unwound after scope exit */ @@ -130,41 +135,43 @@ class ReifyQuotes extends MacroTransformWithImplicits { * defined versions. As a side effect, prepend the expressions `tag1, ..., `tagN` * as splices to `embedded`. */ - private def addTags(expr: Tree)(implicit ctx: Context): Tree = + private def addTags(expr: Tree)(implicit ctx: Context): Tree = { + + def mkTagSymbolAndAssignType(typeRef: TypeRef, tag: Tree): Tree = { + val rhs = transform(tag.select(tpnme.UNARY_~)) + val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs) + + val original = typeRef.symbol.asType + + val local = ctx.newSymbol( + owner = ctx.owner, + name = UniqueName.fresh("T".toTermName).toTypeName, + flags = Synthetic, + info = TypeAlias(tag.tpe.select(tpnme.UNARY_~)), + coord = typeRef.prefix.termSymbol.coord).asType + + ctx.typeAssigner.assignType(untpd.TypeDef(local.name, alias), local) + } + if (importedTags.isEmpty && explicitTags.isEmpty) expr else { val itags = importedTags.toList + // The tree of the tag for each tag comes from implicit search in `tryHeal` val typeDefs = for ((tref, tag) <- itags) yield { - val rhs = transform(tag.select(tpnme.UNARY_~)) - val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs) - val original = tref.symbol.asType - val local = original.copy( - owner = ctx.owner, - name = (original.name + "$$").toTypeName, - flags = Synthetic, - info = TypeAlias(tag.tpe.select(tpnme.UNARY_~))).asType - - ctx.typeAssigner.assignType(untpd.TypeDef(local.name, alias), local) + mkTagSymbolAndAssignType(tref, tag) } importedTags.clear() - + // The tree of the tag for each tag comes from a type ref e.g., ~t val explicitTypeDefs = for (tref <- explicitTags) yield { val tag = ref(tref.prefix.termSymbol) - val rhs = transform(tag.select(tpnme.UNARY_~)) - - val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs) - - val local = ctx.newSymbol( - owner = ctx.owner, - name = UniqueName.fresh("ttt".toTermName).toTypeName, - flags = Synthetic, - info = TypeAlias(tag.tpe.select(tpnme.UNARY_~)), - coord = tref.prefix.termSymbol.coord).asType - - (tref, ctx.typeAssigner.assignType(untpd.TypeDef(local.name, alias), local)) + mkTagSymbolAndAssignType(tref, tag) } - val map: Map[Type, Type] = explicitTypeDefs.map(x => (x._1, x._2.symbol.typeRef)).toMap + val tagsExplicitTypeDefsPairs = explicitTags.zip(explicitTypeDefs) + explicitTags.clear() + + // Maps type splices to type references of tags e.g., ~t -> some type T$1 + val map: Map[Type, Type] = tagsExplicitTypeDefsPairs.map(x => (x._1, x._2.symbol.typeRef)).toMap val tMap = new TypeMap() { override def apply(tp: Type): Type = { if (map.contains(tp)) @@ -174,11 +181,14 @@ class ReifyQuotes extends MacroTransformWithImplicits { } } - Block(typeDefs ++ explicitTypeDefs.map(_._2), - new TreeTypeMap(typeMap = tMap, - substFrom = itags.map(_._1.symbol), substTo = typeDefs.map(_.symbol)) - .apply(expr)) + Block(typeDefs ++ tagsExplicitTypeDefsPairs.map(_._2), + new TreeTypeMap( + typeMap = tMap, + substFrom = itags.map(_._1.symbol), + substTo = typeDefs.map(_.symbol) + ).apply(expr)) } + } /** Enter staging level of symbol defined by `tree`, if applicable. */ def markDef(tree: Tree)(implicit ctx: Context) = tree match { @@ -449,6 +459,11 @@ class ReifyQuotes extends MacroTransformWithImplicits { tree match { case Quoted(quotedTree) => quotation(quotedTree, tree) + case tree: TypeTree if tree.tpe.typeSymbol.isSplice => + val splicedType = tree.tpe.asInstanceOf[TypeRef].prefix.termSymbol + splice(ref(splicedType).select(tpnme.UNARY_~)) + case tree: TypeApply => + super.transform(tree) case tree: Select if tree.symbol.isSplice => splice(tree) case tree: RefTree if needsLifting(tree) => @@ -458,7 +473,6 @@ class ReifyQuotes extends MacroTransformWithImplicits { val last = enteredSyms stats.foreach(markDef) mapOverTree(last) - case Inlined(call, bindings, InlineSplice(expansion @ Select(body, name))) => // To maintain phase consistency, we move the binding of the this parameter into the spliced code val (splicedBindings, stagedBindings) = bindings.partition { diff --git a/project/Build.scala b/project/Build.scala index 3d482e9cf05f..4d7f1ad1d627 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -64,7 +64,7 @@ object Build { val agentOptions = List( -// "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" + // "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" // "-agentpath:/home/dark/opt/yjp-2013-build-13072/bin/linux-x86-64/libyjpagent.so" // "-agentpath:/Applications/YourKit_Java_Profiler_2015_build_15052.app/Contents/Resources/bin/mac/libyjpagent.jnilib", // "-XX:+HeapDumpOnOutOfMemoryError", "-Xmx1g", "-Xss2m" diff --git a/tests/run-with-compiler/i3847-b.check b/tests/run-with-compiler/i3847-b.check index 7773032b1d97..61f2184f31f4 100644 --- a/tests/run-with-compiler/i3847-b.check +++ b/tests/run-with-compiler/i3847-b.check @@ -1,3 +1,3 @@ { - dotty.runtime.Arrays.newGenericArray[Int](3)(reflect.ClassTag.Int) -} + new Array[List[Int]](1) +} \ No newline at end of file diff --git a/tests/run-with-compiler/i3847-b.scala b/tests/run-with-compiler/i3847-b.scala index cc437542dcd7..abb757f5e2c2 100644 --- a/tests/run-with-compiler/i3847-b.scala +++ b/tests/run-with-compiler/i3847-b.scala @@ -3,8 +3,8 @@ import scala.quoted._ import scala.reflect.ClassTag object Arrays { - implicit def ArrayIsLiftable[T: Liftable](implicit t: Type[T], ct: Expr[ClassTag[T]]): Liftable[Array[List[T]]] = (arr: Array[List[T]]) => '{ - new Array[List[~t]](3) + implicit def ArrayIsLiftable[T: Liftable](implicit t: Type[T]): Liftable[Array[List[T]]] = (arr: Array[List[T]]) => '{ + new Array[List[~t]](~(arr.length: Expr[Int])) // TODO add elements } } diff --git a/tests/run-with-compiler/i3847.check b/tests/run-with-compiler/i3847.check index 7773032b1d97..9a4f9f8817e5 100644 --- a/tests/run-with-compiler/i3847.check +++ b/tests/run-with-compiler/i3847.check @@ -1,3 +1 @@ -{ - dotty.runtime.Arrays.newGenericArray[Int](3)(reflect.ClassTag.Int) -} +dotty.runtime.Arrays.newGenericArray[Int](3)(reflect.ClassTag.Int) \ No newline at end of file From 6cd8e2b655094676ca481372ac41810f1f607aa6 Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Tue, 20 Mar 2018 12:01:55 +0100 Subject: [PATCH 3/5] Minor improvements --- .../src/dotty/tools/dotc/transform/ReifyQuotes.scala | 11 ++--------- tests/run-with-compiler/quote-lib.scala | 8 +++----- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index a8309e9c72f5..66deee7013a1 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -173,15 +173,10 @@ class ReifyQuotes extends MacroTransformWithImplicits { // Maps type splices to type references of tags e.g., ~t -> some type T$1 val map: Map[Type, Type] = tagsExplicitTypeDefsPairs.map(x => (x._1, x._2.symbol.typeRef)).toMap val tMap = new TypeMap() { - override def apply(tp: Type): Type = { - if (map.contains(tp)) - map.apply(tp) - else - mapOver(tp) - } + override def apply(tp: Type): Type = map.getOrElse(tp, mapOver(tp)) } - Block(typeDefs ++ tagsExplicitTypeDefsPairs.map(_._2), + Block(typeDefs ++ explicitTypeDefs, new TreeTypeMap( typeMap = tMap, substFrom = itags.map(_._1.symbol), @@ -462,8 +457,6 @@ class ReifyQuotes extends MacroTransformWithImplicits { case tree: TypeTree if tree.tpe.typeSymbol.isSplice => val splicedType = tree.tpe.asInstanceOf[TypeRef].prefix.termSymbol splice(ref(splicedType).select(tpnme.UNARY_~)) - case tree: TypeApply => - super.transform(tree) case tree: Select if tree.symbol.isSplice => splice(tree) case tree: RefTree if needsLifting(tree) => diff --git a/tests/run-with-compiler/quote-lib.scala b/tests/run-with-compiler/quote-lib.scala index bef58a4a2174..ee42ca593b8e 100644 --- a/tests/run-with-compiler/quote-lib.scala +++ b/tests/run-with-compiler/quote-lib.scala @@ -117,11 +117,9 @@ package liftable { } object Arrays { - // FIXME missing hole for ~t -// implicit def ArrayIsLiftable[T: Liftable](implicit t: Type[T], ct: Expr[ClassTag[T]]): Liftable[Array[T]] = (arr: Array[T]) => '{ -// new Array[~t](~(arr.length: Expr[Int]))(~ct) -// } - + implicit def ArrayIsLiftable[T: Liftable](implicit t: Type[T], ct: Expr[ClassTag[T]]): Liftable[Array[T]] = (arr: Array[T]) => '{ + new Array[~t](~(arr.length: Expr[Int]))(~ct) + } } } From 74588421830e9566edd9a05a0745d12ca1877391 Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Tue, 20 Mar 2018 14:18:56 +0100 Subject: [PATCH 4/5] Add toExpr where needed --- tests/run-with-compiler/i3847-b.scala | 4 ++-- tests/run-with-compiler/i3847.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/run-with-compiler/i3847-b.scala b/tests/run-with-compiler/i3847-b.scala index abb757f5e2c2..d444e65d63e9 100644 --- a/tests/run-with-compiler/i3847-b.scala +++ b/tests/run-with-compiler/i3847-b.scala @@ -4,7 +4,7 @@ import scala.reflect.ClassTag object Arrays { implicit def ArrayIsLiftable[T: Liftable](implicit t: Type[T]): Liftable[Array[List[T]]] = (arr: Array[List[T]]) => '{ - new Array[List[~t]](~(arr.length: Expr[Int])) + new Array[List[~t]](~arr.length.toExpr) // TODO add elements } } @@ -13,7 +13,7 @@ object Test { def main(args: Array[String]): Unit = { import Arrays._ implicit val ct: Expr[ClassTag[Int]] = '(ClassTag.Int) - val arr: Expr[Array[List[Int]]] = Array[List[Int]](List(1, 2, 3)) + val arr: Expr[Array[List[Int]]] = Array[List[Int]](List(1, 2, 3)).toExpr println(arr.show) } } \ No newline at end of file diff --git a/tests/run-with-compiler/i3847.scala b/tests/run-with-compiler/i3847.scala index 6ffbd1bcb625..83c5d8f1520e 100644 --- a/tests/run-with-compiler/i3847.scala +++ b/tests/run-with-compiler/i3847.scala @@ -4,7 +4,7 @@ import scala.reflect.ClassTag object Arrays { implicit def ArrayIsLiftable[T: Liftable](implicit t: Type[T], ct: Expr[ClassTag[T]]): Liftable[Array[T]] = (arr: Array[T]) => '{ - new Array[~t](~(arr.length: Expr[Int]))(~ct) + new Array[~t](~arr.length.toExpr)(~ct) // TODO add elements } } @@ -13,7 +13,7 @@ object Test { def main(args: Array[String]): Unit = { import Arrays._ implicit val ct: Expr[ClassTag[Int]] = '(ClassTag.Int) - val arr: Expr[Array[Int]] = Array[Int](1, 2, 3) + val arr: Expr[Array[Int]] = Array[Int](1, 2, 3).toExpr println(arr.show) } } \ No newline at end of file From 6e861b2af99ec66119a400c05c235398527720b6 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 20 Mar 2018 15:31:20 +0100 Subject: [PATCH 5/5] Add missing toExpr --- tests/run-with-compiler/quote-lib.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run-with-compiler/quote-lib.scala b/tests/run-with-compiler/quote-lib.scala index ee42ca593b8e..4d648889b16c 100644 --- a/tests/run-with-compiler/quote-lib.scala +++ b/tests/run-with-compiler/quote-lib.scala @@ -118,9 +118,9 @@ package liftable { object Arrays { implicit def ArrayIsLiftable[T: Liftable](implicit t: Type[T], ct: Expr[ClassTag[T]]): Liftable[Array[T]] = (arr: Array[T]) => '{ - new Array[~t](~(arr.length: Expr[Int]))(~ct) + new Array[~t](~arr.length.toExpr)(~ct) } } } -} \ No newline at end of file +}