Skip to content

Fix #3847: Handle correctly splicing of (possibly) nested type parameters #4116

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 5 commits into from
Mar 20, 2018
Merged
Show file tree
Hide file tree
Changes from 4 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
76 changes: 59 additions & 17 deletions compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,17 @@ 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 */
var enteredSyms: List[Symbol] = Nil

Expand All @@ -128,25 +135,55 @@ 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 =
if (importedTags.isEmpty) expr
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,
flags = Synthetic,
info = TypeAlias(tag.tpe.select(tpnme.UNARY_~)))
ctx.typeAssigner.assignType(untpd.TypeDef(original.name, alias), local)
mkTagSymbolAndAssignType(tref, tag)
}
importedTags.clear()
Block(typeDefs,
new TreeTypeMap(substFrom = itags.map(_._1.symbol), substTo = typeDefs.map(_.symbol))
.apply(expr))

// 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)
mkTagSymbolAndAssignType(tref, tag)
}
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 = map.getOrElse(tp, mapOver(tp))
}

Block(typeDefs ++ explicitTypeDefs,
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 {
Expand Down Expand Up @@ -226,8 +263,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
Expand Down Expand Up @@ -414,6 +454,9 @@ 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: Select if tree.symbol.isSplice =>
splice(tree)
case tree: RefTree if needsLifting(tree) =>
Expand All @@ -423,7 +466,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 {
Expand Down
3 changes: 3 additions & 0 deletions tests/run-with-compiler/i3847-b.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
new Array[List[Int]](1)
}
19 changes: 19 additions & 0 deletions tests/run-with-compiler/i3847-b.scala
Original file line number Diff line number Diff line change
@@ -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]): Liftable[Array[List[T]]] = (arr: Array[List[T]]) => '{
new Array[List[~t]](~arr.length.toExpr)
// 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)).toExpr
println(arr.show)
}
}
1 change: 1 addition & 0 deletions tests/run-with-compiler/i3847.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dotty.runtime.Arrays.newGenericArray[Int](3)(reflect.ClassTag.Int)
19 changes: 19 additions & 0 deletions tests/run-with-compiler/i3847.scala
Original file line number Diff line number Diff line change
@@ -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.toExpr)(~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).toExpr
println(arr.show)
}
}
8 changes: 3 additions & 5 deletions tests/run-with-compiler/quote-lib.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

}
Expand Down