diff options
Diffstat (limited to 'src/compiler')
8 files changed, 344 insertions, 300 deletions
diff --git a/src/compiler/scala/tools/nsc/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala index d194c095f8..183752d4a2 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugin.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala @@ -12,7 +12,8 @@ import scala.reflect.io.{ Directory, File, Path } import java.io.InputStream import java.util.zip.ZipException -import scala.collection.mutable.ListBuffer +import scala.collection.mutable +import mutable.ListBuffer import scala.util.{ Try, Success, Failure } /** Information about a plugin loaded from a jar file. @@ -99,7 +100,7 @@ object Plugin { private def loadDescriptionFromJar(jarp: Path): Try[PluginDescription] = { // XXX Return to this once we have more ARM support def read(is: Option[InputStream]) = is match { - case None => throw new RuntimeException(s"Missing $PluginXML in $jarp") + case None => throw new PluginLoadException(jarp.path, s"Missing $PluginXML in $jarp") case Some(is) => PluginDescription.fromXML(is) } Try(new Jar(jarp.jfile).withEntryStream(PluginXML)(read)) @@ -113,11 +114,14 @@ object Plugin { /** Use a class loader to load the plugin class. */ def load(classname: String, loader: ClassLoader): Try[AnyClass] = { - Try[AnyClass] { - loader loadClass classname - } recoverWith { - case _: Exception => - Failure(new RuntimeException(s"Warning: class not found: ${classname}")) + import scala.util.control.NonFatal + try { + Success[AnyClass](loader loadClass classname) + } catch { + case NonFatal(e) => + Failure(new PluginLoadException(classname, s"Error: unable to load class: $classname")) + case e: NoClassDefFoundError => + Failure(new PluginLoadException(classname, s"Error: class not found: ${e.getMessage} required by $classname")) } } @@ -128,33 +132,54 @@ object Plugin { * A single classloader is created and used to load all of them. */ def loadAllFrom( - jars: List[Path], + paths: List[List[Path]], dirs: List[Path], ignoring: List[String]): List[Try[AnyClass]] = { - // List[(jar, Success(descriptor))] in dir - def scan(d: Directory) = for { - f <- d.files.toList sortBy (_.name) - if Jar isJarOrZip f - pd = loadDescriptionFromJar(f) - if pd.isSuccess - } yield (f, pd) - // (dir, Try(descriptor)) - def explode(d: Directory) = d -> loadDescriptionFromFile(d / PluginXML) - // (j, Try(descriptor)) - def required(j: Path) = j -> loadDescriptionFromJar(j) - - type Paired = Tuple2[Path, Try[PluginDescription]] - val included: List[Paired] = (dirs flatMap (_ ifDirectory scan)).flatten - val exploded: List[Paired] = jars flatMap (_ ifDirectory explode) - val explicit: List[Paired] = jars flatMap (_ ifFile required) - def ignored(p: Paired) = p match { - case (path, Success(pd)) => ignoring contains pd.name - case _ => false + // List[(jar, Try(descriptor))] in dir + def scan(d: Directory) = + d.files.toList sortBy (_.name) filter (Jar isJarOrZip _) map (j => (j, loadDescriptionFromJar(j))) + + type PDResults = List[Try[(PluginDescription, ScalaClassLoader)]] + + // scan plugin dirs for jars containing plugins, ignoring dirs with none and other jars + val fromDirs: PDResults = dirs filter (_.isDirectory) flatMap { d => + scan(d.toDirectory) collect { + case (j, Success(pd)) => Success((pd, loaderFor(Seq(j)))) + } + } + + // scan jar paths for plugins, taking the first plugin you find. + // a path element can be either a plugin.jar or an exploded dir. + def findDescriptor(ps: List[Path]) = { + def loop(qs: List[Path]): Try[PluginDescription] = qs match { + case Nil => Failure(new MissingPluginException(ps)) + case p :: rest => + if (p.isDirectory) loadDescriptionFromFile(p.toDirectory / PluginXML) + else if (p.isFile) loadDescriptionFromJar(p.toFile) + else loop(rest) + } + loop(ps) + } + val fromPaths: PDResults = paths map (p => (p, findDescriptor(p))) map { + case (p, Success(pd)) => Success((pd, loaderFor(p))) + case (_, Failure(e)) => Failure(e) } - val (locs, pds) = ((explicit ::: exploded ::: included) filterNot ignored).unzip - val loader = loaderFor(locs.distinct) - (pds filter (_.isSuccess) map (_.get.classname)).distinct map (Plugin load (_, loader)) + + val seen = mutable.HashSet[String]() + val enabled = (fromPaths ::: fromDirs) map { + case Success((pd, loader)) if seen(pd.classname) => + // a nod to SI-7494, take the plugin classes distinctly + Failure(new PluginLoadException(pd.name, s"Ignoring duplicate plugin ${pd.name} (${pd.classname})")) + case Success((pd, loader)) if ignoring contains pd.name => + Failure(new PluginLoadException(pd.name, s"Disabling plugin ${pd.name}")) + case Success((pd, loader)) => + seen += pd.classname + Plugin.load(pd.classname, loader) + case Failure(e) => + Failure(e) + } + enabled // distinct and not disabled } /** Instantiate a plugin class, given the class and @@ -164,3 +189,11 @@ object Plugin { (clazz getConstructor classOf[Global] newInstance global).asInstanceOf[Plugin] } } + +class PluginLoadException(val path: String, message: String, cause: Exception) extends Exception(message, cause) { + def this(path: String, message: String) = this(path, message, null) +} + +class MissingPluginException(path: String) extends PluginLoadException(path, s"No plugin in path $path") { + def this(paths: List[Path]) = this(paths mkString File.pathSeparator) +} diff --git a/src/compiler/scala/tools/nsc/plugins/PluginLoadException.scala b/src/compiler/scala/tools/nsc/plugins/PluginLoadException.scala deleted file mode 100644 index c5da24993e..0000000000 --- a/src/compiler/scala/tools/nsc/plugins/PluginLoadException.scala +++ /dev/null @@ -1,15 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2007-2013 LAMP/EPFL - * @author Lex Spoon - */ - -package scala.tools.nsc -package plugins - -/** ... - * - * @author Lex Spoon - * @version 1.0, 2007-5-21 - */ -class PluginLoadException(filename: String, cause: Exception) -extends Exception(cause) diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala index 4769705404..12f9aeba27 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala @@ -8,6 +8,7 @@ package scala.tools.nsc package plugins import scala.reflect.io.{ File, Path } +import scala.tools.nsc.util.ClassPath import scala.tools.util.PathResolver.Defaults /** Support for run-time loading of compiler plugins. @@ -16,8 +17,7 @@ import scala.tools.util.PathResolver.Defaults * @version 1.1, 2009/1/2 * Updated 2009/1/2 by Anders Bach Nielsen: Added features to implement SIP 00002 */ -trait Plugins { - self: Global => +trait Plugins { global: Global => /** Load a rough list of the plugins. For speed, it * does not instantiate a compiler run. Therefore it cannot @@ -25,13 +25,20 @@ trait Plugins { * filtered from the final list of plugins. */ protected def loadRoughPluginsList(): List[Plugin] = { - val jars = settings.plugin.value map Path.apply - def injectDefault(s: String) = if (s.isEmpty) Defaults.scalaPluginPath else s - val dirs = (settings.pluginsDir.value split File.pathSeparator).toList map injectDefault map Path.apply - val maybes = Plugin.loadAllFrom(jars, dirs, settings.disable.value) + def asPath(p: String) = ClassPath split p + val paths = settings.plugin.value filter (_ != "") map (s => asPath(s) map Path.apply) + val dirs = { + def injectDefault(s: String) = if (s.isEmpty) Defaults.scalaPluginPath else s + asPath(settings.pluginsDir.value) map injectDefault map Path.apply + } + val maybes = Plugin.loadAllFrom(paths, dirs, settings.disable.value) val (goods, errors) = maybes partition (_.isSuccess) // Explicit parameterization of recover to suppress -Xlint warning about inferred Any - errors foreach (_.recover[Any] { case e: Exception => inform(e.getMessage) }) + errors foreach (_.recover[Any] { + // legacy behavior ignores altogether, so at least warn devs + case e: MissingPluginException => if (global.isDeveloper) warning(e.getMessage) + case e: Exception => inform(e.getMessage) + }) val classes = goods map (_.get) // flatten // Each plugin must only be instantiated once. A common pattern diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala index ba135d7d25..069d6d5fb2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -391,8 +391,10 @@ trait PatternTypers { else freshUnapplyArgType() ) ) + val unapplyArgTree = Ident(unapplyArg) updateAttachment SubpatternsAttachment(args) + // clearing the type is necessary so that ref will be stabilized; see bug 881 - val fun1 = typedPos(fun.pos)(Apply(Select(fun.clearType(), unapplyMethod), Ident(unapplyArg) :: Nil)) + val fun1 = typedPos(fun.pos)(Apply(Select(fun.clearType(), unapplyMethod), unapplyArgTree :: Nil)) def makeTypedUnApply() = { // the union of the expected type and the inferred type of the argument to unapply diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala index f92c9aa845..f5bcaf68e0 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala @@ -3,6 +3,7 @@ package quasiquotes import scala.collection.{immutable, mutable} import scala.reflect.internal.Flags._ +import scala.reflect.macros.TypecheckException class Cardinality private[Cardinality](val value: Int) extends AnyVal { def pred = { assert(value - 1 >= 0); new Cardinality(value - 1) } @@ -30,158 +31,173 @@ 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 inferUnliftable(tpe: Type): Tree = inferParamImplicit(unliftableType, 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(LiftableClass.toType, List(tpe)) - val lifter = c.inferImplicitValue(lifterTpe, silent = true) - if (lifter != EmptyTree) Some(tree => { - val lifted = Apply(lifter, List(u, 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, tpe): (Type, 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 (placeholderName, pos, tptopt) = pat match { + case Bind(pname, inner @ Bind(_, Typed(Ident(nme.WILDCARD), tpt))) => (pname, inner.pos, Some(tpt)) + case Bind(pname, inner @ Typed(Ident(nme.WILDCARD), tpt)) => (pname, inner.pos, Some(tpt)) + case Bind(pname, inner) => (pname, inner.pos, None) } + val treeNoUnlift = Bind(placeholderName, Ident(nme.WILDCARD)) + lazy val tree = + tptopt.map { tpt => + val TypeDef(_, _, _, typedTpt) = + try c.typeCheck(TypeDef(NoMods, TypeName("T"), Nil, tpt)) + catch { case TypecheckException(pos, msg) => c.abort(pos.asInstanceOf[c.Position], msg) } + val tpe = typedTpt.tpe + val (iterableCard, _) = stripIterable(tpe) + if (iterableCard.value < cardinality.value) + c.abort(pat.pos, s"Can't extract $tpe with $cardinality, consider using $iterableCard") + val (_, strippedTpe) = stripIterable(tpe, limit = Some(cardinality)) + if (strippedTpe <:< treeType) treeNoUnlift + else + unlifters.spawn(strippedTpe, cardinality).map { + Apply(_, treeNoUnlift :: Nil) + }.getOrElse { + c.abort(pat.pos, s"Can't find $unliftableType[$strippedTpe], consider providing it") + } + }.getOrElse { treeNoUnlift } } - def parseCardinality(tpe: Type): (Cardinality, Type) = { - if (tpe != null && isIterableType(tpe)) { - val (card, innerTpe) = parseCardinality(tpe.typeArguments.head) - (card.succ, innerTpe) - } else (NoDot, tpe) + /** Full support for unliftable implies that it's possible to interleave + * deconstruction with higher cardinality and unlifting of the values. + * In particular extraction of List[Tree] as List[T: Unliftable] requires + * helper extractors that would do the job: UnliftHelper1[T]. Similarly + * List[List[Tree]] needs UnliftHelper2[T]. + * + * See also "unlift list" tests in UnapplyProps.scala + */ + object unlifters { + private var records = List.empty[(Type, Cardinality)] + // Request an UnliftHelperN[T] where n == card and T == tpe. + // If card == 0 then helper is not needed and plain instance + // of unliftable is returned. + def spawn(tpe: Type, card: Cardinality): Option[Tree] = { + val unlifter = inferUnliftable(tpe) + if (unlifter == EmptyTree) None + else if (card == NoDot) Some(unlifter) + else { + val idx = records.indexWhere { p => p._1 =:= tpe && p._2 == card } + val resIdx = if (idx != -1) idx else { records +:= (tpe, card); records.length - 1} + Some(Ident(TermName(nme.QUASIQUOTE_UNLIFT_HELPER + resIdx))) + } + } + // Returns a list of vals that will defined required unlifters + def preamble(): List[Tree] = + records.zipWithIndex.map { case ((tpe, card), idx) => + val name = TermName(nme.QUASIQUOTE_UNLIFT_HELPER + idx) + val helperName = card match { case DotDot => nme.UnliftHelper1 case DotDotDot => nme.UnliftHelper2 } + val lifter = inferUnliftable(tpe) + assert(helperName.isTermName) + // q"val $name: $u.build.${helperName.toTypeName} = $u.build.$helperName($lifter)" + ValDef(NoMods, name, + AppliedTypeTree(Select(Select(u, nme.build), helperName.toTypeName), List(TypeTree(tpe))), + Apply(Select(Select(u, nme.build), helperName), lifter :: Nil)) + } } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index 126c14ac81..6e6b617e5c 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -161,7 +161,12 @@ trait Parsers { self: Quasiquotes => } object TermParser extends Parser { - def entryPoint = { parser => gen.mkTreeOrBlock(parser.templateOrTopStatSeq()) } + def entryPoint = { parser => + parser.templateOrTopStatSeq() match { + case head :: Nil => Block(Nil, head) + case lst => gen.mkTreeOrBlock(lst) + } + } } object TypeParser extends Parser { 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 b28c85cfc2..6d7aafe266 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 @@ -94,12 +93,12 @@ trait Reifiers { self: Quasiquotes => // cq"$tree if $guard => $succ" :: cq"_ => $fail" :: Nil CaseDef(tree, guard, succ) :: CaseDef(Ident(nme.WILDCARD), EmptyTree, fail) :: Nil } - // q"new { def unapply(tree: $AnyClass) = tree match { case ..$cases } }.unapply(..$args)" + // q"new { def unapply(tree: $AnyClass) = { ..${unlifters.preamble()}; tree match { case ..$cases } } }.unapply(..$args)" Apply( Select( SyntacticNew(Nil, Nil, noSelfType, List( DefDef(NoMods, nme.unapply, Nil, List(List(ValDef(NoMods, nme.tree, TypeTree(AnyClass.toType), EmptyTree))), TypeTree(), - Match(Ident(nme.tree), cases)))), + SyntacticBlock(unlifters.preamble() :+ Match(Ident(nme.tree), cases))))), nme.unapply), args) } @@ -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, location, _) => reifyCase(tree, location) - 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) => @@ -161,17 +166,24 @@ trait Reifiers { self: Quasiquotes => reifyBuildCall(nme.SyntacticForYield, enums, body) case SyntacticAssign(lhs, rhs) => reifyBuildCall(nme.SyntacticAssign, lhs, rhs) - case SyntacticApplied(fun, List(args)) - if args.forall { case Placeholder(_, _, DotDotDot) => false case _ => true } => - reifyBuildCall(nme.SyntacticApply, fun, args) case SyntacticApplied(fun, argss) if argss.nonEmpty => reifyBuildCall(nme.SyntacticApplied, fun, argss) case SyntacticTypeApplied(fun, targs) if targs.nonEmpty => reifyBuildCall(nme.SyntacticTypeApplied, fun, targs) case SyntacticFunction(args, body) => reifyBuildCall(nme.SyntacticFunction, args, body) + case SyntacticIdent(name, isBackquoted) => + reifyBuildCall(nme.SyntacticIdent, name, isBackquoted) + case Block(Nil, Placeholder(Hole(tree, DotDot))) => + mirrorBuildCall(nme.SyntacticBlock, tree) + case Block(Nil, other) => + reifyTree(other) case Block(stats, last) => reifyBuildCall(nme.SyntacticBlock, stats :+ last) + case Try(block, catches, finalizer) => + reifyBuildCall(nme.SyntacticTry, block, catches, finalizer) + case Match(selector, cases) => + reifyBuildCall(nme.SyntacticMatch, selector, cases) // parser emits trees with scala package symbol to ensure // that some names hygienically point to various scala package // members; we need to preserve this symbol to preserve @@ -183,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.treeNoUnlift 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} @@ -196,15 +209,10 @@ trait Reifiers { self: Quasiquotes => super.reifyName(name) } - def reifyCase(tree: Tree, location: Location) = { - if (holesHaveTypes && !(location.tpe <:< caseDefType)) c.abort(tree.pos, s"$caseDefType expected but ${location.tpe} found") - tree - } - 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 @@ -214,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) } @@ -223,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. @@ -262,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(_) @@ -281,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) } @@ -321,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) @@ -405,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.treeNoUnlift + 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)) } |