Skip to content

Commit e4e073b

Browse files
committed
Handle bound types in type patterns
# Conflicts: # tests/run/TupleImpl.scala
1 parent 6ee3c62 commit e4e073b

File tree

2 files changed

+152
-4
lines changed

2 files changed

+152
-4
lines changed

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

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
629629
/** Try to match pattern `pat` against scrutinee reference `scrut`. If successful add
630630
* bindings for variables bound in this pattern to `bindingsBuf`.
631631
*/
632-
def reducePattern(bindingsBuf: mutable.ListBuffer[MemberDef], scrut: TermRef, pat: Tree): Boolean = {
632+
def reducePattern(bindingsBuf: mutable.ListBuffer[MemberDef], scrut: TermRef, pat: Tree)(implicit ctx: Context): Boolean = {
633633
val isImplicit = scrut.info == defn.ImplicitScrutineeTypeRef
634634

635635
def newBinding(name: TermName, flags: FlagSet, rhs: Tree): Symbol = {
@@ -655,8 +655,30 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
655655

656656
pat match {
657657
case Typed(pat1, tpt) =>
658+
val getBoundVars = new TreeAccumulator[List[TypeSymbol]] {
659+
def apply(syms: List[TypeSymbol], t: Tree)(implicit ctx: Context) = {
660+
val syms1 = t match {
661+
case t: Bind if t.symbol.isType => t.symbol.asType :: syms
662+
case _ => syms
663+
}
664+
foldOver(syms1, t)
665+
}
666+
}
667+
val boundVars = getBoundVars(Nil, tpt)
668+
for (bv <- boundVars) ctx.gadt.setBounds(bv, bv.info.bounds)
658669
if (isImplicit) searchImplicit(nme.WILDCARD, tpt)
659-
else scrut <:< tpt.tpe && reducePattern(bindingsBuf, scrut, pat1)
670+
else scrut <:< tpt.tpe && {
671+
for (bv <- boundVars) {
672+
bv.info = TypeAlias(ctx.gadt.bounds(bv).lo)
673+
// FIXME: This is very crude. We should approximate with lower or higher bound depending
674+
// on variance, and we should also take care of recursive bounds. Basically what
675+
// ConstraintHandler#approximation does. However, this only works for constrained paramrefs
676+
// not GADT-bound variables. Hopefully we will get some way to improve this when we
677+
// re-implement GADTs in terms of constraints.
678+
bindingsBuf += TypeDef(bv)
679+
}
680+
reducePattern(bindingsBuf, scrut, pat1)
681+
}
660682
case pat @ Bind(name: TermName, Typed(_, tpt)) if isImplicit =>
661683
searchImplicit(name, tpt)
662684
case pat @ Bind(name: TermName, body) =>
@@ -714,8 +736,9 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
714736
}
715737
val caseBindingsBuf = new mutable.ListBuffer[MemberDef]()
716738
if (scrutType != defn.ImplicitScrutineeTypeRef) caseBindingsBuf += scrutineeBinding
717-
val pat1 = typer.typedPattern(cdef.pat, scrutType)(typer.gadtContext(gadtSyms))
718-
if (reducePattern(caseBindingsBuf, scrutineeSym.termRef, pat1) && guardOK)
739+
val gadtCtx = typer.gadtContext(gadtSyms).addMode(Mode.GADTflexible)
740+
val pat1 = typer.typedPattern(cdef.pat, scrutType)(gadtCtx)
741+
if (reducePattern(caseBindingsBuf, scrutineeSym.termRef, pat1)(gadtCtx) && guardOK)
719742
Some((caseBindingsBuf.toList, cdef.body))
720743
else
721744
None
@@ -843,6 +866,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
843866
case none => true
844867
}
845868
} && !boundSym.is(TransparentImplicitMethod)
869+
// FIXME: It would be nice if we could also drop type bindings, but reference counting is trickier for them.
846870

847871
val inlineBindings = new TreeMap {
848872
override def transform(t: Tree)(implicit ctx: Context) = t match {

tests/run/TupleAbstract.scala

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package test
2+
3+
import annotation.showAsInfix
4+
5+
class TypeLevel {
6+
type Tuple
7+
type Empty <: Tuple
8+
type Pair[+H, +T <: Tuple] <: Tuple
9+
erased def erasedValue[T]: T = ???
10+
case class Typed[T](val value: T) { type Type = T }
11+
}
12+
13+
object Tuples {
14+
val typelevel = new TypeLevel
15+
import typelevel._
16+
17+
transparent def _empty: typelevel.Tuple = erasedValue[Empty]
18+
transparent def _pair[H, T <: typelevel.Tuple] (x: H, xs: T): typelevel.Tuple = erasedValue[Pair[H, T]]
19+
20+
transparent def _size(xs: typelevel.Tuple): Int = xs match {
21+
case _: typelevel.Empty => 0
22+
case _: typelevel.Pair[x1, xs1] => _size(erasedValue[xs1]) + 1
23+
}
24+
25+
val x = _size(erasedValue[Pair[Int, Pair[String, Empty]]])
26+
/*
27+
transparent def _index(xs: Tuple, n: Int): Any = xs match {
28+
case x *: _ if n == 0 => x
29+
case _ *: xs1 if n > 0 => _index(xs1, n - 1)
30+
}
31+
32+
class TupleOps(val xs: Tuple) extends AnyVal {
33+
34+
transparent def *: [H] (x: H): Tuple = new *:(x, xs)
35+
transparent def size: Int = _size(xs)
36+
37+
transparent def apply(n: Int): Any = {
38+
erased val typed = Typed(_index(xs, n))
39+
val result = _size(xs) match {
40+
case 1 =>
41+
n match {
42+
case 1 => xs.asInstanceOf[Tuple1[_]].__1
43+
}
44+
case 2 =>
45+
n match {
46+
case 1 => xs.asInstanceOf[Tuple2[_, _]].__1
47+
case 2 => xs.asInstanceOf[Tuple2[_, _]].__2
48+
}
49+
case 3 =>
50+
n match {
51+
case 1 => xs.asInstanceOf[Tuple3[_, _, _]].__1
52+
case 2 => xs.asInstanceOf[Tuple3[_, _, _]].__2
53+
case 3 => xs.asInstanceOf[Tuple3[_, _, _]].__3
54+
}
55+
case 4 =>
56+
n match {
57+
case 1 => xs.asInstanceOf[Tuple4[_, _, _, _]].__1
58+
case 2 => xs.asInstanceOf[Tuple4[_, _, _, _]].__2
59+
case 3 => xs.asInstanceOf[Tuple4[_, _, _, _]].__3
60+
case 4 => xs.asInstanceOf[Tuple4[_, _, _, _]].__4
61+
}
62+
}
63+
result.asInstanceOf[typed.Type]
64+
}
65+
transparent def **: (ys: Tuple): Tuple = ys match {
66+
case Empty => xs
67+
case y *: ys1 => y *: (ys1 **: xs)
68+
}
69+
transparent def head = xs match {
70+
case x *: _ => x
71+
}
72+
transparent def tail = xs match {
73+
case _ *: xs => xs
74+
}
75+
}
76+
77+
val emptyArray = Array[Object]()
78+
79+
transparent def toObj(t: Any) = t.asInstanceOf[Object]
80+
81+
transparent def toArray(t: Tuple): Array[Object] = t.size match {
82+
case 0 => emptyArray
83+
case 1 => Array(toObj(t(0)))
84+
case 2 => Array(toObj(t(0)), toObj(t(1)))
85+
case 3 => Array(toObj(t(0)), toObj(t(1)), toObj(t(2)))
86+
case 4 => Array(toObj(t(0)), toObj(t(1)), toObj(t(2)), toObj(t(3)))
87+
}
88+
89+
transparent implicit def tupleDeco(xs: Tuple): TupleOps = new TupleOps(xs)
90+
91+
transparent def apply(): Tuple = Empty
92+
transparent def apply(x1: Any): Tuple = x1 *: Empty
93+
transparent def apply(x1: Any, x2: Any) = x1 *: x2 *: Empty
94+
transparent def apply(x1: Any, x2: Any, x3: Any) = x1 *: x2 *: x3 *: Empty
95+
96+
val xs0 = Tuple()
97+
val xs1 = Tuple(2)
98+
val xs2 = Tuple(2, "a")
99+
val xs3 = Tuple(true, 1, 2.0)
100+
transparent val s0 = xs0.size; val s0c: 0 = s0
101+
transparent val s1 = xs1.size; val s1c: 1 = s1
102+
transparent val s2 = xs2.size; val s2c: 2 = s2
103+
transparent val s3 = xs3.size; val s3c: 3 = s3
104+
val e0 = xs3(0); val e0c: Boolean = e0
105+
val e1 = xs3(1); val e1c: Int = e1
106+
val e2 = xs3(2); val e2c: Double = e2
107+
108+
val conc0 = xs0 **: xs3
109+
val conc1 = xs3 **: xs0
110+
val conc2 = xs2 **: xs3
111+
val e3c: Int = conc0(1)
112+
val e4c: Int = conc1(1)
113+
val e5c: Int = conc2(0)
114+
val e6c: Double = conc2(4)
115+
*/
116+
}
117+
/*
118+
class Tuple1[+T1](val __1: T1) extends *:(__1, Empty)
119+
class Tuple2[+T1, +T2](val __1: T1, val __2: T2) extends *:(__1, *:(__2, Empty))
120+
class Tuple3[+T1, +T2, +T3](val __1: T1, val __2: T2, val __3: T3) extends *:(__1, *:(__2, *:(__3, Empty)))
121+
class Tuple4[+T1, +T2, +T3, +T4](val __1: T1, val __2: T2, val __3: T3, val __4: T4) extends *:(__1, *:(__2, *:(__3, *:(__4, Empty))))
122+
123+
object Test extends App
124+
*/

0 commit comments

Comments
 (0)