Skip to content

Commit 5610aa4

Browse files
committed
wip
1 parent 2b95f1d commit 5610aa4

File tree

9 files changed

+197
-8
lines changed

9 files changed

+197
-8
lines changed

Foo.scala

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
class a {
2+
3+
def foo(x: Any) =
4+
5+
new Foo { type T = Int; type Tup = (Int, List[Int]); val contents: Tup = (3, 5 :: Nil) } match {
6+
case Bar(x, ls) =>
7+
val l2 = x :: ls
8+
l2
9+
}
10+
new Foo { val contents = ??? } match {
11+
case Bar(x, ls) =>
12+
val l2 = x :: ls
13+
l2
14+
}
15+
}
16+
17+
18+
19+
trait Foo {
20+
type T
21+
type Tup <: Tuple
22+
val contents: Tup
23+
}
24+
25+
object Foo {
26+
def unaplly[F <: Foo](arg: F): Option[arg.Tup] = Some(arg.contents)
27+
}
28+
29+
object Bar {
30+
def unapply(arg: Foo { type Tup = (T, List[T]) }): Option[arg.Tup] =
31+
Foo.unaplly[Foo { type Tup = (T, List[T]) }](arg)
32+
}

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
11811181
/** An extractor for typed splices */
11821182
object Splice {
11831183
def apply(tree: Tree)(implicit ctx: Context): Tree = {
1184-
val baseType = tree.tpe.baseType(defn.QuotedExprClass)
1184+
val baseType = tree.tpe.baseType(defn.QuotedExprClass).orElse(tree.tpe.baseType(defn.QuotedTypeClass))
11851185
val argType =
11861186
if (baseType != NoType) baseType.argTypesHi.head
11871187
else {

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ class Definitions {
723723
lazy val InternalQuoted_patternHoleR: TermRef = InternalQuotedModule.requiredMethodRef("patternHole")
724724
def InternalQuoted_patternHole(implicit ctx: Context): Symbol = InternalQuoted_patternHoleR.symbol
725725
lazy val InternalQuoted_patternBindHoleAnnot: ClassSymbol = InternalQuotedModule.requiredClass("patternBindHole")
726+
lazy val InternalQuoted_patternTypeHole: Symbol = InternalQuotedModule.requiredType("patternTypeHole")
726727

727728
lazy val InternalQuotedMatcherModuleRef: TermRef = ctx.requiredModuleRef("scala.internal.quoted.Matcher")
728729
def InternalQuotedMatcherModule(implicit ctx: Context): Symbol = InternalQuotedMatcherModuleRef.symbol

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
584584
else keywordStr("'{") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
585585
case Splice(tree) =>
586586
keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
587+
case TypSplice(tree) =>
588+
keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
587589
case tree: Applications.IntegratedTypeArgs =>
588590
toText(tree.app) ~ Str("(with integrated type args)").provided(ctx.settings.YprintDebug.value)
589591
case Thicket(trees) =>

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

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1944,12 +1944,22 @@ class Typer extends Namer
19441944
val quotedPt = if (exprPt.exists) exprPt.argTypesHi.head else defn.AnyType
19451945
val quoted1 = typedExpr(quoted, quotedPt)(quoteContext.addMode(Mode.QuotedPattern))
19461946
val (shape, splices) = splitQuotePattern(quoted1)
1947+
val typeBindings = splices.collect {
1948+
case t if t.tpe.derivesFrom(defn.QuotedTypeClass) =>
1949+
t.tpe.widen.argTypesHi.head.typeSymbol
1950+
}
1951+
val inQuoteTypeBinding = typeBindings.map { sym =>
1952+
ctx.newSymbol(sym.owner, (sym.name + "$$$").toTypeName, // TODO remove $$$, just there for debugging
1953+
EmptyFlags, sym.info, coord = sym.coord)
1954+
}
1955+
val shape2 =
1956+
seq(inQuoteTypeBinding.map(TypeDef), shape.subst(typeBindings, inQuoteTypeBinding))
19471957
val patType = defn.tupleType(splices.tpes.map(_.widen))
19481958
val splicePat = typed(untpd.Tuple(splices.map(untpd.TypedSplice(_))).withSpan(quoted.span), patType)
19491959
UnApply(
19501960
fun = ref(defn.InternalQuotedMatcher_unapplyR).appliedToType(patType),
19511961
implicits =
1952-
ref(defn.InternalQuoted_exprQuoteR).appliedToType(shape.tpe).appliedTo(shape) ::
1962+
ref(defn.InternalQuoted_exprQuoteR).appliedToType(shape.tpe).appliedTo(shape2) ::
19531963
implicitArgTree(defn.TastyReflectionType, tree.span) :: Nil,
19541964
patterns = splicePat :: Nil,
19551965
proto = pt)
@@ -2002,8 +2012,11 @@ class Typer extends Namer
20022012
}
20032013

20042014
/** A hole the shape pattern of a quoted.Matcher.unapply, representing a splice */
2005-
def patternHole(splice: Tree)(implicit ctx: Context): Tree =
2006-
ref(defn.InternalQuoted_patternHoleR).appliedToType(splice.tpe).withSpan(splice.span)
2015+
def patternHole(splice: Tree)(implicit ctx: Context): Tree = {
2016+
val Splice(pat) = splice
2017+
if (pat.tpe.derivesFrom(defn.QuotedTypeClass)) AppliedTypeTree(ref(defn.InternalQuoted_patternTypeHole), TypeTree(splice.tpe) :: Nil).withSpan(splice.span)
2018+
else ref(defn.InternalQuoted_patternHoleR).appliedToType(splice.tpe).withSpan(splice.span)
2019+
}
20072020

20082021
/** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */
20092022
def typedSplice(tree: untpd.Splice, pt: Type)(implicit ctx: Context): Tree = track("typedSplice") {
@@ -2042,9 +2055,34 @@ class Typer extends Namer
20422055

20432056
/** Translate ${ t: Type[T] }` into type `t.splice` while tracking the quotation level in the context */
20442057
def typedTypSplice(tree: untpd.TypSplice, pt: Type)(implicit ctx: Context): Tree = track("typedTypSplice") {
2058+
// TODO factor out comon code with typedSplice
20452059
ctx.compilationUnit.needsStaging = true
20462060
checkSpliceOutsideQuote(tree)
2047-
typedSelect(untpd.Select(tree.expr, tpnme.splice), pt)(spliceContext).withSpan(tree.span)
2061+
tree.expr match {
2062+
case untpd.Quote(innerExpr) =>
2063+
ctx.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.sourcePos)
2064+
typed(innerExpr, pt)
2065+
case expr =>
2066+
if (ctx.mode.is(Mode.QuotedPattern) && level == 1) {
2067+
if (isFullyDefined(pt, ForceDegree.all)) {
2068+
ctx.error(i"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.sourcePos)
2069+
tree.withType(UnspecifiedErrorType)
2070+
} else {
2071+
val bindingBounds = TypeBounds.apply(defn.NothingType, defn.AnyType)
2072+
val sym = ctx.newPatternBoundSymbol("ttt".toTypeName, bindingBounds, expr.span)
2073+
val bind = Bind(sym, untpd.Ident(nme.WILDCARD).withType(bindingBounds)).withSpan(expr.span)
2074+
2075+
def spliceOwner(ctx: Context): Symbol =
2076+
if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner
2077+
val pat = typedPattern(tree.expr, defn.QuotedTypeType.appliedTo(sym.typeRef))(
2078+
spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
2079+
Splice(Typed(pat, AppliedTypeTree(TypeTree(defn.QuotedTypeType), bind :: Nil)))
2080+
}
2081+
2082+
} else {
2083+
typedSelect(untpd.Select(tree.expr, tpnme.splice), pt)(spliceContext).withSpan(tree.span)
2084+
}
2085+
}
20482086
}
20492087

20502088
private def checkSpliceOutsideQuote(tree: untpd.Tree)(implicit ctx: Context): Unit = {

library/src-3.x/scala/internal/Quoted.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ object Quoted {
2424
/** A splice of a name in a quoted pattern is desugared by wrapping getting this annotation */
2525
class patternBindHole extends Annotation
2626

27+
type patternTypeHole[T] = T
2728
}

tests/pos/quotedPatterns.scala

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
object Test {
22

33
val x = '{1 + 2}
4-
5-
def f(x: Int) = x
6-
def g(x: Int, y: Int) = x * y
4+
//
5+
// def f(x: Int) = x
6+
// def g(x: Int, y: Int) = x * y
77

88
def res given tasty.Reflection: quoted.Expr[Int] = x match {
99
case '{1 + 2} => '{0}
@@ -30,6 +30,10 @@ object Test {
3030
case '{ def $ff[T](i: T): Int = $z; 2 } =>
3131
val a: quoted.matching.Bind[[T] => T => Int] = ff
3232
z
33+
// case '{ poly[$t]($x); 2 } => ???
34+
case '{ val x: $t = $x; (${scala.quoted.matching.Bind(t2)}: x.type); 4 } if t == t2 => ???
3335
case _ => '{1}
3436
}
37+
38+
def poly[T](x: T): Unit = ()
3539
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import scala.quoted._
2+
import scala.quoted.matching._
3+
4+
import scala.tasty.Reflection
5+
6+
import scala.internal.quoted.Matcher._
7+
import scala.internal.Quoted._
8+
9+
object Macros {
10+
11+
inline def swapFandG(x: => Unit): Unit = ${impl('x)}
12+
13+
private def impl(x: Expr[Unit])(implicit reflect: Reflection): Expr[Unit] = {
14+
15+
type TT // type binding
16+
object DSLf {
17+
def unapply(x: Expr[_])(implicit reflect: Reflection): Option[Tuple2[Type[_], Expr[Any]]] =
18+
scala.internal.quoted.Matcher.unapply[Tuple2[Type[_], Expr[Any]]](x)('{type t; DSL.f[Hole[t]](patternHole[t]) }, reflect)
19+
} // case '{ DSL.f[$t]($x) } =>
20+
21+
x match {
22+
// case '{ DSL.f[$t]($x) } =>
23+
// case scala.internal.quoted.Matcher.unapply[Tuple2[Type[tt @ _], Expr[tt]]](Tuple2(t, TypedExpr(x)(`t`, reflect))(/*implicits*/ '{ DSL.f[Hole[Nothing, Any]](hole[Any])] }, reflect) =>
24+
case DSLf((t: Type[tt], x: Expr[t2])) =>
25+
26+
implicit val tt = t
27+
'{ DSL.g[$t]($x) }
28+
29+
// case '{ DSL.f[$t]($x) } =>
30+
// case DSLg(t, x) =>
31+
// '{ DSL.f[$t]($x) }
32+
???
33+
case _ =>
34+
x
35+
}
36+
}
37+
38+
}
39+
40+
41+
object TypedExpr {
42+
def unapply[T](arg: Expr[_])(implicit t: Type[T], reflect: Reflection): Option[Expr[T]] = {
43+
import reflect._
44+
if (arg.unseal.tpe <:< t.unseal.tpe) Some(arg.asInstanceOf[Expr[T]])
45+
else None
46+
}
47+
}
48+
49+
50+
//
51+
// DSL in which the user write the code
52+
//
53+
54+
object DSL {
55+
def f[T](x: T): Unit = println("f: " + x.toString)
56+
def g[T](x: T): Unit = println("g: " + x.toString)
57+
}
58+
59+
//
60+
// Helper to abstract call to scala.internal.quoted.Matcher.unapply and setup an object with the unapply
61+
//
62+
63+
class ExprMatch[Tup <: Tuple](pattern: Expr[_]) {
64+
def unapply(x: Expr[_])(implicit reflect: Reflection): Option[Tup] =
65+
scala.internal.quoted.Matcher.unapply[Tup](x)(pattern, reflect)
66+
}
67+
68+
69+
//class a {
70+
//
71+
// def foo(x: Any) =
72+
//
73+
// new Foo { type T = Int; type Tup = (Int, List[Int]); val contents: Tup = (3, 5 :: Nil) } match {
74+
// case Bar(x, ls) =>
75+
// val l2 = x :: ls
76+
// l2
77+
// }
78+
// new Foo { val contents = ??? } match {
79+
// case Bar(x, ls) =>
80+
// val l2 = x :: ls
81+
// l2
82+
// }
83+
//}
84+
//
85+
//
86+
//
87+
//trait Foo {
88+
// type T
89+
// type Tup <: Tuple
90+
// val contents: Tup
91+
//}
92+
//
93+
//object Foo {
94+
// def unaplly[F <: Foo](arg: F): Option[arg.Tup] = Some(arg.contents)
95+
//}
96+
//
97+
//object Bar {
98+
// def unapply(arg: Foo { type Tup = (T, List[T]) }): Option[arg.Tup] =
99+
// Foo.unaplly[Foo { type Tup = (T, List[T]) }](arg)
100+
//}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import Macros._
2+
3+
4+
object Test {
5+
6+
def main(args: Array[String]): Unit = {
7+
swapFandG(DSL.f(5))
8+
// swapFandG(DSL.g("abc"))
9+
}
10+
11+
}

0 commit comments

Comments
 (0)