@@ -1975,13 +1975,51 @@ class Typer extends Namer
1975
1975
val exprPt = pt.baseType(defn.QuotedExprClass )
1976
1976
val quotedPt = if (exprPt.exists) exprPt.argTypesHi.head else defn.AnyType
1977
1977
val quoted1 = typedExpr(quoted, quotedPt)(quoteContext.addMode(Mode .QuotedPattern ))
1978
- val (shape, splices) = splitQuotePattern(quoted1)
1979
- val patType = defn.tupleType(splices.tpes.map(_.widen))
1980
- val splicePat = typed(untpd.Tuple (splices.map(untpd.TypedSplice (_))).withSpan(quoted.span), patType)
1978
+
1979
+ val (typeBindings, shape, splices) = splitQuotePattern(quoted1)
1980
+
1981
+ class ReplaceBindings extends TypeMap () {
1982
+ override def apply (tp : Type ): Type = tp match {
1983
+ case tp : TypeRef =>
1984
+ val tp1 = if (tp.typeSymbol == defn.QuotedType_splice ) tp.dealias else tp
1985
+ typeBindings.get(tp1.typeSymbol).fold(tp)(_.symbol.typeRef)
1986
+ case tp => mapOver(tp)
1987
+ }
1988
+ }
1989
+ val replaceBindings = new ReplaceBindings
1990
+ val patType = defn.tupleType(splices.tpes.map(tpe => replaceBindings(tpe.widen)))
1991
+
1992
+ val typeBindingsTuple = tpd.tupleTypeTree(typeBindings.values.toList)
1993
+
1994
+ val replaceBindingsInTree = new TreeMap {
1995
+ private [this ] var bindMap = Map .empty[Symbol , Symbol ]
1996
+ override def transform (tree : tpd.Tree )(implicit ctx : Context ): tpd.Tree = {
1997
+ tree match {
1998
+ case tree : Bind =>
1999
+ val sym = tree.symbol
2000
+ val newInfo = replaceBindings(sym.info)
2001
+ val newSym = ctx.newSymbol(sym.owner, sym.name, sym.flags, newInfo, sym.privateWithin, sym.coord)
2002
+ bindMap += sym -> newSym
2003
+ Bind (newSym, transform(tree.body)).withSpan(sym.span)
2004
+ case _ =>
2005
+ super .transform(tree).withType(replaceBindingsInType(tree.tpe))
2006
+ }
2007
+ }
2008
+ private [this ] val replaceBindingsInType = new ReplaceBindings {
2009
+ override def apply (tp : Type ): Type = tp match {
2010
+ case tp : TermRef => bindMap.get(tp.termSymbol).fold[Type ](tp)(_.typeRef)
2011
+ case tp => super .apply(tp)
2012
+ }
2013
+ }
2014
+ }
2015
+
2016
+ val splicePat = typed(untpd.Tuple (splices.map(x => untpd.TypedSplice (replaceBindingsInTree.transform(x)))).withSpan(quoted.span), patType)
2017
+
1981
2018
UnApply (
1982
- fun = ref(defn.InternalQuotedMatcher_unapplyR ).appliedToType( patType),
2019
+ fun = ref(defn.InternalQuotedMatcher_unapplyR ).appliedToTypeTrees(typeBindingsTuple :: TypeTree ( patType) :: Nil ),
1983
2020
implicits =
1984
2021
ref(defn.InternalQuoted_exprQuoteR ).appliedToType(shape.tpe).appliedTo(shape) ::
2022
+ Literal (Constant (typeBindings.nonEmpty)) ::
1985
2023
implicitArgTree(defn.TastyReflectionType , tree.span) :: Nil ,
1986
2024
patterns = splicePat :: Nil ,
1987
2025
proto = pt)
@@ -1991,13 +2029,26 @@ class Typer extends Namer
1991
2029
}
1992
2030
}
1993
2031
1994
- def splitQuotePattern (quoted : Tree )(implicit ctx : Context ): (Tree , List [Tree ]) = {
2032
+ def splitQuotePattern (quoted : Tree )(implicit ctx : Context ): (Map [ Symbol , Bind ], Tree , List [Tree ]) = {
1995
2033
val ctx0 = ctx
2034
+
2035
+ val typeBindings : collection.mutable.Map [Symbol , Bind ] = collection.mutable.Map .empty
2036
+ def getBinding (sym : Symbol ): Bind =
2037
+ typeBindings.getOrElseUpdate(sym, {
2038
+ val bindingBounds = sym.info
2039
+ val bsym = ctx.newPatternBoundSymbol(sym.name.toTypeName, bindingBounds, quoted.span)
2040
+ Bind (bsym, untpd.Ident (nme.WILDCARD ).withType(bindingBounds)).withSpan(quoted.span)
2041
+ })
2042
+
1996
2043
object splitter extends tpd.TreeMap {
1997
2044
val patBuf = new mutable.ListBuffer [Tree ]
2045
+ val freshTypePatBuf = new mutable.ListBuffer [Tree ]
2046
+ val freshTypeBindingsBuff = new mutable.ListBuffer [Tree ]
2047
+ val typePatBuf = new mutable.ListBuffer [Tree ]
1998
2048
override def transform (tree : Tree )(implicit ctx : Context ) = tree match {
1999
2049
case Typed (Splice (pat), tpt) if ! tpt.tpe.derivesFrom(defn.RepeatedParamClass ) =>
2000
- val exprTpt = AppliedTypeTree (TypeTree (defn.QuotedExprType ), tpt :: Nil )
2050
+ val tpt1 = transform(tpt) // Transform type bindings
2051
+ val exprTpt = AppliedTypeTree (TypeTree (defn.QuotedExprType ), tpt1 :: Nil )
2001
2052
transform(Splice (Typed (pat, exprTpt)))
2002
2053
case Splice (pat) =>
2003
2054
try patternHole(tree)
@@ -2007,6 +2058,12 @@ class Typer extends Namer
2007
2058
val pat1 = if (patType eq patType1) pat else pat.withType(patType1)
2008
2059
patBuf += pat1
2009
2060
}
2061
+ case Select (pat, _) if tree.symbol == defn.QuotedType_splice =>
2062
+ val sym = tree.tpe.dealias.typeSymbol.asType
2063
+ val tdef = TypeDef (sym).withSpan(sym.span)
2064
+ freshTypeBindingsBuff += transformTypeBindingTypeDef(tdef, freshTypePatBuf)
2065
+ TypeTree (tree.tpe.dealias).withSpan(tree.span)
2066
+
2010
2067
case ddef : ValOrDefDef =>
2011
2068
if (ddef.symbol.hasAnnotation(defn.InternalQuoted_patternBindHoleAnnot )) {
2012
2069
val bindingType = ddef.symbol.info match {
@@ -2025,17 +2082,55 @@ class Typer extends Namer
2025
2082
patBuf += Bind (sym, untpd.Ident (nme.WILDCARD ).withType(bindingExprTpe)).withSpan(ddef.span)
2026
2083
}
2027
2084
super .transform(tree)
2085
+ case tdef : TypeDef if tdef.symbol.hasAnnotation(defn.InternalQuoted_patternBindHoleAnnot ) =>
2086
+ transformTypeBindingTypeDef(tdef, typePatBuf)
2028
2087
case _ =>
2029
2088
super .transform(tree)
2030
2089
}
2090
+
2091
+ def transformTypeBindingTypeDef (tdef : TypeDef , buff : mutable.Builder [Tree , List [Tree ]]): Tree = {
2092
+ val bindingType = getBinding(tdef.symbol).symbol.typeRef
2093
+ val bindingTypeTpe = AppliedType (defn.QuotedTypeType , bindingType :: Nil )
2094
+ assert(tdef.name.startsWith(" $" ))
2095
+ val bindName = tdef.name.toString.stripPrefix(" $" ).toTermName
2096
+ val sym = ctx0.newPatternBoundSymbol(bindName, bindingTypeTpe, tdef.span, flags = ImplicitTerm )
2097
+ buff += Bind (sym, untpd.Ident (nme.WILDCARD ).withType(bindingTypeTpe)).withSpan(tdef.span)
2098
+ super .transform(tdef)
2099
+ }
2100
+ }
2101
+ val shape0 = splitter.transform(quoted)
2102
+ val patterns = (splitter.freshTypePatBuf.iterator ++ splitter.typePatBuf.iterator ++ splitter.patBuf.iterator).toList
2103
+ val freshTypeBindings = splitter.freshTypeBindingsBuff.result()
2104
+
2105
+ val shape1 = seq(
2106
+ freshTypeBindings,
2107
+ shape0
2108
+ )
2109
+ val shape2 = {
2110
+ if (freshTypeBindings.isEmpty) shape1
2111
+ else {
2112
+ val isFreshTypeBindings = freshTypeBindings.map(_.symbol).toSet
2113
+ val typeMap = new TypeMap () {
2114
+ def apply (tp : Type ): Type = tp match {
2115
+ case tp : TypeRef if tp.typeSymbol == defn.QuotedType_splice =>
2116
+ val tp1 = tp.dealias
2117
+ if (isFreshTypeBindings(tp1.typeSymbol)) tp1
2118
+ else tp
2119
+ case tp => mapOver(tp)
2120
+ }
2121
+ }
2122
+ new TreeTypeMap (typeMap = typeMap).transform(shape1)
2123
+ }
2031
2124
}
2032
- val result = splitter.transform(quoted)
2033
- (result, splitter.patBuf.toList )
2125
+
2126
+ (typeBindings.toMap, shape2, patterns )
2034
2127
}
2035
2128
2036
2129
/** A hole the shape pattern of a quoted.Matcher.unapply, representing a splice */
2037
- def patternHole (splice : Tree )(implicit ctx : Context ): Tree =
2130
+ def patternHole (splice : Tree )(implicit ctx : Context ): Tree = {
2131
+ val Splice (pat) = splice
2038
2132
ref(defn.InternalQuoted_patternHoleR ).appliedToType(splice.tpe).withSpan(splice.span)
2133
+ }
2039
2134
2040
2135
/** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */
2041
2136
def typedSplice (tree : untpd.Splice , pt : Type )(implicit ctx : Context ): Tree = track(" typedSplice" ) {
@@ -2081,7 +2176,28 @@ class Typer extends Namer
2081
2176
ctx.warning(" Canceled quote directly inside a splice. ${ '[ XYZ ] } is equivalent to XYZ." , tree.sourcePos)
2082
2177
typed(innerType, pt)
2083
2178
case expr =>
2084
- typedSelect(untpd.Select (tree.expr, tpnme.splice), pt)(spliceContext).withSpan(tree.span)
2179
+ if (ctx.mode.is(Mode .QuotedPattern ) && level == 1 ) {
2180
+ if (isFullyDefined(pt, ForceDegree .all)) {
2181
+ ctx.error(i " Spliced type pattern must not be fully defined. Consider using $pt directly " , tree.expr.sourcePos)
2182
+ tree.withType(UnspecifiedErrorType )
2183
+ } else {
2184
+ def spliceOwner (ctx : Context ): Symbol =
2185
+ if (ctx.mode.is(Mode .QuotedPattern )) spliceOwner(ctx.outer) else ctx.owner
2186
+ val name = expr match {
2187
+ case Ident (name) => (" $" + name).toTypeName
2188
+ case Typed (Ident (name), _) => (" $" + name).toTypeName
2189
+ case Bind (name, _) => (" $" + name).toTypeName
2190
+ case _ => NameKinds .UniqueName .fresh(" $" .toTypeName)
2191
+ }
2192
+ val typeSym = ctx.newSymbol(spliceOwner(ctx), name, EmptyFlags , TypeBounds .empty, NoSymbol , expr.span)
2193
+ typeSym.addAnnotation(Annotation (New (ref(defn.InternalQuoted_patternBindHoleAnnot .typeRef)).withSpan(expr.span)))
2194
+ val pat = typedPattern(expr, defn.QuotedTypeType .appliedTo(typeSym.typeRef))(
2195
+ spliceContext.retractMode(Mode .QuotedPattern ).withOwner(spliceOwner(ctx)))
2196
+ pat.select(tpnme.splice)
2197
+ }
2198
+ } else {
2199
+ typedSelect(untpd.Select (tree.expr, tpnme.splice), pt)(spliceContext).withSpan(tree.span)
2200
+ }
2085
2201
}
2086
2202
}
2087
2203
0 commit comments