Skip to content

Commit 014be6f

Browse files
committed
Fix HK quoted pattern type variables
The issue was in the encoding into `{ExprMatchModule,TypeMatchModule}.unapply`. Specifically with the `TypeBindings` argument. This arguments holds the list of type variable definitions (`tpd.Bind` trees). We used a `Tuple` to list all the types inside. The problem is that higher-kinded type variables do not conform with the upper bounds of the tuple elements. The solution is to use an HList with any-kinded elements.
1 parent b3c1c98 commit 014be6f

File tree

8 files changed

+45
-11
lines changed

8 files changed

+45
-11
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,7 +1498,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
14981498
}
14991499
}
15001500

1501-
/** Creates the tuple type tree repesentation of the type trees in `ts` */
1501+
/** Creates the tuple type tree representation of the type trees in `ts` */
15021502
def tupleTypeTree(elems: List[Tree])(using Context): Tree = {
15031503
val arity = elems.length
15041504
if arity <= Definitions.MaxTupleArity then
@@ -1509,10 +1509,14 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
15091509
else nestedPairsTypeTree(elems)
15101510
}
15111511

1512-
/** Creates the nested pairs type tree repesentation of the type trees in `ts` */
1512+
/** Creates the nested pairs type tree representation of the type trees in `ts` */
15131513
def nestedPairsTypeTree(ts: List[Tree])(using Context): Tree =
15141514
ts.foldRight[Tree](TypeTree(defn.EmptyTupleModule.termRef))((x, acc) => AppliedTypeTree(TypeTree(defn.PairClass.typeRef), x :: acc :: Nil))
15151515

