@@ -26,31 +26,90 @@ object QuotePatterns:
2626 import tpd ._
2727
2828 /** Check for restricted patterns */
29- def checkPattern (quotePattern : QuotePattern )(using Context ): Unit = new tpd.TreeTraverser {
30- def traverse (tree : Tree )(using Context ): Unit = tree match {
31- case _ : SplicePattern =>
32- case tdef : TypeDef if tdef.symbol.isClass =>
33- val kind = if tdef.symbol.is(Module ) then " objects" else " classes"
34- report.error(em " Implementation restriction: cannot match $kind" , tree.srcPos)
35- case tree : NamedDefTree =>
36- if tree.name.is(NameKinds .WildcardParamName ) then
37- report.warning(
38- " Use of `_` for lambda in quoted pattern. Use explicit lambda instead or use `$_` to match any term." ,
39- tree.srcPos)
40- if tree.name.isTermName && ! tree.nameSpan.isSynthetic && tree.name != nme.ANON_FUN && tree.name.startsWith(" $" ) then
41- report.error(" Names cannot start with $ quote pattern" , tree.namePos)
42- traverseChildren(tree)
43- case _ : Match =>
44- report.error(" Implementation restriction: cannot match `match` expressions" , tree.srcPos)
45- case _ : Try =>
46- report.error(" Implementation restriction: cannot match `try` expressions" , tree.srcPos)
47- case _ : Return =>
48- report.error(" Implementation restriction: cannot match `return` statements" , tree.srcPos)
49- case _ =>
50- traverseChildren(tree)
51- }
29+ def checkPattern (quotePattern : QuotePattern )(using Context ): Unit =
30+ def validatePatternAndCollectTypeVars (): Set [Symbol ] = new tpd.TreeAccumulator [Set [Symbol ]] {
31+ override def apply (typevars : Set [Symbol ], tree : tpd.Tree )(using Context ): Set [Symbol ] =
32+ // Collect type variables
33+ val typevars1 = tree match
34+ case tree @ DefDef (_, paramss, _, _) =>
35+ typevars union paramss.flatMap{ params => params match
36+ case TypeDefs (tdefs) => tdefs.map(_.symbol)
37+ case _ => List .empty
38+ }.toSet union typevars
39+ case _ => typevars
40+
41+ // Validate pattern
42+ tree match
43+ case _ : SplicePattern => typevars1
44+ case tdef : TypeDef if tdef.symbol.isClass =>
45+ val kind = if tdef.symbol.is(Module ) then " objects" else " classes"
46+ report.error(em " Implementation restriction: cannot match $kind" , tree.srcPos)
47+ typevars1
48+ case tree : NamedDefTree =>
49+ if tree.name.is(NameKinds .WildcardParamName ) then
50+ report.warning(
51+ " Use of `_` for lambda in quoted pattern. Use explicit lambda instead or use `$_` to match any term." ,
52+ tree.srcPos)
53+ if tree.name.isTermName && ! tree.nameSpan.isSynthetic && tree.name != nme.ANON_FUN && tree.name.startsWith(" $" ) then
54+ report.error(" Names cannot start with $ quote pattern" , tree.namePos)
55+ foldOver(typevars1, tree)
56+ case _ : Match =>
57+ report.error(" Implementation restriction: cannot match `match` expressions" , tree.srcPos)
58+ typevars1
59+ case _ : Try =>
60+ report.error(" Implementation restriction: cannot match `try` expressions" , tree.srcPos)
61+ typevars1
62+ case _ : Return =>
63+ report.error(" Implementation restriction: cannot match `return` statements" , tree.srcPos)
64+ typevars1
65+ case _ =>
66+ foldOver(typevars1, tree)
67+ }.apply(Set .empty, quotePattern.body)
68+
69+ val boundTypeVars = validatePatternAndCollectTypeVars()
5270
53- }.traverse(quotePattern.body)
71+ /*
72+ * This part checks well-formedness of arguments to hoas patterns.
73+ * (1) Type arguments of a hoas patterns must be introduced in the quote pattern.ctxShow
74+ * Examples
75+ * well-formed: '{ [A] => (x : A) => $a[A](x) } // A is introduced in the quote pattern
76+ * ill-formed: '{ (x : Int) => $a[Int](x) } // Int is defined outside of the quote pattern
77+ * (2) If value arguments of a hoas pattern has a type with type variables that are introduced in
78+ * the quote pattern, those type variables should be in type arguments to the hoas patternHole
79+ * Examples
80+ * well-formed: '{ [A] => (x : A) => $a[A](x) } // a : [A] => (x:A) => A
81+ * ill-formed: '{ [A] => (x : A) => $a(x) } // a : (x:A) => A ...but A is undefined; hence ill-formed
82+ */
83+ new tpd.TreeTraverser {
84+ override def traverse (tree : tpd.Tree )(using Context ): Unit = tree match {
85+ case tree : SplicePattern =>
86+ def uncapturedTypeVars (arg : tpd.Tree , capturedTypeVars : List [tpd.Tree ]): Set [Type ] =
87+ /* Sometimes arg is untyped when a splice pattern is ill-formed.
88+ * Return early in such case.
89+ * Refer to QuoteAndSplices::typedSplicePattern
90+ */
91+ if ! arg.hasType then return Set .empty
92+
93+ val capturedTypeVarsSet = capturedTypeVars.map(_.symbol).toSet
94+ new TypeAccumulator [Set [Type ]] {
95+ def apply (x : Set [Type ], tp : Type ): Set [Type ] =
96+ if boundTypeVars.contains(tp.typeSymbol) && ! capturedTypeVarsSet.contains(tp.typeSymbol) then
97+ foldOver(x + tp, tp)
98+ else
99+ foldOver(x, tp)
100+ }.apply(Set .empty, arg.tpe)
101+
102+ for (typearg <- tree.typeargs) // case (1)
103+ do
104+ if ! boundTypeVars.contains(typearg.symbol) then
105+ report.error(" Type arguments of a hoas pattern needs to be defined inside the quoted pattern" , typearg.srcPos)
106+ for (arg <- tree.args) // case (2)
107+ do
108+ if ! uncapturedTypeVars(arg, tree.typeargs).isEmpty then
109+ report.error(" Type variables that this argument depends on are not captured in this hoas pattern" , arg.srcPos)
110+ case _ => traverseChildren(tree)
111+ }
112+ }.traverse(quotePattern.body)
54113
55114 /** Encode the quote pattern into an `unapply` that the pattern matcher can handle.
56115 *
@@ -74,7 +133,7 @@ object QuotePatterns:
74133 * .ExprMatch // or TypeMatch
75134 * .unapply[
76135 * KCons[t1 >: l1 <: b1, ...KCons[tn >: ln <: bn, KNil]...], // scala.quoted.runtime.{KCons, KNil}
77- * (T1, T2, (A1, ..., An) => T3, ...)
136+ * (Expr[T1], Expr[T2], Expr[ (A1, ..., An) => T3] , ...)
78137 * ](
79138 * '{
80139 * type t1' >: l1' <: b1'
@@ -197,16 +256,24 @@ object QuotePatterns:
197256 val patBuf = new mutable.ListBuffer [Tree ]
198257 val shape = new tpd.TreeMap {
199258 override def transform (tree : Tree )(using Context ) = tree match {
200- case Typed (splice @ SplicePattern (pat, Nil ), tpt) if ! tpt.tpe.derivesFrom(defn.RepeatedParamClass ) =>
259+ case Typed (splice @ SplicePattern (pat, Nil , Nil ), tpt) if ! tpt.tpe.derivesFrom(defn.RepeatedParamClass ) =>
201260 transform(tpt) // Collect type bindings
202261 transform(splice)
203- case SplicePattern (pat, args) =>
262+ case SplicePattern (pat, typeargs, args) =>
204263 val patType = pat.tpe.widen
205264 val patType1 = patType.translateFromRepeated(toArray = false )
206265 val pat1 = if (patType eq patType1) pat else pat.withType(patType1)
207266 patBuf += pat1
208- if args.isEmpty then ref(defn.QuotedRuntimePatterns_patternHole .termRef).appliedToType(tree.tpe).withSpan(tree.span)
209- else ref(defn.QuotedRuntimePatterns_higherOrderHole .termRef).appliedToType(tree.tpe).appliedTo(SeqLiteral (args, TypeTree (defn.AnyType ))).withSpan(tree.span)
267+ if typeargs.isEmpty && args.isEmpty then ref(defn.QuotedRuntimePatterns_patternHole .termRef).appliedToType(tree.tpe).withSpan(tree.span)
268+ else if typeargs.isEmpty then
269+ ref(defn.QuotedRuntimePatterns_higherOrderHole .termRef)
270+ .appliedToType(tree.tpe)
271+ .appliedTo(SeqLiteral (args, TypeTree (defn.AnyType )))
272+ .withSpan(tree.span)
273+ else ref(defn.QuotedRuntimePatterns_higherOrderHoleWithTypes .termRef)
274+ .appliedToTypeTrees(List (TypeTree (tree.tpe), tpd.hkNestedPairsTypeTree(typeargs)))
275+ .appliedTo(SeqLiteral (args, TypeTree (defn.AnyType )))
276+ .withSpan(tree.span)
210277 case _ =>
211278 super .transform(tree)
212279 }
@@ -232,17 +299,19 @@ object QuotePatterns:
232299 fun match
233300 // <quotes>.asInstanceOf[QuoteMatching].{ExprMatch,TypeMatch}.unapply[<typeBindings>, <resTypes>]
234301 case TypeApply (Select (Select (TypeApply (Select (quotes, _), _), _), _), typeBindings :: resTypes :: Nil ) =>
235- val bindings = unrollBindings (typeBindings)
302+ val bindings = unrollHkNestedPairsTypeTree (typeBindings)
236303 val addPattenSplice = new TreeMap {
237304 private val patternIterator = patterns.iterator.filter {
238305 case pat : Bind => ! pat.symbol.name.is(PatMatGivenVarName )
239306 case _ => true
240307 }
241308 override def transform (tree : tpd.Tree )(using Context ): tpd.Tree = tree match
242309 case TypeApply (patternHole, _) if patternHole.symbol == defn.QuotedRuntimePatterns_patternHole =>
243- cpy.SplicePattern (tree)(patternIterator.next(), Nil )
310+ cpy.SplicePattern (tree)(patternIterator.next(), Nil , Nil )
244311 case Apply (patternHole, SeqLiteral (args, _) :: Nil ) if patternHole.symbol == defn.QuotedRuntimePatterns_higherOrderHole =>
245- cpy.SplicePattern (tree)(patternIterator.next(), args)
312+ cpy.SplicePattern (tree)(patternIterator.next(), Nil , args)
313+ case Apply (TypeApply (patternHole, List (_, targsTpe)), SeqLiteral (args, _) :: Nil ) if patternHole.symbol == defn.QuotedRuntimePatterns_higherOrderHoleWithTypes =>
314+ cpy.SplicePattern (tree)(patternIterator.next(), unrollHkNestedPairsTypeTree(targsTpe), args)
246315 case _ => super .transform(tree)
247316 }
248317 val body = addPattenSplice.transform(shape) match
@@ -260,7 +329,7 @@ object QuotePatterns:
260329 case body => body
261330 cpy.QuotePattern (tree)(bindings, body, quotes)
262331
263- private def unrollBindings (tree : Tree )(using Context ): List [Tree ] = tree match
332+ private def unrollHkNestedPairsTypeTree (tree : Tree )(using Context ): List [Tree ] = tree match
264333 case AppliedTypeTree (tupleN, bindings) if defn.isTupleClass(tupleN.symbol) => bindings // TupleN, 1 <= N <= 22
265- case AppliedTypeTree (_, head :: tail :: Nil ) => head :: unrollBindings (tail) // KCons or *:
334+ case AppliedTypeTree (_, head :: tail :: Nil ) => head :: unrollHkNestedPairsTypeTree (tail) // KCons or *:
266335 case _ => Nil // KNil or EmptyTuple
0 commit comments