Skip to content

Commit bd932a5

Browse files
committed
Split code and add description of the transformation
1 parent 94070d0 commit bd932a5

File tree

1 file changed

+122
-61
lines changed

1 file changed

+122
-61
lines changed

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

Lines changed: 122 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)