From 800f5acd0d7117bf953829da7c6d955e61e63bdc Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Thu, 5 Sep 2013 13:27:54 +0200 Subject: add support for function type splicing and extraction --- .../scala/tools/nsc/ast/parser/TreeBuilder.scala | 3 +-- .../scala/tools/reflect/quasiquotes/Parsers.scala | 4 ++++ .../tools/reflect/quasiquotes/Placeholders.scala | 7 +++++++ .../scala/tools/reflect/quasiquotes/Reifiers.scala | 4 ++++ src/reflect/scala/reflect/api/BuildUtils.scala | 7 +++++++ .../scala/reflect/internal/BuildUtils.scala | 22 +++++++++++++++++++++- src/reflect/scala/reflect/internal/StdNames.scala | 2 ++ src/reflect/scala/reflect/internal/TreeGen.scala | 4 ++++ .../quasiquotes/TypeConstructionProps.scala | 6 ++++++ .../quasiquotes/TypeDeconstructionProps.scala | 6 ++++++ 10 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index ed7c0d1a0a..28e3217449 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -516,8 +516,7 @@ abstract class TreeBuilder { } /** Create a tree representing the function type (argtpes) => restpe */ - def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = - AppliedTypeTree(rootScalaDot(newTypeName("Function" + argtpes.length)), argtpes ::: List(restpe)) + def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = gen.mkFunctionTypeTree(argtpes, restpe) /** Append implicit parameter section if `contextBounds` nonempty */ def addEvidenceParams(owner: Name, vparamss: List[List[ValDef]], contextBounds: List[Tree]): List[List[ValDef]] = { diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index fe0809c869..868eccebcd 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -64,6 +64,10 @@ trait Parsers { self: Quasiquotes => case (head @ Ident(name)) :: Nil if holeMap.contains(name) => Block(Nil, head) case _ => super.makeBlock(stats) } + + // tq"$a => $b" + override def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = + AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), argtpes :+ restpe) } import treeBuilder.{global => _, _} diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala index 5d7edcd75e..f0886b5735 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala @@ -113,6 +113,13 @@ trait Placeholders { self: Quasiquotes => } } + object FunctionTypePlaceholder { + def unapply(tree: Tree): Option[(List[Tree], Tree)] = tree match { + case AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), args :+ res) => Some((args, res)) + case _ => None + } + } + object SymbolPlaceholder { def unapply(scrutinee: Any): Option[Tree] = scrutinee match { case Placeholder(tree, SymbolLocation, _) => Some(tree) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index a817afe741..9789801fac 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -45,6 +45,7 @@ trait Reifiers { self: Quasiquotes => case Placeholder(tree, _, card @ Dot()) => c.abort(tree.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 _ => EmptyTree @@ -104,6 +105,9 @@ trait Reifiers { self: Quasiquotes => case _ => reifyBuildCall(nme.TupleTypeN, args) } + def reifyFunctionType(argtpes: List[Tree], restpe: Tree) = + reifyBuildCall(nme.SyntacticFunctionType, argtpes, restpe) + def reifyRefineStat(tree: Tree) = tree /** Splits list into a list of groups where subsequent elements are considered diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index a0a47fe23c..a74d3c137d 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -132,5 +132,12 @@ private[reflect] trait BuildUtils { self: Universe => def apply(stats: List[Tree]): Tree def unapply(tree: Tree): Option[List[Tree]] } + + val SyntacticFunctionType: SyntacticFunctionTypeExtractor + + trait SyntacticFunctionTypeExtractor { + def apply(argtpes: List[Tree], restpe: Tree): Tree + def unapply(tree: Tree): Option[(List[Tree], Tree)] + } } } diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index abe9e39067..e2894c5b94 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -5,7 +5,7 @@ package internal import Flags._ trait BuildUtils { self: SymbolTable => - import definitions.{TupleClass, MaxTupleArity, ScalaPackage, UnitClass} + import definitions.{TupleClass, FunctionClass, MaxTupleArity, MaxFunctionArity, ScalaPackage, UnitClass} class BuildImpl extends BuildApi { @@ -200,6 +200,26 @@ trait BuildUtils { self: SymbolTable => } } + object SyntacticFunctionType extends SyntacticFunctionTypeExtractor { + def apply(argtpes: List[Tree], restpe: Tree): Tree = { + require(argtpes.length <= MaxFunctionArity + 1, s"Function types with arity bigger than $MaxFunctionArity aren't supported") + gen.mkFunctionTypeTree(argtpes, restpe) + } + + def unapply(tree: Tree): Option[(List[Tree], Tree)] = tree match { + case AppliedTypeTree(id: Ident, args @ (argtpes :+ restpe)) + if args.length - 1 <= MaxFunctionArity && id.symbol == FunctionClass(args.length - 1) => + Some((argtpes, restpe)) + case AppliedTypeTree(Select(pack, fun), args @ (argtpes :+ restpe)) + if args.length - 1 <= MaxFunctionArity && pack.symbol == ScalaPackage && fun == FunctionClass(args.length - 1).name => + Some((argtpes, restpe)) + case AppliedTypeTree(Select(Select(Ident(nme.ROOTPKG), nme.scala_), fun), args @ (argtpes :+ restpe)) + if args.length - 1 <= MaxFunctionArity && fun == FunctionClass(args.length - 1).name => + Some((argtpes, restpe)) + case _ => None + } + } + object SyntacticBlock extends SyntacticBlockExtractor { def apply(stats: List[Tree]): Tree = gen.mkBlock(stats) diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 0b1f444772..f4662296e2 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -249,6 +249,7 @@ trait StdNames { // quasiquote-specific names final val QUASIQUOTE_MODS: NameType = "$quasiquote$mods$" final val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$" + final val QUASIQUOTE_FUNCTION: NameType = "$quasiquote$function$" final val QUASIQUOTE_REFINE_STAT: NameType = "$quasiquote$refine$stat$" // Annotation simple names, used in Namer @@ -590,6 +591,7 @@ trait StdNames { val SyntacticApplied: NameType = "SyntacticApplied" val SyntacticBlock: NameType = "SyntacticBlock" val SyntacticClassDef: NameType = "SyntacticClassDef" + val SyntacticFunctionType: NameType= "SyntacticFunctionType" val SyntacticTypeApplied: NameType = "SyntacticTypeApplied" val This: NameType = "This" val ThisType: NameType = "ThisType" diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index 9623cc31f6..648c921eb7 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -415,6 +415,10 @@ abstract class TreeGen extends macros.TreeBuilder { } } + /** Create a tree representing the function type (argtpes) => restpe */ + def mkFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = + AppliedTypeTree(rootScalaDot(newTypeName("Function" + argtpes.length)), argtpes ::: List(restpe)) + /** Create block of statements `stats` */ def mkBlock(stats: List[Tree]): Tree = if (stats.isEmpty) Literal(Constant(())) diff --git a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala index 459d733733..cac83ff8ac 100644 --- a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala @@ -27,4 +27,10 @@ object TypeConstructionProps extends QuasiquoteProperties("type construction") val stats = q"def foo" :: q"val x: Int" :: q"type Y = String" :: Nil assert(tq"T { ..$stats }" ≈ tq"T { def foo; val x: Int; type Y = String }") } + + property("function type") = test { + val argtpes = tq"A" :: tq"B" :: Nil + val restpe = tq"C" + assert(tq"..$argtpes => $restpe" ≈ tq"(A, B) => C") + } } \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala index 5c919e55cb..02787c551b 100644 --- a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala @@ -31,4 +31,10 @@ object TypeDeconstructionProps extends QuasiquoteProperties("type deconstruction val tq"T { ..$stats }" = tq"T { def foo; val x: Int; type Y = String }" assert(stats ≈ (q"def foo" :: q"val x: Int" :: q"type Y = String" :: Nil)) } + + property("function type") = test { + val tq"..$argtpes => $restpe" = tq"(A, B) => C" + assert(argtpes ≈ (tq"A" :: tq"B" :: Nil)) + assert(restpe ≈ tq"C") + } } \ No newline at end of file -- cgit v1.2.3