Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Insert top level type variables before typing quote pattern #16883

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1518,7 +1518,7 @@ object Trees {
paramss.mapConserve(transformParams)

protected def transformMoreCases(tree: Tree)(using Context): Tree = {
assert(ctx.reporter.errorsReported)
assert(ctx.reporter.errorsReported, tree)
tree
}
}
Expand Down
81 changes: 78 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Flags._
import dotty.tools.dotc.core.NameKinds.PatMatGivenVarName
import dotty.tools.dotc.core.NameOps.isVarPattern
import dotty.tools.dotc.core.Names._
import dotty.tools.dotc.core.StagingContext._
import dotty.tools.dotc.core.StdNames._
Expand All @@ -24,7 +25,6 @@ import dotty.tools.dotc.util.Stats.record
import dotty.tools.dotc.reporting.IllegalVariableInPatternAlternative
import scala.collection.mutable


/** Type quotes `'{ ... }` and splices `${ ... }` */
trait QuotesAndSplices {
self: Typer =>
Expand Down Expand Up @@ -394,9 +394,84 @@ trait QuotesAndSplices {
}
val quoted0 = desugar.quotedPattern(quoted, untpd.TypedSplice(TypeTree(quotedPt)))
val quoteCtx = quoteContext.addMode(Mode.QuotedPattern).retractMode(Mode.Pattern)

def normalizeTypeBindings(quoted: untpd.Tree): untpd.Tree =
val variables = mutable.Set.empty[TypeName] // TODO use stable order
val typeNormalizer = new untpd.UntypedTreeMap {
override def transform(tpt: untpd.Tree)(using Context) =
tpt match
case untpd.Ident(tpnme.WILDCARD_STAR) => tpt
case tpt @ untpd.Ident(name) if name.isTypeName && !tpt.isBackquoted && name.isVarPattern =>
variables += name.asTypeName
tpt.pushAttachment(Trees.Backquoted, ())
tpt
case _: untpd.TypedSplice => tpt
case _ => super.transform(tpt)
}
val termNormalizer = new untpd.UntypedTreeMap {
override def transform(tree: untpd.Tree)(using Context) =
tree match
case untpd.Splice(_) => tree
case untpd.Typed(expr, tpt) =>
untpd.cpy.Typed(tree)(
super.transform(expr),
typeNormalizer.transform(tpt)
)
case untpd.TypeApply(fn, targs) =>
untpd.cpy.TypeApply(tree)(
super.transform(fn),
targs.map(typeNormalizer.transform)
)
case _ => super.transform(tree)
}

val transformed: untpd.Tree =
if quoted.isType then quoted // FIXME: typeNormalizer.transform(quoted)
else termNormalizer.transform(quoted)

def typeBindingDefinedInSource: List[TypeName] =
transformed match
case untpd.Block(stats, _) =>
stats.takeWhile {
case untpd.TypeDef(name, _) => name.isVarPattern
case _ => false
}.map(_.asInstanceOf[untpd.TypeDef].name.asTypeName)
case _ => Nil
variables --= typeBindingDefinedInSource

// println("==============")
// println(quoted.show)
// println(quoted)
// println("--------------")
// println(transformed.show)
// println(transformed)
// println(" ")
// println(variables)
// println(" ")

if variables.isEmpty then transformed
else
transformed match
case untpd.Block(stats, expr) =>
val typeBindings = stats.takeWhile {
case untpd.TypeDef(name, _) => name.isVarPattern
case _ => false
}
variables --= typeBindings.map(_.asInstanceOf[untpd.TypeDef].name.asTypeName)
untpd.cpy.Block(quoted)(
variables.toList.map(name => untpd.TypeDef(name, untpd.TypeBoundsTree(untpd.EmptyTree, untpd.EmptyTree, untpd.EmptyTree))) ::: stats,
expr
)
case _ =>
untpd.cpy.Block(quoted)(
variables.toList.map(name => untpd.TypeDef(name, untpd.TypeBoundsTree(untpd.EmptyTree, untpd.EmptyTree, untpd.EmptyTree))),
transformed
)

val quoted0normalized = normalizeTypeBindings(quoted0)
val quoted1 =
if quoted.isType then typedType(quoted0, WildcardType)(using quoteCtx)
else typedExpr(quoted0, WildcardType)(using quoteCtx)
if quoted.isType then typedType(quoted0normalized, WildcardType)(using quoteCtx)
else typedExpr(quoted0normalized, WildcardType)(using quoteCtx)

val (typeBindings, shape, splices) = splitQuotePattern(quoted1)

Expand Down
2 changes: 1 addition & 1 deletion tests/neg-macros/quotedPatterns-5.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import scala.quoted.*
object Test {
def test(x: quoted.Expr[Int])(using Quotes): Unit = x match {
case '{ type t; 4 } => Type.of[t]
case '{ type t; poly[t]($x); 4 } => // error: duplicate pattern variable: t
case '{ type t; poly[t]($x); 4 } =>
case '{ type `t`; poly[`t`]($x); 4 } =>
Type.of[t] // error
case _ =>
Expand Down
18 changes: 18 additions & 0 deletions tests/pos-macros/i14708.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import scala.quoted.*

object Main {
def foo(a: Expr[Any])(using Quotes) = {
a match {
case '{ ($x: Set[t]).toSet } =>
case '{ ($x: Set[t]).toSet[t] } =>
case '{ val a = 1; a; ($x: Set[t]).toSet } =>
case '{ type u; ($x: Set[`u`]).toSet } =>
case '{ type t; ($x: Set[t]).toSet } =>
case '{ varargs(${_}*) } =>
case '{ type u; ($x: t, $y: t, $z: u) } =>
case _ =>
}
}
}

def varargs(x: Any*): Unit = ()
2 changes: 1 addition & 1 deletion tests/pos-macros/i16265.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ class Foo(val value: Int)

def foo(exprs: Expr[Any])(using Quotes): Any =
exprs match
case '{ $tuple: (Foo *: tail) } =>
case '{ type tail <: Tuple; $tuple: (Foo *: tail) } => // FIXME infer bounds of tail
val x = '{ ${tuple}.head.value }
???
2 changes: 1 addition & 1 deletion tests/pos-macros/i7264b.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import scala.quoted.*
class Foo {
def f[T2: Type](e: Expr[T2])(using Quotes) = e match {
case '{ $x: *:[Int, t] } =>
case '{ type t <: Tuple; $x: *:[Int, t] } => // FIXME infer bounds of tail
Type.of[ *:[Int, t] ]
}
}