summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDen Shabalin <den.shabalin@gmail.com>2013-12-02 16:31:33 +0100
committerDen Shabalin <den.shabalin@gmail.com>2013-12-10 16:02:45 +0100
commit4c899ea34c01eebf1215abd579d11393cf20a487 (patch)
tree2b730c5274281f94b216f7f59a2ccfb7ef82160c
parent4be6ea147a7d8f300c1e6db2a216b50fe8cf5dc7 (diff)
downloadscala-4c899ea34c01eebf1215abd579d11393cf20a487.tar.gz
scala-4c899ea34c01eebf1215abd579d11393cf20a487.tar.bz2
scala-4c899ea34c01eebf1215abd579d11393cf20a487.zip
Refactor Holes and Reifiers slices of Quasiquotes cake
This commit performs a number of preliminary refactoring needed to implement unliftable: 1. Replace previous inheritance-heavy implementation of Holes with similar but much simpler one. Holes are now split into two different categories: ApplyHole and UnapplyHole which correspondingly represent information about holes in construction and deconstruction quasiquotes. 2. Make Placeholders extract holes rather than their field values. This is required to be able to get additional mode-specific information from holes (e.g. only ApplyHoles have types). 3. Bring ApplyReifier & UnapplyReifier even closer to the future where there is just a single base Reifier with mode parameter. Along the way a few bugs were fixed: 1. SI-7980 SI-7996 fail with nice error on bottom types splices 2. Use asSeenFrom instead of typeArguments in parseCardinality. This fixes a crash if T <:< Iterable[Tree] but does not itself have any type arguments. 3. Fix spurious error message on splicing of Lists through Liftable[List[T]]
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Holes.scala224
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala41
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala163
-rw-r--r--test/files/neg/si7980.check4
-rw-r--r--test/files/neg/si7980.scala8
-rw-r--r--test/files/scalacheck/quasiquotes/ErrorProps.scala14
-rw-r--r--test/files/scalacheck/quasiquotes/LiftableProps.scala10
-rw-r--r--test/files/scalacheck/quasiquotes/TermConstructionProps.scala5
8 files changed, 230 insertions, 239 deletions
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
index 325e6d0783..c90401e765 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
@@ -30,158 +30,114 @@ trait Holes { self: Quasiquotes =>
import definitions._
import universeTypes._
- /** Location characterizes a kind of a non-terminal in Scala syntax where something is going to be spliced.
- * A location is typically associated with a type of the things that can be spliced there.
- * Associated type might be different from an actual tpe of a splicee due to lifting.
- * This is the first pillar of modularity in the quasiquote reifier.
- */
- sealed abstract class Location(val tpe: Type)
- case object UnknownLocation extends Location(NoType)
- case class TreeLocation(override val tpe: Type) extends Location(tpe)
- case object NameLocation extends Location(nameType)
- case object ModsLocation extends Location(modsType)
- case object FlagsLocation extends Location(flagsType)
- case object SymbolLocation extends Location(symbolType)
- case class IterableLocation(card: Cardinality, sublocation: TreeLocation) extends Location(NoType) {
- override val tpe = {
- def loop(n: Cardinality, tpe: Type): Type =
- if (n == NoDot) tpe
- else appliedType(IterableClass.toType, List(loop(n.pred, tpe)))
- loop(card, sublocation.tpe)
+ protected lazy val IterableTParam = IterableClass.typeParams(0).asType.toType
+ protected def inferParamImplicit(tfun: Type, targ: Type) = c.inferImplicitValue(appliedType(tfun, List(targ)), silent = true)
+ protected def inferLiftable(tpe: Type): Tree = inferParamImplicit(liftableType, tpe)
+ protected def isLiftableType(tpe: Type) = inferLiftable(tpe) != EmptyTree
+ protected def isNativeType(tpe: Type) =
+ (tpe <:< treeType) || (tpe <:< nameType) || (tpe <:< modsType) ||
+ (tpe <:< flagsType) || (tpe <:< symbolType)
+ protected def isBottomType(tpe: Type) =
+ tpe <:< NothingClass.tpe || tpe <:< NullClass.tpe
+ protected def stripIterable(tpe: Type, limit: Option[Cardinality] = None): (Cardinality, Type) =
+ if (limit.map { _ == NoDot }.getOrElse { false }) (NoDot, tpe)
+ else if (tpe != null && !isIterableType(tpe)) (NoDot, tpe)
+ else if (isBottomType(tpe)) (NoDot, tpe)
+ else {
+ val targ = IterableTParam.asSeenFrom(tpe, IterableClass)
+ val (card, innerTpe) = stripIterable(targ, limit.map { _.pred })
+ (card.succ, innerTpe)
}
+ protected def iterableTypeFromCard(n: Cardinality, tpe: Type): Type = {
+ if (n == NoDot) tpe
+ else appliedType(IterableClass.toType, List(iterableTypeFromCard(n.pred, tpe)))
}
- /** Hole type describes location, cardinality and a pre-reification routine associated with a hole.
- * An interesting thing about HoleType is that it can be completely inferred from the type of the splicee.
- * This is the second pillar of modularity in the quasiquote reifier.
+ /** Hole encapsulates information about splices in quasiquotes.
+ * It packs together a cardinality of a splice, pre-reified tree
+ * representation (possibly preprocessed) and position.
*/
- case class HoleType(preprocessor: Tree => Tree, location: Location, cardinality: Cardinality) {
- def makeHole(tree: Tree) = Hole(preprocessor(tree), location, cardinality)
+ abstract class Hole {
+ val tree: Tree
+ val pos: Position
+ val cardinality: Cardinality
}
- object HoleType {
- def unapply(tpe: Type): Option[HoleType] = tpe match {
- case NativeType(holeTpe) => Some(holeTpe)
- case LiftableType(holeTpe) => Some(holeTpe)
- case IterableTreeType(holeTpe) => Some(holeTpe)
- case IterableLiftableType(holeTpe) => Some(holeTpe)
- case _ => None
- }
-
- trait HoleTypeExtractor {
- def unapply(tpe: Type): Option[HoleType] = {
- for {
- preprocessor <- this.preprocessor(tpe)
- location <- this.location(tpe)
- cardinality <- Some(this.cardinality(tpe))
- } yield HoleType(preprocessor, location, cardinality)
- }
- def preprocessor(tpe: Type): Option[Tree => Tree]
- def location(tpe: Type): Option[Location]
- def cardinality(tpe: Type): Cardinality = parseCardinality(tpe)._1
- def lifter(tpe: Type): Option[Tree => Tree] = {
- val lifterTpe = appliedType(liftableType, List(tpe))
- val lifter = c.inferImplicitValue(lifterTpe, silent = true)
- if (lifter != EmptyTree) Some(tree => {
- val lifted = Apply(lifter, List(tree))
- val targetType = Select(u, tpnme.Tree)
- atPos(tree.pos)(TypeApply(Select(lifted, nme.asInstanceOf_), List(targetType)))
- }) else None
- }
+ object Hole {
+ def apply(card: Cardinality, tree: Tree): Hole =
+ if (method != nme.unapply) new ApplyHole(card, tree)
+ else new UnapplyHole(card, tree)
+ def unapply(hole: Hole): Some[(Tree, Cardinality)] = Some((hole.tree, hole.cardinality))
+ }
- def iterator(tpe: Type)(elementTransform: Tree => Tree): Option[Tree => Tree] = {
- def reifyIterable(tree: Tree, n: Cardinality): Tree = {
- def loop(tree: Tree, n: Cardinality) =
- if (n == NoDot) elementTransform(tree)
- else {
- val x: TermName = c.freshName()
- val wrapped = reifyIterable(Ident(x), n.pred)
- val xToWrapped = Function(List(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree)), wrapped)
- Select(Apply(Select(tree, nme.map), List(xToWrapped)), nme.toList)
- }
- if (tree.tpe != null && (tree.tpe <:< listTreeType || tree.tpe <:< listListTreeType)) tree
- else atPos(tree.pos)(loop(tree, n))
- }
- val card = parseCardinality(tpe)._1
- if (card != NoDot) Some(reifyIterable(_, card)) else None
- }
+ class ApplyHole(card: Cardinality, splicee: Tree) extends Hole {
+ val (strippedTpe: Type, tpe: Type) = {
+ if (stripIterable(splicee.tpe)._1.value < card.value) cantSplice()
+ val (_, strippedTpe) = stripIterable(splicee.tpe, limit = Some(card))
+ if (isBottomType(strippedTpe)) cantSplice()
+ else if (isNativeType(strippedTpe)) (strippedTpe, iterableTypeFromCard(card, strippedTpe))
+ else if (isLiftableType(strippedTpe)) (strippedTpe, iterableTypeFromCard(card, treeType))
+ else cantSplice()
}
- object NativeType extends HoleTypeExtractor {
- def preprocessor(tpe: Type) = Some(identity)
- def location(tpe: Type) = {
- if (tpe <:< treeType) Some(TreeLocation(tpe))
- else if (tpe <:< nameType) Some(NameLocation)
- else if (tpe <:< modsType) Some(ModsLocation)
- else if (tpe <:< flagsType) Some(FlagsLocation)
- else if (tpe <:< symbolType) Some(SymbolLocation)
- else None
- }
+ val tree = {
+ def inner(itpe: Type)(tree: Tree) =
+ if (isNativeType(itpe)) tree
+ else if (isLiftableType(itpe)) lifted(itpe)(tree)
+ else global.abort("unreachable")
+ if (card == NoDot) inner(strippedTpe)(splicee)
+ else iterated(card, strippedTpe, inner(strippedTpe))(splicee)
}
- object LiftableType extends HoleTypeExtractor {
- def preprocessor(tpe: Type) = lifter(tpe)
- def location(tpe: Type) = Some(TreeLocation(treeType))
+ val pos = splicee.pos
+
+ val cardinality = stripIterable(tpe)._1
+
+ protected def cantSplice(): Nothing = {
+ val (iterableCard, iterableType) = stripIterable(splicee.tpe)
+ val holeCardMsg = if (card != NoDot) s" with $card" else ""
+ val action = "splice " + splicee.tpe + holeCardMsg
+ val suggestCard = card != iterableCard || card != NoDot
+ val spliceeCardMsg = if (card != iterableCard && iterableCard != NoDot) s"using $iterableCard" else "omitting the dots"
+ val cardSuggestion = if (suggestCard) spliceeCardMsg else ""
+ val suggestLifting = (card == NoDot || iterableCard != NoDot) && !(iterableType <:< treeType) && !isLiftableType(iterableType)
+ val liftedTpe = if (card != NoDot) iterableType else splicee.tpe
+ val liftSuggestion = if (suggestLifting) s"providing an implicit instance of Liftable[$liftedTpe]" else ""
+ val advice =
+ if (isBottomType(iterableType)) "bottom type values often indicate programmer mistake"
+ else "consider " + List(cardSuggestion, liftSuggestion).filter(_ != "").mkString(" or ")
+ c.abort(splicee.pos, s"Can't $action, $advice")
}
- object IterableTreeType extends HoleTypeExtractor {
- def preprocessor(tpe: Type) = iterator(tpe)(identity)
- def location(tpe: Type) = {
- val (card, elementTpe) = parseCardinality(tpe)
- if (card != NoDot && elementTpe <:< treeType) Some(IterableLocation(card, TreeLocation(elementTpe)))
- else None
- }
+ protected def lifted(tpe: Type)(tree: Tree): Tree = {
+ val lifter = inferLiftable(tpe)
+ assert(lifter != EmptyTree, s"couldnt find a liftable for $tpe")
+ val lifted = Apply(lifter, List(tree))
+ val targetType = Select(u, tpnme.Tree)
+ atPos(tree.pos)(TypeApply(Select(lifted, nme.asInstanceOf_), List(targetType)))
}
- object IterableLiftableType extends HoleTypeExtractor {
- def preprocessor(tpe: Type) = {
- val (_, elementTpe) = parseCardinality(tpe)
- for {
- lifter <- this.lifter(elementTpe)
- iterator <- this.iterator(tpe)(lifter)
- } yield iterator
+ protected def iterated(card: Cardinality, tpe: Type, elementTransform: Tree => Tree = identity)(tree: Tree): Tree = {
+ assert(card != NoDot)
+ def reifyIterable(tree: Tree, n: Cardinality): Tree = {
+ def loop(tree: Tree, n: Cardinality): Tree =
+ if (n == NoDot) elementTransform(tree)
+ else {
+ val x: TermName = c.freshName()
+ val wrapped = reifyIterable(Ident(x), n.pred)
+ val xToWrapped = Function(List(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree)), wrapped)
+ Select(Apply(Select(tree, nme.map), List(xToWrapped)), nme.toList)
+ }
+ if (tree.tpe != null && (tree.tpe <:< listTreeType || tree.tpe <:< listListTreeType)) tree
+ else atPos(tree.pos)(loop(tree, n))
}
- def location(tpe: Type) = Some(IterableLocation(cardinality(tpe), TreeLocation(treeType)))
+ reifyIterable(tree, card)
}
}
- /** Hole encapsulates information about splices in quasiquotes.
- * It packs together a cardinality of a splice, a splicee (possibly preprocessed)
- * and the description of the location in Scala syntax where the splicee can be spliced.
- * This is the third pillar of modularity in the quasiquote reifier.
- */
- case class Hole(tree: Tree, location: Location, cardinality: Cardinality)
-
- object Hole {
- def apply(splicee: Tree, holeCard: Cardinality): Hole = {
- if (method == nme.unapply) return new Hole(splicee, UnknownLocation, holeCard)
- val (spliceeCard, elementTpe) = parseCardinality(splicee.tpe)
- def cantSplice() = {
- val holeCardMsg = if (holeCard != NoDot) s" with $holeCard" else ""
- val action = "splice " + splicee.tpe + holeCardMsg
- val suggestCard = holeCard != spliceeCard || holeCard != NoDot
- val spliceeCardMsg = if (holeCard != spliceeCard && spliceeCard != NoDot) s"using $spliceeCard" else "omitting the dots"
- val cardSuggestion = if (suggestCard) spliceeCardMsg else ""
- def canBeLifted(tpe: Type) = HoleType.LiftableType.unapply(tpe).nonEmpty
- val suggestLifting = (holeCard == NoDot || spliceeCard != NoDot) && !(elementTpe <:< treeType) && !canBeLifted(elementTpe)
- val liftedTpe = if (holeCard != NoDot) elementTpe else splicee.tpe
- val liftSuggestion = if (suggestLifting) s"providing an implicit instance of Liftable[$liftedTpe]" else ""
- val advice = List(cardSuggestion, liftSuggestion).filter(_ != "").mkString(" or ")
- c.abort(splicee.pos, s"Can't $action, consider $advice")
- }
- val holeTpe = splicee.tpe match {
- case _ if holeCard != spliceeCard => cantSplice()
- case HoleType(holeTpe) => holeTpe
- case _ => cantSplice()
- }
- holeTpe.makeHole(splicee)
+ class UnapplyHole(val cardinality: Cardinality, pat: Tree) extends Hole {
+ val (tree, pos) = pat match {
+ case Bind(pname, inner) => (Bind(pname, Ident(nme.WILDCARD)), inner.pos)
}
}
-
- def parseCardinality(tpe: Type): (Cardinality, Type) = {
- if (tpe != null && isIterableType(tpe)) {
- val (card, innerTpe) = parseCardinality(tpe.typeArguments.head)
- (card.succ, innerTpe)
- } else (NoDot, tpe)
- }
-} \ No newline at end of file
+}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
index 54be9123c7..bdb44ad9a2 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
@@ -13,6 +13,7 @@ import scala.collection.{immutable, mutable}
trait Placeholders { self: Quasiquotes =>
import global._
import Cardinality._
+ import universeTypes._
// Step 1: Transform Scala source with holes into vanilla Scala source
@@ -32,13 +33,17 @@ trait Placeholders { self: Quasiquotes =>
def appendHole(tree: Tree, cardinality: Cardinality) = {
val placeholderName = c.freshName(TermName(nme.QUASIQUOTE_PREFIX + sessionSuffix))
sb.append(placeholderName)
- val holeTree = if (method == nme.unapply) Bind(placeholderName, Ident(nme.WILDCARD)) else tree
- holeMap(placeholderName) = Hole(holeTree, cardinality)
+ val holeTree =
+ if (method != nme.unapply) tree
+ else Bind(placeholderName, tree)
+ holeMap(placeholderName) = Hole(cardinality, holeTree)
}
val iargs = method match {
case nme.apply => args
- case nme.unapply => List.fill(parts.length - 1)(EmptyTree)
+ case nme.unapply =>
+ val (dummy @ Ident(nme.SELECTOR_DUMMY)) :: Nil = args
+ dummy.attachments.get[SubpatternsAttachment].get.patterns
case _ => global.abort("unreachable")
}
@@ -78,9 +83,9 @@ trait Placeholders { self: Quasiquotes =>
trait HolePlaceholder {
def matching: PartialFunction[Any, Name]
- def unapply(scrutinee: Any): Option[(Tree, Location, Cardinality)] = {
+ def unapply(scrutinee: Any): Option[Hole] = {
val name = matching.lift(scrutinee)
- name.flatMap { holeMap.get(_).map { case Hole(repr, loc, card) => (repr, loc, card) } }
+ name.flatMap { holeMap.get(_) }
}
}
@@ -128,44 +133,44 @@ trait Placeholders { self: Quasiquotes =>
}
object SymbolPlaceholder {
- def unapply(scrutinee: Any): Option[Tree] = scrutinee match {
- case Placeholder(tree, SymbolLocation, _) => Some(tree)
+ def unapply(scrutinee: Any): Option[Hole] = scrutinee match {
+ case Placeholder(hole: ApplyHole) if hole.tpe <:< symbolType => Some(hole)
case _ => None
}
}
object CasePlaceholder {
- def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
- case CaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), List(Placeholder(tree, location, card))), EmptyTree, EmptyTree) => Some((tree, location, card))
+ def unapply(tree: Tree): Option[Hole] = tree match {
+ case CaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), List(Placeholder(hole))), EmptyTree, EmptyTree) => Some(hole)
case _ => None
}
}
object RefineStatPlaceholder {
- def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
- case ValDef(_, Placeholder(tree, location, card), Ident(tpnme.QUASIQUOTE_REFINE_STAT), _) => Some((tree, location, card))
+ def unapply(tree: Tree): Option[Hole] = tree match {
+ case ValDef(_, Placeholder(hole), Ident(tpnme.QUASIQUOTE_REFINE_STAT), _) => Some(hole)
case _ => None
}
}
object EarlyDefPlaceholder {
- def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
- case ValDef(_, Placeholder(tree, location, card), Ident(tpnme.QUASIQUOTE_EARLY_DEF), _) => Some((tree, location, card))
+ def unapply(tree: Tree): Option[Hole] = tree match {
+ case ValDef(_, Placeholder(hole), Ident(tpnme.QUASIQUOTE_EARLY_DEF), _) => Some(hole)
case _ => None
}
}
object PackageStatPlaceholder {
- def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
- case ValDef(NoMods, Placeholder(tree, location, card), Ident(tpnme.QUASIQUOTE_PACKAGE_STAT), EmptyTree) => Some((tree, location, card))
+ def unapply(tree: Tree): Option[Hole] = tree match {
+ case ValDef(NoMods, Placeholder(hole), Ident(tpnme.QUASIQUOTE_PACKAGE_STAT), EmptyTree) => Some(hole)
case _ => None
}
}
object ForEnumPlaceholder {
- def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
- case build.SyntacticValFrom(Bind(Placeholder(tree, location, card), Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) =>
- Some((tree, location, card))
+ def unapply(tree: Tree): Option[Hole] = tree match {
+ case build.SyntacticValFrom(Bind(Placeholder(hole), Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) =>
+ Some(hole)
case _ => None
}
}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
index d5a310668e..6bd7663526 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
@@ -13,7 +13,7 @@ trait Reifiers { self: Quasiquotes =>
import Cardinality._
import universeTypes._
- abstract class Reifier extends {
+ abstract class Reifier(val isReifyingExpressions: Boolean) extends {
val global: self.global.type = self.global
val universe = self.universe
val reifee = EmptyTree
@@ -22,7 +22,6 @@ trait Reifiers { self: Quasiquotes =>
} with ReflectReifier {
lazy val typer = throw new UnsupportedOperationException
- def isReifyingExpressions: Boolean
def isReifyingPatterns: Boolean = !isReifyingExpressions
def action = if (isReifyingExpressions) "splice" else "extract"
def holesHaveTypes = isReifyingExpressions
@@ -107,7 +106,7 @@ trait Reifiers { self: Quasiquotes =>
def reifyFillingHoles(tree: Tree): Tree = {
val reified = reifyTree(tree)
holeMap.unused.foreach { hole =>
- c.abort(holeMap(hole).tree.pos, s"Don't know how to $action here")
+ c.abort(holeMap(hole).pos, s"Don't know how to $action here")
}
wrap(reified)
}
@@ -117,21 +116,27 @@ trait Reifiers { self: Quasiquotes =>
reifyTreeSyntactically(tree)
def reifyTreePlaceholder(tree: Tree): Tree = tree match {
- case Placeholder(tree, TreeLocation(_), _) if isReifyingExpressions => tree
- case Placeholder(tree, _, NoDot) if isReifyingPatterns => tree
- case Placeholder(tree, _, card @ Dot()) => c.abort(tree.pos, s"Can't $action with $card here")
+ case Placeholder(hole: ApplyHole) if hole.tpe <:< treeType => hole.tree
+ case Placeholder(Hole(tree, NoDot)) if isReifyingPatterns => tree
+ case Placeholder(hole @ Hole(_, card @ Dot())) => c.abort(hole.pos, s"Can't $action with $card here")
case TuplePlaceholder(args) => reifyTuple(args)
case TupleTypePlaceholder(args) => reifyTupleType(args)
case FunctionTypePlaceholder(argtpes, restpe) => reifyFunctionType(argtpes, restpe)
- case CasePlaceholder(tree, _, _) => tree
- case RefineStatPlaceholder(tree, _, _) => reifyRefineStat(tree)
- case EarlyDefPlaceholder(tree, _, _) => reifyEarlyDef(tree)
- case PackageStatPlaceholder(tree, _, _) => reifyPackageStat(tree)
- case ForEnumPlaceholder(tree, _, _) => tree
+ case CasePlaceholder(hole) => hole.tree
+ case RefineStatPlaceholder(hole) => reifyRefineStat(hole)
+ case EarlyDefPlaceholder(hole) => reifyEarlyDef(hole)
+ case PackageStatPlaceholder(hole) => reifyPackageStat(hole)
+ // for enumerators are checked not during splicing but during
+ // desugaring of the for loop in SyntacticFor & SyntacticForYield
+ case ForEnumPlaceholder(hole) => hole.tree
case _ => EmptyTree
}
override def reifyTreeSyntactically(tree: Tree) = tree match {
+ case RefTree(qual, SymbolPlaceholder(Hole(tree, _))) if isReifyingExpressions =>
+ mirrorBuildCall(nme.RefTree, reify(qual), tree)
+ case This(SymbolPlaceholder(Hole(tree, _))) if isReifyingExpressions =>
+ mirrorCall(nme.This, tree)
case SyntacticTraitDef(mods, name, tparams, earlyDefs, parents, selfdef, body) =>
reifyBuildCall(nme.SyntacticTraitDef, mods, name, tparams, earlyDefs, parents, selfdef, body)
case SyntacticClassDef(mods, name, tparams, constrmods, vparamss, earlyDefs, parents, selfdef, body) =>
@@ -169,7 +174,7 @@ trait Reifiers { self: Quasiquotes =>
reifyBuildCall(nme.SyntacticFunction, args, body)
case SyntacticIdent(name, isBackquoted) =>
reifyBuildCall(nme.SyntacticIdent, name, isBackquoted)
- case Block(Nil, Placeholder(tree, _, DotDot)) =>
+ case Block(Nil, Placeholder(Hole(tree, DotDot))) =>
mirrorBuildCall(nme.SyntacticBlock, tree)
case Block(Nil, other) =>
reifyTree(other)
@@ -190,9 +195,10 @@ trait Reifiers { self: Quasiquotes =>
}
override def reifyName(name: Name): Tree = name match {
- case Placeholder(tree, location, _) =>
- if (holesHaveTypes && !(location.tpe <:< nameType)) c.abort(tree.pos, s"$nameType expected but ${location.tpe} found")
- tree
+ case Placeholder(hole: ApplyHole) =>
+ if (!(hole.tpe <:< nameType)) c.abort(hole.pos, s"$nameType expected but ${hole.tpe} found")
+ hole.tree
+ case Placeholder(hole: UnapplyHole) => hole.tree
case FreshName(prefix) if prefix != nme.QUASIQUOTE_NAME_PREFIX =>
def fresh() = c.freshName[TermName](nme.QUASIQUOTE_NAME_PREFIX)
def introduceName() = { val n = fresh(); nameMap(name) += n; n}
@@ -205,8 +211,8 @@ trait Reifiers { self: Quasiquotes =>
def reifyTuple(args: List[Tree]) = args match {
case Nil => reify(Literal(Constant(())))
- case List(hole @ Placeholder(_, _, NoDot)) => reify(hole)
- case List(Placeholder(_, _, _)) => reifyBuildCall(nme.SyntacticTuple, args)
+ case List(hole @ Placeholder(Hole(_, NoDot))) => reify(hole)
+ case List(Placeholder(_)) => reifyBuildCall(nme.SyntacticTuple, args)
// in a case we only have one element tuple without
// any cardinality annotations this means that this is
// just an expression wrapped in parentheses
@@ -216,8 +222,8 @@ trait Reifiers { self: Quasiquotes =>
def reifyTupleType(args: List[Tree]) = args match {
case Nil => reify(Select(Ident(nme.scala_), tpnme.Unit))
- case List(hole @ Placeholder(_, _, NoDot)) => reify(hole)
- case List(Placeholder(_, _, _)) => reifyBuildCall(nme.SyntacticTupleType, args)
+ case List(hole @ Placeholder(Hole(_, NoDot))) => reify(hole)
+ case List(Placeholder(_)) => reifyBuildCall(nme.SyntacticTupleType, args)
case List(other) => reify(other)
case _ => reifyBuildCall(nme.SyntacticTupleType, args)
}
@@ -225,13 +231,18 @@ trait Reifiers { self: Quasiquotes =>
def reifyFunctionType(argtpes: List[Tree], restpe: Tree) =
reifyBuildCall(nme.SyntacticFunctionType, argtpes, restpe)
- def reifyRefineStat(tree: Tree) = tree
+ def reifyConstructionCheck(name: TermName, hole: Hole) = hole match {
+ case _: UnapplyHole => hole.tree
+ case _: ApplyHole => mirrorBuildCall(name, hole.tree)
+ }
+
+ def reifyRefineStat(hole: Hole) = reifyConstructionCheck(nme.mkRefineStat, hole)
- def reifyEarlyDef(tree: Tree) = tree
+ def reifyEarlyDef(hole: Hole) = reifyConstructionCheck(nme.mkEarlyDef, hole)
- def reifyAnnotation(tree: Tree) = tree
+ def reifyAnnotation(hole: Hole) = reifyConstructionCheck(nme.mkAnnotation, hole)
- def reifyPackageStat(tree: Tree) = tree
+ def reifyPackageStat(hole: Hole) = reifyConstructionCheck(nme.mkPackageStat, hole)
/** Splits list into a list of groups where subsequent elements are considered
* similar by the corresponding function.
@@ -264,7 +275,7 @@ trait Reifiers { self: Quasiquotes =>
*
* reifyMultiCardinalityList(lst) {
* // first we define patterns that extract high-cardinality holeMap (currently ..)
- * case Placeholder(CorrespondsTo(tree, tpe)) if tpe <:< iterableTreeType => tree
+ * case Placeholder(IterableType(_, _)) => tree
* } {
* // in the end we define how single elements are reified, typically with default reify call
* reify(_)
@@ -283,21 +294,22 @@ trait Reifiers { self: Quasiquotes =>
* elements.
*/
override def reifyList(xs: List[Any]): Tree = reifyMultiCardinalityList(xs) {
- case Placeholder(tree, _, DotDot) => tree
- case CasePlaceholder(tree, _, DotDot) => tree
- case RefineStatPlaceholder(tree, _, DotDot) => reifyRefineStat(tree)
- case EarlyDefPlaceholder(tree, _, DotDot) => reifyEarlyDef(tree)
- case PackageStatPlaceholder(tree, _, DotDot) => reifyPackageStat(tree)
- case ForEnumPlaceholder(tree, _, DotDot) => tree
- case List(Placeholder(tree, _, DotDotDot)) => tree
+ case Placeholder(Hole(tree, DotDot)) => tree
+ case CasePlaceholder(Hole(tree, DotDot)) => tree
+ case RefineStatPlaceholder(h @ Hole(_, DotDot)) => reifyRefineStat(h)
+ case EarlyDefPlaceholder(h @ Hole(_, DotDot)) => reifyEarlyDef(h)
+ case PackageStatPlaceholder(h @ Hole(_, DotDot)) => reifyPackageStat(h)
+ case ForEnumPlaceholder(Hole(tree, DotDot)) => tree
+ case List(Placeholder(Hole(tree, DotDotDot))) => tree
} {
reify(_)
}
def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) {
- case AnnotPlaceholder(tree, _, DotDot) => reifyAnnotation(tree)
+ case AnnotPlaceholder(h @ Hole(_, DotDot)) => reifyAnnotation(h)
} {
- case AnnotPlaceholder(tree, UnknownLocation | TreeLocation(_), NoDot) => reifyAnnotation(tree)
+ case AnnotPlaceholder(h: ApplyHole) if h.tpe <:< treeType => reifyAnnotation(h)
+ case AnnotPlaceholder(h: UnapplyHole) if h.cardinality == NoDot => reifyAnnotation(h)
case other => reify(other)
}
@@ -323,78 +335,55 @@ trait Reifiers { self: Quasiquotes =>
override def mirrorBuildCall(name: TermName, args: Tree*): Tree =
Apply(Select(Select(universe, nme.build), name), args.toList)
- }
-
- class ApplyReifier extends Reifier {
- def isReifyingExpressions = true
- override def reifyTreeSyntactically(tree: Tree): Tree = tree match {
- case RefTree(qual, SymbolPlaceholder(tree)) =>
- mirrorBuildCall(nme.RefTree, reify(qual), tree)
- case This(SymbolPlaceholder(tree)) =>
- mirrorCall(nme.This, tree)
- case _ =>
- super.reifyTreeSyntactically(tree)
- }
+ override def scalaFactoryCall(name: String, args: Tree*): Tree =
+ call("scala." + name, args: _*)
+ }
- override def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree = xs match {
- case Nil => mkList(Nil)
- case _ =>
+ class ApplyReifier extends Reifier(isReifyingExpressions = true) {
+ def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree =
+ if (xs.isEmpty) mkList(Nil)
+ else {
def reifyGroup(group: List[T]): Tree = group match {
case List(elem) if fill.isDefinedAt(elem) => fill(elem)
case elems => mkList(elems.map(fallback))
}
val head :: tail = group(xs) { (a, b) => !fill.isDefinedAt(a) && !fill.isDefinedAt(b) }
tail.foldLeft[Tree](reifyGroup(head)) { (tree, lst) => Apply(Select(tree, nme.PLUSPLUS), List(reifyGroup(lst))) }
- }
+ }
override def reifyModifiers(m: Modifiers) =
if (m == NoMods) super.reifyModifiers(m)
else {
val (modsPlaceholders, annots) = m.annotations.partition {
- case ModsPlaceholder(_, _, _) => true
+ case ModsPlaceholder(_) => true
case _ => false
}
val (mods, flags) = modsPlaceholders.map {
- case ModsPlaceholder(tree, location, card) => (tree, location)
- }.partition { case (tree, location) =>
- location match {
- case ModsLocation => true
- case FlagsLocation => false
- case _ => c.abort(tree.pos, s"$flagsType or $modsType expected but ${tree.tpe} found")
- }
+ case ModsPlaceholder(hole: ApplyHole) => hole
+ }.partition { hole =>
+ if (hole.tpe <:< modsType) true
+ else if (hole.tpe <:< flagsType) false
+ else c.abort(hole.pos, s"$flagsType or $modsType expected but ${hole.tpe} found")
}
mods match {
- case (tree, _) :: Nil =>
- if (flags.nonEmpty) c.abort(flags(0)._1.pos, "Can't splice flags together with modifiers, consider merging flags into modifiers")
- if (annots.nonEmpty) c.abort(tree.pos, "Can't splice modifiers together with annotations, consider merging annotations into modifiers")
- ensureNoExplicitFlags(m, tree.pos)
- tree
- case _ :: (second, _) :: Nil =>
- c.abort(second.pos, "Can't splice multiple modifiers, consider merging them into a single modifiers instance")
+ case hole :: Nil =>
+ if (flags.nonEmpty) c.abort(flags(0).pos, "Can't splice flags together with modifiers, consider merging flags into modifiers")
+ if (annots.nonEmpty) c.abort(hole.pos, "Can't splice modifiers together with annotations, consider merging annotations into modifiers")
+ ensureNoExplicitFlags(m, hole.pos)
+ hole.tree
+ case _ :: hole :: Nil =>
+ c.abort(hole.pos, "Can't splice multiple modifiers, consider merging them into a single modifiers instance")
case _ =>
val baseFlags = reifyFlags(m.flags)
- val reifiedFlags = flags.foldLeft[Tree](baseFlags) { case (flag, (tree, _)) => Apply(Select(flag, nme.OR), List(tree)) }
+ val reifiedFlags = flags.foldLeft[Tree](baseFlags) { case (flag, hole) => Apply(Select(flag, nme.OR), List(hole.tree)) }
mirrorFactoryCall(nme.Modifiers, reifiedFlags, reify(m.privateWithin), reifyAnnotList(annots))
}
}
- override def reifyRefineStat(tree: Tree) = mirrorBuildCall(nme.mkRefineStat, tree)
-
- override def reifyEarlyDef(tree: Tree) = mirrorBuildCall(nme.mkEarlyDef, tree)
-
- override def reifyAnnotation(tree: Tree) = mirrorBuildCall(nme.mkAnnotation, tree)
-
- override def reifyPackageStat(tree: Tree) = mirrorBuildCall(nme.mkPackageStat, tree)
}
-
- class UnapplyReifier extends Reifier {
- def isReifyingExpressions = false
-
- override def scalaFactoryCall(name: String, args: Tree*): Tree =
- call("scala." + name, args: _*)
-
- override def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree) = xs match {
+ class UnapplyReifier extends Reifier(isReifyingExpressions = false) {
+ def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree = xs match {
case init :+ last if fill.isDefinedAt(last) =>
init.foldRight[Tree](fill(last)) { (el, rest) =>
val cons = Select(Select(Select(Ident(nme.scala_), nme.collection), nme.immutable), nme.CONS)
@@ -407,14 +396,14 @@ trait Reifiers { self: Quasiquotes =>
override def reifyModifiers(m: Modifiers) =
if (m == NoMods) super.reifyModifiers(m)
else {
- val mods = m.annotations.collect { case ModsPlaceholder(tree, _, _) => tree }
+ val mods = m.annotations.collect { case ModsPlaceholder(hole: UnapplyHole) => hole }
mods match {
- case tree :: Nil =>
- if (m.annotations.length != 1) c.abort(tree.pos, "Can't extract modifiers together with annotations, consider extracting just modifiers")
- ensureNoExplicitFlags(m, tree.pos)
- tree
- case _ :: second :: rest =>
- c.abort(second.pos, "Can't extract multiple modifiers together, consider extracting a single modifiers instance")
+ case hole :: Nil =>
+ if (m.annotations.length != 1) c.abort(hole.pos, "Can't extract modifiers together with annotations, consider extracting just modifiers")
+ ensureNoExplicitFlags(m, hole.pos)
+ hole.tree
+ case _ :: hole :: _ =>
+ c.abort(hole.pos, "Can't extract multiple modifiers together, consider extracting a single modifiers instance")
case Nil =>
mirrorFactoryCall(nme.Modifiers, reifyFlags(m.flags), reify(m.privateWithin), reifyAnnotList(m.annotations))
}
diff --git a/test/files/neg/si7980.check b/test/files/neg/si7980.check
new file mode 100644
index 0000000000..b085cabf1d
--- /dev/null
+++ b/test/files/neg/si7980.check
@@ -0,0 +1,4 @@
+si7980.scala:7: error: Can't splice Nothing, bottom type values often indicate programmer mistake
+ println(q"class ${Name(X)} { }")
+ ^
+one error found
diff --git a/test/files/neg/si7980.scala b/test/files/neg/si7980.scala
new file mode 100644
index 0000000000..b21907de54
--- /dev/null
+++ b/test/files/neg/si7980.scala
@@ -0,0 +1,8 @@
+object Test extends App {
+ import scala.reflect.runtime.universe._
+ def Name[T:TypeTag](name:String): T = implicitly[TypeTag[T]] match {
+ case t => newTypeName(name).asInstanceOf[T]
+ }
+ val X = "ASDF"
+ println(q"class ${Name(X)} { }")
+}
diff --git a/test/files/scalacheck/quasiquotes/ErrorProps.scala b/test/files/scalacheck/quasiquotes/ErrorProps.scala
index adad48fcdf..92d299bede 100644
--- a/test/files/scalacheck/quasiquotes/ErrorProps.scala
+++ b/test/files/scalacheck/quasiquotes/ErrorProps.scala
@@ -172,6 +172,20 @@ object ErrorProps extends QuasiquoteProperties("errors") {
val q"$m1 $m2 def foo" = EmptyTree
""")
+ property("can't splice values of Null") = fails(
+ "Can't splice Null, bottom type values often indicate programmer mistake",
+ """
+ val n = null
+ q"$n"
+ """)
+
+ property("can't splice values of Nothing") = fails(
+ "Can't splice Nothing, bottom type values often indicate programmer mistake",
+ """
+ def n = ???
+ q"$n"
+ """)
+
// // Make sure a nice error is reported in this case
// { import Flag._; val mods = NoMods; q"lazy $mods val x: Int" }
} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/LiftableProps.scala b/test/files/scalacheck/quasiquotes/LiftableProps.scala
index 1271e1accd..2173337edd 100644
--- a/test/files/scalacheck/quasiquotes/LiftableProps.scala
+++ b/test/files/scalacheck/quasiquotes/LiftableProps.scala
@@ -76,4 +76,14 @@ object LiftableProps extends QuasiquoteProperties("liftable") {
val const = Constant(0)
assert(q"$const" ≈ q"0")
}
+
+ property("lift list variants") = test {
+ val lst = List(1, 2)
+ val immutable = q"$scalapkg.collection.immutable"
+ assert(q"$lst" ≈ q"$immutable.List(1, 2)")
+ assert(q"f(..$lst)" ≈ q"f(1, 2)")
+ val llst = List(List(1), List(2))
+ assert(q"f(..$llst)" ≈ q"f($immutable.List(1), $immutable.List(2))")
+ assert(q"f(...$llst)" ≈ q"f(1)(2)")
+ }
} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
index 338c379fc3..6fb05ff9a4 100644
--- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
@@ -204,6 +204,11 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") {
assert(q"f(${if (true) q"a" else q"b"})" ≈ q"f(a)")
}
+ property("splice iterable of non-parametric type") = test {
+ object O extends Iterable[Tree] { def iterator = List(q"foo").iterator }
+ q"f(..$O)"
+ }
+
property("SI-8016") = test {
val xs = q"1" :: q"2" :: Nil
assertEqAst(q"..$xs", "{1; 2}")