@@ -1967,65 +1967,132 @@ class Typer extends Namer
19671967 typedTypeApply(untpd.TypeApply (untpd.ref(defn.InternalQuoted_typeQuoteR ), quoted :: Nil ), pt)(quoteContext).withSpan(tree.span)
19681968 case quoted =>
19691969 ctx.compilationUnit.needsStaging = true
1970- if (ctx.mode.is(Mode .Pattern ) && level == 0 ) {
1971- val exprPt = pt.baseType(defn.QuotedExprClass )
1972- val quotedPt = if (exprPt.exists) exprPt.argTypesHi.head else defn.AnyType
1973- val quoted1 = typedExpr(quoted, quotedPt)(quoteContext.addMode(Mode .QuotedPattern ))
1974-
1975- val (typeBindings, shape, splices) = splitQuotePattern(quoted1)
1976-
1977- class ReplaceBindings extends TypeMap () {
1978- override def apply (tp : Type ): Type = tp match {
1979- case tp : TypeRef =>
1980- val tp1 = if (tp.typeSymbol == defn.QuotedType_splice ) tp.dealias else tp
1981- typeBindings.get(tp1.typeSymbol).fold(tp)(_.symbol.typeRef)
1982- case tp => mapOver(tp)
1983- }
1984- }
1985- val replaceBindings = new ReplaceBindings
1986- val patType = defn.tupleType(splices.tpes.map(tpe => replaceBindings(tpe.widen)))
1987-
1988- val typeBindingsTuple = tpd.tupleTypeTree(typeBindings.values.toList)
1989-
1990- val replaceBindingsInTree = new TreeMap {
1991- private [this ] var bindMap = Map .empty[Symbol , Symbol ]
1992- override def transform (tree : tpd.Tree )(implicit ctx : Context ): tpd.Tree = {
1993- tree match {
1994- case tree : Bind =>
1995- val sym = tree.symbol
1996- val newInfo = replaceBindings(sym.info)
1997- val newSym = ctx.newSymbol(sym.owner, sym.name, sym.flags, newInfo, sym.privateWithin, sym.coord)
1998- bindMap += sym -> newSym
1999- Bind (newSym, transform(tree.body)).withSpan(sym.span)
2000- case _ =>
2001- super .transform(tree).withType(replaceBindingsInType(tree.tpe))
2002- }
2003- }
2004- private [this ] val replaceBindingsInType = new ReplaceBindings {
2005- override def apply (tp : Type ): Type = tp match {
2006- case tp : TermRef => bindMap.get(tp.termSymbol).fold[Type ](tp)(_.typeRef)
2007- case tp => super .apply(tp)
2008- }
2009- }
2010- }
2011-
2012- val splicePat = typed(untpd.Tuple (splices.map(x => untpd.TypedSplice (replaceBindingsInTree.transform(x)))).withSpan(quoted.span), patType)
2013-
2014- UnApply (
2015- fun = ref(defn.InternalQuotedMatcher_unapplyR ).appliedToTypeTrees(typeBindingsTuple :: TypeTree (patType) :: Nil ),
2016- implicits =
2017- ref(defn.InternalQuoted_exprQuoteR ).appliedToType(shape.tpe).appliedTo(shape) ::
2018- Literal (Constant (typeBindings.nonEmpty)) ::
2019- implicitArgTree(defn.QuoteContextType , tree.span) :: Nil ,
2020- patterns = splicePat :: Nil ,
2021- proto = pt)
2022- }
1970+ if (ctx.mode.is(Mode .Pattern ) && level == 0 )
1971+ typedQuotePattern(quoted, pt, tree.span)
20231972 else
20241973 typedApply(untpd.Apply (untpd.ref(defn.InternalQuoted_exprQuoteR ), quoted), pt)(quoteContext).withSpan(tree.span)
20251974 }
20261975 }
20271976
2028- def splitQuotePattern (quoted : Tree )(implicit ctx : Context ): (Map [Symbol , Bind ], Tree , List [Tree ]) = {
1977+ /** Type a quote pattern `case '{ <quoted> } =>` qiven the a current prototype. Typing the pattern
1978+ * will also transform it into a call to `scala.internal.quoted.Matcher.unapply`.
1979+ *
1980+ * Code directly inside the quote is typed as an expression using Mode.QuotedPattern. Splices
1981+ * within the quotes become patterns again and typed acordingly.
1982+ *
1983+ * ```
1984+ * case '{ ($ls: List[$t]) } =>
1985+ * // `t` is of type `Type[T$1]` for some unknown T$1
1986+ * // `t` is implicitly available
1987+ * // `l` is of type `Expr[List[T$1]]`
1988+ * '{ val h: $t = $ls.head }
1989+ * ```
1990+ *
1991+ * For each type splice we will create a new type binding in the pattern match ($t @ _ in this case)
1992+ * and a corresponding type in the quoted pattern as a hole (@patternBindHole type $t in this case).
1993+ * All these generated types are inserted at the start of the quoted code.
1994+ *
1995+ * After typing the tree will resemble
1996+ *
1997+ * ```
1998+ * case '{ type ${given t: Type[$t @ _]}; ${ls: Expr[List[$t]]} } => ...
1999+ * ```
2000+ *
2001+ * Then the pattern is _split_ into the expression containd in the pattern replacing the splices by holes,
2002+ * and the patterns in the splices. All these are recombined into a call to `Matcher.unapply`.
2003+ *
2004+ * ```
2005+ * case scala.internal.quoted.Matcher.unapply[
2006+ * Tuple1[$t @ _], // Type binging definition
2007+ * Tuple2[Type[$t], Expr[List[$t]]] // Typing the result of the pattern match
2008+ * ](
2009+ * Tuple2.unapply
2010+ * [Type[$t], Expr[List[$t]]] //Propagated from the tuple above
2011+ * (implict t @ _, ls @ _: Expr[List[$t]]) // from the spliced patterns
2012+ * )(
2013+ * '{ // Runtime quote Matcher.unapply uses to mach against. Expression directly inside the quoted pattern without the splices
2014+ * @scala.internal.Quoted.patternBindHole type $t
2015+ * scala.internal.Quoted.patternHole[List[$t]]
2016+ * },
2017+ * true, // If there is at least one type splice. Used to instantiate the context with or without GADT constraints
2018+ * x$2 // tasty.Reflection instance
2019+ * ) => ...
2020+ * ```
2021+ */
2022+ private def typedQuotePattern (quoted : untpd.Tree , pt : Type , quoteSpan : Span )(implicit ctx : Context ): Tree = {
2023+ val exprPt = pt.baseType(defn.QuotedExprClass )
2024+ val quotedPt = if (exprPt.exists) exprPt.argTypesHi.head else defn.AnyType
2025+ val quoted1 = typedExpr(quoted, quotedPt)(quoteContext.addMode(Mode .QuotedPattern ))
2026+
2027+ val (typeBindings, shape, splices) = splitQuotePattern(quoted1)
2028+
2029+ class ReplaceBindings extends TypeMap () {
2030+ override def apply (tp : Type ): Type = tp match {
2031+ case tp : TypeRef =>
2032+ val tp1 = if (tp.typeSymbol == defn.QuotedType_splice ) tp.dealias else tp
2033+ typeBindings.get(tp1.typeSymbol).fold(tp)(_.symbol.typeRef)
2034+ case tp => mapOver(tp)
2035+ }
2036+ }
2037+ val replaceBindings = new ReplaceBindings
2038+ val patType = defn.tupleType(splices.tpes.map(tpe => replaceBindings(tpe.widen)))
2039+
2040+ val typeBindingsTuple = tpd.tupleTypeTree(typeBindings.values.toList)
2041+
2042+ val replaceBindingsInTree = new TreeMap {
2043+ private [this ] var bindMap = Map .empty[Symbol , Symbol ]
2044+ override def transform (tree : tpd.Tree )(implicit ctx : Context ): tpd.Tree = {
2045+ tree match {
2046+ case tree : Bind =>
2047+ val sym = tree.symbol
2048+ val newInfo = replaceBindings(sym.info)
2049+ val newSym = ctx.newSymbol(sym.owner, sym.name, sym.flags, newInfo, sym.privateWithin, sym.coord)
2050+ bindMap += sym -> newSym
2051+ Bind (newSym, transform(tree.body)).withSpan(sym.span)
2052+ case _ =>
2053+ super .transform(tree).withType(replaceBindingsInType(tree.tpe))
2054+ }
2055+ }
2056+ private [this ] val replaceBindingsInType = new ReplaceBindings {
2057+ override def apply (tp : Type ): Type = tp match {
2058+ case tp : TermRef => bindMap.get(tp.termSymbol).fold[Type ](tp)(_.typeRef)
2059+ case tp => super .apply(tp)
2060+ }
2061+ }
2062+ }
2063+
2064+ val splicePat = typed(untpd.Tuple (splices.map(x => untpd.TypedSplice (replaceBindingsInTree.transform(x)))).withSpan(quoted.span), patType)
2065+
2066+ UnApply (
2067+ fun = ref(defn.InternalQuotedMatcher_unapplyR ).appliedToTypeTrees(typeBindingsTuple :: TypeTree (patType) :: Nil ),
2068+ implicits =
2069+ ref(defn.InternalQuoted_exprQuoteR ).appliedToType(shape.tpe).appliedTo(shape) ::
2070+ Literal (Constant (typeBindings.nonEmpty)) ::
2071+ implicitArgTree(defn.QuoteContextType , quoteSpan) :: Nil ,
2072+ patterns = splicePat :: Nil ,
2073+ proto = pt)
2074+ }
2075+
2076+ /** Split a typed quoted pattern is split into its type bindings, pattern expression and inner patterns.
2077+ * Type definitions with `@patternBindHole` will be inserted in the pattern expression for each type binding.
2078+ *
2079+ * A quote pattern
2080+ * ```
2081+ * case '{ type ${given t: Type[$t @ _]}; ${ls: Expr[List[$t]]} } => ...
2082+ * ```
2083+ * will return
2084+ * ```
2085+ * (
2086+ * Map(<$t>: Symbol -> <$t @ _>: Bind),
2087+ * <'{
2088+ * @scala.internal.Quoted.patternBindHole type $t
2089+ * scala.internal.Quoted.patternHole[List[$t]]
2090+ * }>: Tree,
2091+ * List(<ls: Expr[List[$t]]>: Tree)
2092+ * )
2093+ * ```
2094+ */
2095+ private def splitQuotePattern (quoted : Tree )(implicit ctx : Context ): (Map [Symbol , Bind ], Tree , List [Tree ]) = {
20292096 val ctx0 = ctx
20302097
20312098 val typeBindings : collection.mutable.Map [Symbol , Bind ] = collection.mutable.Map .empty
@@ -2047,7 +2114,7 @@ class Typer extends Namer
20472114 val exprTpt = AppliedTypeTree (TypeTree (defn.QuotedExprType ), tpt1 :: Nil )
20482115 transform(Splice (Typed (pat, exprTpt)))
20492116 case Splice (pat) =>
2050- try patternHole( tree)
2117+ try ref(defn. InternalQuoted_patternHoleR ).appliedToType( tree.tpe).withSpan(tree.span )
20512118 finally {
20522119 val patType = pat.tpe.widen
20532120 val patType1 = patType.underlyingIfRepeated(isJava = false )
@@ -2122,12 +2189,6 @@ class Typer extends Namer
21222189 (typeBindings.toMap, shape2, patterns)
21232190 }
21242191
2125- /** A hole the shape pattern of a quoted.Matcher.unapply, representing a splice */
2126- def patternHole (splice : Tree )(implicit ctx : Context ): Tree = {
2127- val Splice (pat) = splice
2128- ref(defn.InternalQuoted_patternHoleR ).appliedToType(splice.tpe).withSpan(splice.span)
2129- }
2130-
21312192 /** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */
21322193 def typedSplice (tree : untpd.Splice , pt : Type )(implicit ctx : Context ): Tree = track(" typedSplice" ) {
21332194 checkSpliceOutsideQuote(tree)
0 commit comments