1516+
/** Creates the nested higher-kinded pairs type tree representation of the type trees in `ts` */
1517+
def hkNestedPairsTypeTree(ts: List[Tree])(using Context): Tree =
1518+
ts.foldRight[Tree](TypeTree(defn.QuoteMatching_KNil.typeRef))((x, acc) => AppliedTypeTree(TypeTree(defn.QuoteMatching_KCons.typeRef), x :: acc :: Nil))
1519+
15161520
/** Replaces all positions in `tree` with zero-extent positions */
15171521
private def focusPositions(tree: Tree)(using Context): Tree = {
15181522
val transformer = new tpd.TreeMap {

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,9 @@ class Definitions {
865865
@tu lazy val QuoteMatching_ExprMatchModule: Symbol = QuoteMatchingClass.requiredClass("ExprMatchModule")
866866
@tu lazy val QuoteMatching_TypeMatch: Symbol = QuoteMatchingClass.requiredMethod("TypeMatch")
867867
@tu lazy val QuoteMatching_TypeMatchModule: Symbol = QuoteMatchingClass.requiredClass("TypeMatchModule")
868+
@tu lazy val QuoteMatchingModule: Symbol = requiredModule("scala.quoted.runtime.QuoteMatching")
869+
@tu lazy val QuoteMatching_KNil: Symbol = QuoteMatchingModule.requiredType("KNil")
870+
@tu lazy val QuoteMatching_KCons: Symbol = QuoteMatchingModule.requiredType("KCons")
868871

869872
@tu lazy val ToExprModule: Symbol = requiredModule("scala.quoted.ToExpr")
870873
@tu lazy val ToExprModule_BooleanToExpr: Symbol = ToExprModule.requiredMethod("BooleanToExpr")

compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ trait QuotesAndSplices {
364364
*
365365
* ```
366366
* case scala.internal.quoted.Expr.unapply[
367-
* Tuple1[t @ _], // Type binging definition
367+
* KList[t @ _, KNil], // Type binging definition
368368
* Tuple2[Type[t], Expr[List[t]]] // Typing the result of the pattern match
369369
* ](
370370
* Tuple2.unapply
@@ -411,7 +411,7 @@ trait QuotesAndSplices {
411411
val replaceBindings = new ReplaceBindings
412412
val patType = defn.tupleType(splices.tpes.map(tpe => replaceBindings(tpe.widen)))
413413

414-
val typeBindingsTuple = tpd.tupleTypeTree(typeBindings.values.toList)
414+
val typeBindingsTuple = tpd.hkNestedPairsTypeTree(typeBindings.values.toList)
415415

416416
val replaceBindingsInTree = new TreeMap {
417417
private var bindMap = Map.empty[Symbol, Symbol]

compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ object QuoteMatcher {
121121

122122
private def withEnv[T](env: Env)(body: Env ?=> T): T = body(using env)
123123

124-
def treeMatch(scrutineeTerm: Tree, patternTerm: Tree)(using Context): Option[Tuple] =
124+
def treeMatch(scrutineeTree: Tree, patternTree: Tree)(using Context): Option[Tuple] =
125125
given Env = Map.empty
126-
scrutineeTerm =?= patternTerm
126+
scrutineeTree =?= patternTree
127127

128128
/** Check that all trees match with `mtch` and concatenate the results with &&& */
129129
private def matchLists[T](l1: List[T], l2: List[T])(mtch: (T, T) => Matching): Matching = (l1, l2) match {

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3093,14 +3093,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
30933093
new TypeImpl(tree, SpliceScope.getCurrent).asInstanceOf[scala.quoted.Type[T]]
30943094

30953095
object ExprMatch extends ExprMatchModule:
3096-
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: scala.quoted.Expr[Any])(using pattern: scala.quoted.Expr[Any]): Option[Tup] =
3096+
def unapply[TypeBindings, Tup <: Tuple](scrutinee: scala.quoted.Expr[Any])(using pattern: scala.quoted.Expr[Any]): Option[Tup] =
30973097
val scrutineeTree = reflect.asTerm(scrutinee)
30983098
val patternTree = reflect.asTerm(pattern)
30993099
treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]]
31003100
end ExprMatch
31013101

31023102
object TypeMatch extends TypeMatchModule:
3103-
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: scala.quoted.Type[?])(using pattern: scala.quoted.Type[?]): Option[Tup] =
3103+
def unapply[TypeBindings, Tup <: Tuple](scrutinee: scala.quoted.Type[?])(using pattern: scala.quoted.Type[?]): Option[Tup] =
31043104
val scrutineeTree = reflect.TypeTree.of(using scrutinee)
31053105
val patternTree = reflect.TypeTree.of(using pattern)
31063106
treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]]

library/src/scala/quoted/runtime/QuoteMatching.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ trait QuoteMatching:
1717
* - `ExprMatch.unapply('{ f(0, myInt) })('{ f(patternHole[Int], patternHole[Int]) }, _)`
1818
* will return `Some(Tuple2('{0}, '{ myInt }))`
1919
* - `ExprMatch.unapply('{ f(0, "abc") })('{ f(0, patternHole[Int]) }, _)`
20-
* will return `None` due to the missmatch of types in the hole
20+
* will return `None` due to the mismatch of types in the hole
2121
*
2222
* Holes:
2323
* - scala.quoted.runtime.Patterns.patternHole[T]: hole that matches an expression `x` of type `Expr[U]`
@@ -27,7 +27,7 @@ trait QuoteMatching:
2727
* @param pattern `Expr[Any]` containing the pattern tree
2828
* @return None if it did not match, `Some(tup)` if it matched where `tup` contains `Expr[Ti]``
2929
*/
30-
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: Expr[Any])(using pattern: Expr[Any]): Option[Tup]
30+
def unapply[TypeBindings, Tup <: Tuple](scrutinee: Expr[Any])(using pattern: Expr[Any]): Option[Tup]
3131
}
3232

3333
val TypeMatch: TypeMatchModule
@@ -40,5 +40,10 @@ trait QuoteMatching:
4040
* @param pattern `Type[?]` containing the pattern tree
4141
* @return None if it did not match, `Some(tup)` if it matched where `tup` contains `Type[Ti]``
4242
*/
43-
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: Type[?])(using pattern: Type[?]): Option[Tup]
43+
def unapply[TypeBindings, Tup <: Tuple](scrutinee: Type[?])(using pattern: Type[?]): Option[Tup]
4444
}
45+
46+
object QuoteMatching:
47+
type KList
48+
type KCons[+H <: AnyKind, +T <: KList] <: KList
49+
type KNil <: KList
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import scala.quoted._
2+
3+
private def impl(x: Expr[Any])(using Quotes): Expr[Unit] = {
4+
x match
5+
case '{ foo[x] } =>
6+
assert(Type.show[x] == "scala.Int", Type.show[x])
7+
case '{ type f[X]; foo[`f`] } =>
8+
assert(Type.show[f] == "[A >: scala.Nothing <: scala.Any] => scala.collection.immutable.List[A]", Type.show[f])
9+
case '{ type f <: AnyKind; foo[`f`] } =>
10+
assert(Type.show[f] == "[K >: scala.Nothing <: scala.Any, V >: scala.Nothing <: scala.Any] => scala.collection.immutable.Map[K, V]", Type.show[f])
11+
case x => throw MatchError(x.show)
12+
'{}
13+
}
14+
15+
inline def test(inline x: Any): Unit = ${ impl('x) }
16+
17+
def foo[T <: AnyKind]: Any = ???
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@main
2+
def Test =
3+
test(foo[Int])
4+
test(foo[List])
5+
test(foo[Map])

0 commit comments

Comments
 (0)