Skip to content

Commit

Permalink
Insert top level type variables before typing quote pattern
Browse files Browse the repository at this point in the history
```scala
case '{ type u; ($x: t, $y: t, $z: u) }
```
is desugared to
```scala
case '{ type t; type u; ($x: `t`, $y: `t`, $z: `u`) }
```

Fixes #14708
  • Loading branch information
nicolasstucki committed Feb 12, 2023
1 parent 0ab89e6 commit 41a4277
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 7 deletions.
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] ]
}
}

0 comments on commit 41a4277

Please sign in to comment.