diff options
8 files changed, 145 insertions, 53 deletions
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index 2410af49e5..36452c2a6d 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -14,6 +14,7 @@ import scala.util.Try */ trait Parsers { self: Quasiquotes => import global.{Try => _, _} + import build.implodePatDefs abstract class Parser extends { val global: self.global.type = self.global @@ -182,7 +183,7 @@ trait Parsers { self: Quasiquotes => } object TermParser extends Parser { - def entryPoint = parser => Q(gen.mkTreeOrBlock(parser.templateOrTopStatSeq())) + def entryPoint = parser => Q(implodePatDefs(gen.mkTreeOrBlock(parser.templateOrTopStatSeq()))) } object TypeParser extends Parser { @@ -195,7 +196,7 @@ trait Parsers { self: Quasiquotes => } object CaseParser extends Parser { - def entryPoint = _.caseClause() + def entryPoint = parser => implodePatDefs(parser.caseClause()) } object PatternParser extends Parser { @@ -209,7 +210,7 @@ trait Parsers { self: Quasiquotes => def entryPoint = { parser => val enums = parser.enumerator(isFirst = false, allowNestedIf = false) assert(enums.length == 1) - enums.head + implodePatDefs(enums.head) } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index 017e966f63..70580adbce 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -194,8 +194,8 @@ trait Reifiers { self: Quasiquotes => reifyBuildCall(nme.SyntacticEmptyTypeTree) case SyntacticImport(expr, selectors) => reifyBuildCall(nme.SyntacticImport, expr, selectors) - case Q(Placeholder(Hole(tree, DotDot))) => - mirrorBuildCall(nme.SyntacticBlock, tree) + case Q(tree) if fillListHole.isDefinedAt(tree) => + mirrorBuildCall(nme.SyntacticBlock, fillListHole(tree)) case Q(other) => reifyTree(other) // Syntactic block always matches so we have to be careful @@ -311,11 +311,7 @@ trait Reifiers { self: Quasiquotes => */ def reifyMultiCardinalityList(xs: List[Any])(fill: PartialFunction[Any, Tree])(fallback: Any => Tree): Tree - /** Reifies arbitrary list filling ..$x and ...$y holeMap when they are put - * in the correct position. Fallbacks to regular reification for non-high cardinality - * elements. - */ - override def reifyList(xs: List[Any]): Tree = reifyMultiCardinalityList(xs) { + val fillListHole: PartialFunction[Any, Tree] = { case Placeholder(Hole(tree, DotDot)) => tree case CasePlaceholder(Hole(tree, DotDot)) => tree case RefineStatPlaceholder(h @ Hole(_, DotDot)) => reifyRefineStat(h) @@ -323,12 +319,23 @@ trait Reifiers { self: Quasiquotes => case PackageStatPlaceholder(h @ Hole(_, DotDot)) => reifyPackageStat(h) case ForEnumPlaceholder(Hole(tree, DotDot)) => tree case ParamPlaceholder(Hole(tree, DotDot)) => tree + case SyntacticPatDef(mods, pat, tpt, rhs) => + reifyBuildCall(nme.SyntacticPatDef, mods, pat, tpt, rhs) + case SyntacticValDef(mods, p @ Placeholder(h: ApplyHole), tpt, rhs) if h.tpe <:< treeType => + mirrorBuildCall(nme.SyntacticPatDef, reify(mods), h.tree, reify(tpt), reify(rhs)) + } + + val fillListOfListsHole: PartialFunction[Any, Tree] = { case List(ParamPlaceholder(Hole(tree, DotDotDot))) => tree case List(Placeholder(Hole(tree, DotDotDot))) => tree - } { - reify(_) } + /** Reifies arbitrary list filling ..$x and ...$y holeMap when they are put + * in the correct position. Fallbacks to regular reification for non-high cardinality + * elements. + */ + override def reifyList(xs: List[Any]): Tree = reifyMultiCardinalityList(xs)(fillListHole.orElse(fillListOfListsHole))(reify) + def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) { case AnnotPlaceholder(h @ Hole(_, DotDot)) => reifyAnnotation(h) } { diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index ec20a89a10..57dc7da6cc 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -220,6 +220,12 @@ private[reflect] trait BuildUtils { self: Universe => def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)] } + val SyntacticPatDef: SyntacticPatDefExtractor + + trait SyntacticPatDefExtractor { + def apply(mods: Modifiers, pat: Tree, tpt: Tree, rhs: Tree): List[ValDef] + } + val SyntacticAssign: SyntacticAssignExtractor trait SyntacticAssignExtractor { diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index c5581601de..6f1e4d2e32 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -193,9 +193,9 @@ trait BuildUtils { self: SymbolTable => // recover constructor contents generated by gen.mkTemplate protected object UnCtor { def unapply(tree: Tree): Option[(Modifiers, List[List[ValDef]], List[Tree])] = tree match { - case DefDef(mods, nme.MIXIN_CONSTRUCTOR, _, _, _, Block(lvdefs, _)) => + case DefDef(mods, nme.MIXIN_CONSTRUCTOR, _, _, _, SyntacticBlock(lvdefs :+ _)) => Some((mods | Flag.TRAIT, Nil, lvdefs)) - case DefDef(mods, nme.CONSTRUCTOR, Nil, vparamss, _, Block(lvdefs :+ _, _)) => + case DefDef(mods, nme.CONSTRUCTOR, Nil, vparamss, _, SyntacticBlock(lvdefs :+ _ :+ _)) => Some((mods, vparamss, lvdefs)) case _ => None } @@ -474,10 +474,9 @@ trait BuildUtils { self: SymbolTable => } protected class SyntacticValDefBase(isMutable: Boolean) extends SyntacticValDefExtractor { - def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) = { - val mods1 = if (isMutable) mods | MUTABLE else mods - ValDef(mods1, name, tpt, rhs) - } + def modifiers(mods: Modifiers): Modifiers = if (isMutable) mods | MUTABLE else mods + + def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef = ValDef(modifiers(mods), name, tpt, rhs) def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)] = tree match { case ValDef(mods, name, tpt, rhs) if mods.hasFlag(MUTABLE) == isMutable => @@ -547,25 +546,57 @@ trait BuildUtils { self: SymbolTable => // match a sequence of desugared `val $pat = $value` protected object UnPatSeq { - def unapply(trees: List[Tree]): Option[List[(Tree, Tree)]] = trees match { - case Nil => Some(Nil) - // case q"$mods val ${_}: ${_} = ${MaybeUnchecked(value)} match { case $pat => (..$ids) }" :: tail - case ValDef(mods, _, _, Match(MaybeUnchecked(value), CaseDef(pat, EmptyTree, SyntacticTuple(ids)) :: Nil)) :: tail + def unapply(trees: List[Tree]): Option[List[(Tree, Tree)]] = { + val imploded = implodePatDefs(trees) + val patvalues = imploded.flatMap { + case SyntacticPatDef(_, pat, EmptyTree, rhs) => Some((pat, rhs)) + case ValDef(_, name, SyntacticEmptyTypeTree(), rhs) => Some((Bind(name, self.Ident(nme.WILDCARD)), rhs)) + case ValDef(_, name, tpt, rhs) => Some((Bind(name, Typed(self.Ident(nme.WILDCARD), tpt)), rhs)) + case _ => None + } + if (patvalues.length == imploded.length) Some(patvalues) else None + } + } + + // implode multiple-statement desugaring of pattern definitions + // into single-statement valdefs with nme.QUASIQUOTE_PAT_DEF name + object implodePatDefs extends Transformer { + override def transform(tree: Tree) = tree match { + case templ: Template => deriveTemplate(templ)(transformStats) + case block: Block => + val Block(init, last) = block + Block(transformStats(init), transform(last)).copyAttrs(block) + case ValDef(mods, name1, SyntacticEmptyTypeTree(), Match(MaybeTyped(MaybeUnchecked(value), tpt), CaseDef(pat, EmptyTree, Ident(name2)) :: Nil)) + if name1 == name2 => + ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value)) + case _ => + super.transform(tree) + } + def transformStats(trees: List[Tree]): List[Tree] = trees match { + case Nil => Nil + case ValDef(mods, _, SyntacticEmptyTypeTree(), Match(MaybeTyped(MaybeUnchecked(value), tpt), CaseDef(pat, EmptyTree, SyntacticTuple(ids)) :: Nil)) :: tail if mods.hasFlag(SYNTHETIC) && mods.hasFlag(ARTIFACT) => - tail.drop(ids.length) match { - case UnPatSeq(rest) => Some((pat, value) :: rest) - case _ => None + ids match { + case Nil => + ValDef(NoMods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value)) :: transformStats(tail) + case _ => + val mods = tail.take(1).head.asInstanceOf[ValDef].mods + ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value)) :: transformStats(tail.drop(ids.length)) } - // case q"${_} val $name1: ${_} = ${MaybeUnchecked(value)} match { case $pat => ${Ident(name2)} }" :: UnPatSeq(rest) - case ValDef(_, name1, _, Match(MaybeUnchecked(value), CaseDef(pat, EmptyTree, Ident(name2)) :: Nil)) :: UnPatSeq(rest) - if name1 == name2 => - Some((pat, value) :: rest) - // case q"${_} val $name: ${SyntacticEmptyTypeTree()} = $value" :: UnPatSeq(rest) => - case ValDef(_, name, SyntacticEmptyTypeTree(), value) :: UnPatSeq(rest) => - Some((Bind(name, self.Ident(nme.WILDCARD)), value) :: rest) - // case q"${_} val $name: $tpt = $value" :: UnPatSeq(rest) => - case ValDef(_, name, tpt, value) :: UnPatSeq(rest) => - Some((Bind(name, Typed(self.Ident(nme.WILDCARD), tpt)), value) :: rest) + case other :: tail => + transform(other) :: transformStats(tail) + } + def apply(tree: Tree) = transform(tree) + def apply(trees: List[Tree]) = transformStats(trees) + } + + object SyntacticPatDef extends SyntacticPatDefExtractor { + def apply(mods: Modifiers, pat: Tree, tpt: Tree, rhs: Tree): List[ValDef] = tpt match { + case SyntacticEmptyTypeTree() => gen.mkPatDef(mods, pat, rhs) + case _ => gen.mkPatDef(mods, Typed(pat, tpt), rhs) + } + def unapply(tree: Tree): Option[(Modifiers, Tree, Tree, Tree)] = tree match { + case ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), rhs) => Some((mods, pat, tpt, rhs)) case _ => None } } @@ -746,6 +777,13 @@ trait BuildUtils { self: SymbolTable => } } + protected object MaybeTyped { + def unapply(tree: Tree): Some[(Tree, Tree)] = tree match { + case Typed(v, tpt) => Some((v, tpt)) + case v => Some((v, SyntacticEmptyTypeTree())) + } + } + protected def mkCases(cases: List[Tree]): List[CaseDef] = cases.map { case c: CaseDef => c case tree => throw new IllegalArgumentException("$tree is not valid representation of pattern match case") diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 0ac067a399..de582ee5f9 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -334,6 +334,7 @@ trait StdNames { val QUASIQUOTE_NAME_PREFIX: String = "nn$" val QUASIQUOTE_PACKAGE_STAT: NameType = "$quasiquote$package$stat$" val QUASIQUOTE_PARAM: NameType = "$quasiquote$param$" + val QUASIQUOTE_PAT_DEF: NameType = "$quasiquote$pat$def$" val QUASIQUOTE_PREFIX: String = "qq$" val QUASIQUOTE_REFINE_STAT: NameType = "$quasiquote$refine$stat$" val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$" @@ -615,6 +616,7 @@ trait StdNames { val SyntacticNew: NameType = "SyntacticNew" val SyntacticObjectDef: NameType = "SyntacticObjectDef" val SyntacticPackageObjectDef: NameType = "SyntacticPackageObjectDef" + val SyntacticPatDef: NameType = "SyntacticPatDef" val SyntacticTraitDef: NameType = "SyntacticTraitDef" val SyntacticTry: NameType = "SyntacticTry" val SyntacticTuple: NameType = "SyntacticTuple" diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index 29fdba2781..e7675eb4bf 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -691,11 +691,11 @@ abstract class TreeGen extends macros.TreeBuilder { } /** Create tree for pattern definition <val pat0 = rhs> */ - def mkPatDef(pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[Tree] = + def mkPatDef(pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[ValDef] = mkPatDef(Modifiers(0), pat, rhs) /** Create tree for pattern definition <mods val pat0 = rhs> */ - def mkPatDef(mods: Modifiers, pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[Tree] = matchVarPattern(pat) match { + def mkPatDef(mods: Modifiers, pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[ValDef] = matchVarPattern(pat) match { case Some((name, tpt)) => List(atPos(pat.pos union rhs.pos) { ValDef(mods, name.toTermName, tpt, rhs) diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala index 39e4c48073..5f22925335 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala @@ -7,21 +7,22 @@ object DefinitionConstructionProps with TraitConstruction with TypeDefConstruction with ValDefConstruction + with PatDefConstruction with DefConstruction - with PackageConstruction + with PackageConstruction with ImportConstruction { - property("SI-6842") = test { - val x: Tree = q"val x: Int" - assertEqAst(q"def f($x) = 0", "def f(x: Int) = 0") - assertEqAst(q"class C($x)", "class C(val x: Int)") - assertEqAst(q"class C { $x => }", "class C { x: Int => }") - assertEqAst(q"trait B { $x => }", "trait B { x: Int => }") - assertEqAst(q"object A { $x => }", "object A { x: Int => }") - val t: Tree = q"type T" - assertEqAst(q"def f[$t] = 0", "def f[T] = 0") - assertEqAst(q"class C[$t]", "class C[T]") - assertEqAst(q"trait B[$t]", "trait B[T]") - } + + val x: Tree = q"val x: Int" + property("SI-6842 a1") = test { assertEqAst(q"def f($x) = 0", "def f(x: Int) = 0") } + property("SI-6842 a2") = test { assertEqAst(q"class C($x)", "class C(val x: Int)") } + property("SI-6842 a3") = test { assertEqAst(q"class C { $x => }", "class C { x: Int => }") } + property("SI-6842 a4") = test { assertEqAst(q"trait B { $x => }", "trait B { x: Int => }") } + property("SI-6842 a5") = test { assertEqAst(q"object A { $x => }", "object A { x: Int => }") } + + val t: Tree = q"type T" + property("SI-6842 b1") = test { assertEqAst(q"def f[$t] = 0", "def f[T] = 0") } + property("SI-6842 b2") = test { assertEqAst(q"class C[$t]", "class C[T]") } + property("SI-6842 b3") = test { assertEqAst(q"trait B[$t]", "trait B[T]") } } trait ClassConstruction { self: QuasiquoteProperties => @@ -200,19 +201,56 @@ trait TypeDefConstruction { self: QuasiquoteProperties => } trait ValDefConstruction { self: QuasiquoteProperties => - property("splice term name into val") = forAll { (name: TermName, tpt: Tree, rhs: Tree) => + property("splice into val") = forAll { (name: TermName, tpt: Tree, rhs: Tree) => q"val $name: $tpt = $rhs" ≈ ValDef(Modifiers(), name, tpt, rhs) } - property("splice term name into var") = forAll { (name: TermName, tpt: Tree, rhs: Tree) => + property("splice into var") = forAll { (name: TermName, tpt: Tree, rhs: Tree) => q"var $name: $tpt = $rhs" ≈ ValDef(Modifiers(MUTABLE), name, tpt, rhs) } + // left tree is not a pattern due to Si-8211 property("SI-8202") = test { assertEqAst(q"val (x: Int) = 1", "val x: Int = 1") } } +trait PatDefConstruction { self: QuasiquoteProperties => + property("splice pattern into pat def") = test { + val pat = pq"(a, b)" + assertEqAst(q"val $pat = (1, 2)", "val (a, b) = (1, 2)") + val tpt = tq"(Int, Int)" + assertEqAst(q"val $pat: $tpt = (1, 2)", "val (a, b): (Int, Int) = (1, 2)") + } + + property("splice pattern into pat def within other pattern (1)") = test { + val pat = pq"(a, b)" + assertEqAst(q"val Foo($pat) = Foo((1, 2))", "val Foo((a, b)) = Foo((1, 2))") + val tpt = tq"Foo" + assertEqAst(q"val Foo($pat): $tpt = Foo((1, 2))", "val Foo((a, b)): Foo = Foo((1, 2))") + } + + property("splice patterns into pat def within other pattern (2)") = test { + val pat1 = pq"(a, b)"; val pat2 = pq"(c, d)" + assertEqAst(q"val ($pat1, $pat2) = ((1, 2), (3, 4))", "val ((a, b), (c, d)) = ((1, 2), (3, 4))") + val tpt = tq"((Int, Int), (Int, Int))" + assertEqAst(q"val ($pat1, $pat2): $tpt = ((1, 2), (3, 4))", "val ((a, b), (c, d)): ((Int, Int), (Int, Int)) = ((1, 2), (3, 4))") + } + + property("splice pattern without free vars into pat def") = test { + val pat = pq"((1, 2), 3)" + assertEqAst(q"val $pat = ((1, 2), 3)", "{ val ((1, 2), 3) = ((1, 2), 3) }") + val tpt = tq"((Int, Int), Int)" + assertEqAst(q"val $pat: $tpt = ((1, 2), 3)","{ val ((1, 2), 3): ((Int, Int), Int) = ((1, 2), 3) }") + } + + // won't result into pattern match due to SI-8211 + property("splice typed pat into pat def") = test { + val pat = pq"x: Int" + assertEqAst(q"val $pat = 2", "{ val x: Int = 2 }") + } +} + trait MethodConstruction { self: QuasiquoteProperties => property("splice paramss into defdef") = test { val paramss = List(q"val x: Int") :: List(q"val y: Int = 1") :: Nil diff --git a/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala index 589b8d4d72..5d84984514 100644 --- a/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala +++ b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala @@ -28,7 +28,7 @@ trait Helpers { override def transform(tree: Tree): Tree = tree match { case Ident(SimplifiedName(name)) => Ident(name) - case ValDef(mods, SimplifiedName(name), tpt, rhs) => ValDef(mods, name, tpt, rhs) + case ValDef(mods, SimplifiedName(name), tpt, rhs) => ValDef(mods, name, transform(tpt), transform(rhs)) case Bind(SimplifiedName(name), rhs) => Bind(name, rhs) case _ => super.transform(tree) |