@@ -1971,13 +1971,51 @@ class Typer extends Namer
19711971 val exprPt = pt.baseType(defn.QuotedExprClass )
19721972 val quotedPt = if (exprPt.exists) exprPt.argTypesHi.head else defn.AnyType
19731973 val quoted1 = typedExpr(quoted, quotedPt)(quoteContext.addMode(Mode .QuotedPattern ))
1974- val (shape, splices) = splitQuotePattern(quoted1)
1975- val patType = defn.tupleType(splices.tpes.map(_.widen))
1976- val splicePat = typed(untpd.Tuple (splices.map(untpd.TypedSplice (_))).withSpan(quoted.span), patType)
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+
19772014 UnApply (
1978- fun = ref(defn.InternalQuotedMatcher_unapplyR ).appliedToType( patType),
2015+ fun = ref(defn.InternalQuotedMatcher_unapplyR ).appliedToTypeTrees(typeBindingsTuple :: TypeTree ( patType) :: Nil ),
19792016 implicits =
19802017 ref(defn.InternalQuoted_exprQuoteR ).appliedToType(shape.tpe).appliedTo(shape) ::
2018+ Literal (Constant (typeBindings.nonEmpty)) ::
19812019 implicitArgTree(defn.TastyReflectionType , tree.span) :: Nil ,
19822020 patterns = splicePat :: Nil ,
19832021 proto = pt)
@@ -1987,13 +2025,26 @@ class Typer extends Namer
19872025 }
19882026 }
19892027
1990- def splitQuotePattern (quoted : Tree )(implicit ctx : Context ): (Tree , List [Tree ]) = {
2028+ def splitQuotePattern (quoted : Tree )(implicit ctx : Context ): (Map [ Symbol , Bind ], Tree , List [Tree ]) = {
19912029 val ctx0 = ctx
2030+
2031+ val typeBindings : collection.mutable.Map [Symbol , Bind ] = collection.mutable.Map .empty
2032+ def getBinding (sym : Symbol ): Bind =
2033+ typeBindings.getOrElseUpdate(sym, {
2034+ val bindingBounds = sym.info
2035+ val bsym = ctx.newPatternBoundSymbol(sym.name.toTypeName, bindingBounds, quoted.span)
2036+ Bind (bsym, untpd.Ident (nme.WILDCARD ).withType(bindingBounds)).withSpan(quoted.span)
2037+ })
2038+
19922039 object splitter extends tpd.TreeMap {
19932040 val patBuf = new mutable.ListBuffer [Tree ]
2041+ val freshTypePatBuf = new mutable.ListBuffer [Tree ]
2042+ val freshTypeBindingsBuff = new mutable.ListBuffer [Tree ]
2043+ val typePatBuf = new mutable.ListBuffer [Tree ]
19942044 override def transform (tree : Tree )(implicit ctx : Context ) = tree match {
19952045 case Typed (Splice (pat), tpt) if ! tpt.tpe.derivesFrom(defn.RepeatedParamClass ) =>
1996- val exprTpt = AppliedTypeTree (TypeTree (defn.QuotedExprType ), tpt :: Nil )
2046+ val tpt1 = transform(tpt) // Transform type bindings
2047+ val exprTpt = AppliedTypeTree (TypeTree (defn.QuotedExprType ), tpt1 :: Nil )
19972048 transform(Splice (Typed (pat, exprTpt)))
19982049 case Splice (pat) =>
19992050 try patternHole(tree)
@@ -2003,6 +2054,12 @@ class Typer extends Namer
20032054 val pat1 = if (patType eq patType1) pat else pat.withType(patType1)
20042055 patBuf += pat1
20052056 }
2057+ case Select (pat, _) if tree.symbol == defn.QuotedType_splice =>
2058+ val sym = tree.tpe.dealias.typeSymbol.asType
2059+ val tdef = TypeDef (sym).withSpan(sym.span)
2060+ freshTypeBindingsBuff += transformTypeBindingTypeDef(tdef, freshTypePatBuf)
2061+ TypeTree (tree.tpe.dealias).withSpan(tree.span)
2062+
20062063 case ddef : ValOrDefDef =>
20072064 if (ddef.symbol.hasAnnotation(defn.InternalQuoted_patternBindHoleAnnot )) {
20082065 val bindingType = ddef.symbol.info match {
@@ -2021,17 +2078,55 @@ class Typer extends Namer
20212078 patBuf += Bind (sym, untpd.Ident (nme.WILDCARD ).withType(bindingExprTpe)).withSpan(ddef.span)
20222079 }
20232080 super .transform(tree)
2081+ case tdef : TypeDef if tdef.symbol.hasAnnotation(defn.InternalQuoted_patternBindHoleAnnot ) =>
2082+ transformTypeBindingTypeDef(tdef, typePatBuf)
20242083 case _ =>
20252084 super .transform(tree)
20262085 }
2086+
2087+ def transformTypeBindingTypeDef (tdef : TypeDef , buff : mutable.Builder [Tree , List [Tree ]]): Tree = {
2088+ val bindingType = getBinding(tdef.symbol).symbol.typeRef
2089+ val bindingTypeTpe = AppliedType (defn.QuotedTypeType , bindingType :: Nil )
2090+ assert(tdef.name.startsWith(" $" ))
2091+ val bindName = tdef.name.toString.stripPrefix(" $" ).toTermName
2092+ val sym = ctx0.newPatternBoundSymbol(bindName, bindingTypeTpe, tdef.span, flags = ImplicitTerm )
2093+ buff += Bind (sym, untpd.Ident (nme.WILDCARD ).withType(bindingTypeTpe)).withSpan(tdef.span)
2094+ super .transform(tdef)
2095+ }
2096+ }
2097+ val shape0 = splitter.transform(quoted)
2098+ val patterns = (splitter.freshTypePatBuf.iterator ++ splitter.typePatBuf.iterator ++ splitter.patBuf.iterator).toList
2099+ val freshTypeBindings = splitter.freshTypeBindingsBuff.result()
2100+
2101+ val shape1 = seq(
2102+ freshTypeBindings,
2103+ shape0
2104+ )
2105+ val shape2 = {
2106+ if (freshTypeBindings.isEmpty) shape1
2107+ else {
2108+ val isFreshTypeBindings = freshTypeBindings.map(_.symbol).toSet
2109+ val typeMap = new TypeMap () {
2110+ def apply (tp : Type ): Type = tp match {
2111+ case tp : TypeRef if tp.typeSymbol == defn.QuotedType_splice =>
2112+ val tp1 = tp.dealias
2113+ if (isFreshTypeBindings(tp1.typeSymbol)) tp1
2114+ else tp
2115+ case tp => mapOver(tp)
2116+ }
2117+ }
2118+ new TreeTypeMap (typeMap = typeMap).transform(shape1)
2119+ }
20272120 }
2028- val result = splitter.transform(quoted)
2029- (result, splitter.patBuf.toList )
2121+
2122+ (typeBindings.toMap, shape2, patterns )
20302123 }
20312124
20322125 /** A hole the shape pattern of a quoted.Matcher.unapply, representing a splice */
2033- def patternHole (splice : Tree )(implicit ctx : Context ): Tree =
2126+ def patternHole (splice : Tree )(implicit ctx : Context ): Tree = {
2127+ val Splice (pat) = splice
20342128 ref(defn.InternalQuoted_patternHoleR ).appliedToType(splice.tpe).withSpan(splice.span)
2129+ }
20352130
20362131 /** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */
20372132 def typedSplice (tree : untpd.Splice , pt : Type )(implicit ctx : Context ): Tree = track(" typedSplice" ) {
@@ -2077,7 +2172,28 @@ class Typer extends Namer
20772172 ctx.warning(" Canceled quote directly inside a splice. ${ '[ XYZ ] } is equivalent to XYZ." , tree.sourcePos)
20782173 typed(innerType, pt)
20792174 case expr =>
2080- typedSelect(untpd.Select (tree.expr, tpnme.splice), pt)(spliceContext).withSpan(tree.span)
2175+ if (ctx.mode.is(Mode .QuotedPattern ) && level == 1 ) {
2176+ if (isFullyDefined(pt, ForceDegree .all)) {
2177+ ctx.error(i " Spliced type pattern must not be fully defined. Consider using $pt directly " , tree.expr.sourcePos)
2178+ tree.withType(UnspecifiedErrorType )
2179+ } else {
2180+ def spliceOwner (ctx : Context ): Symbol =
2181+ if (ctx.mode.is(Mode .QuotedPattern )) spliceOwner(ctx.outer) else ctx.owner
2182+ val name = expr match {
2183+ case Ident (name) => (" $" + name).toTypeName
2184+ case Typed (Ident (name), _) => (" $" + name).toTypeName
2185+ case Bind (name, _) => (" $" + name).toTypeName
2186+ case _ => NameKinds .UniqueName .fresh(" $" .toTypeName)
2187+ }
2188+ val typeSym = ctx.newSymbol(spliceOwner(ctx), name, EmptyFlags , TypeBounds .empty, NoSymbol , expr.span)
2189+ typeSym.addAnnotation(Annotation (New (ref(defn.InternalQuoted_patternBindHoleAnnot .typeRef)).withSpan(expr.span)))
2190+ val pat = typedPattern(expr, defn.QuotedTypeType .appliedTo(typeSym.typeRef))(
2191+ spliceContext.retractMode(Mode .QuotedPattern ).withOwner(spliceOwner(ctx)))
2192+ pat.select(tpnme.splice)
2193+ }
2194+ } else {
2195+ typedSelect(untpd.Select (tree.expr, tpnme.splice), pt)(spliceContext).withSpan(tree.span)
2196+ }
20812197 }
20822198 }
20832199
0 commit comments