Skip to content

Commit 57a51d6

Browse files
author
Aleksander Boruch-Gruszecki
committed
Do not instantiate GadtMap TypeVars
Instead, record semi-instantiations in the constraint. Otherwise, since the TypeVars live long enough, the _second_ case of a nested pattern match would see bounds recorded by the _first_ one.
1 parent 21d0e27 commit 57a51d6

File tree

4 files changed

+266
-23
lines changed

4 files changed

+266
-23
lines changed

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

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,7 @@ object Contexts {
777777
override def addBound(sym: Symbol, bound: Type, isUpper: Boolean)(implicit ctx: Context): Boolean = inCtx(ctx) {
778778
@annotation.tailrec def stripInst(tp: Type): Type = tp match {
779779
case tv: TypeVar =>
780-
val inst = tv.inst orElse instType(tv)
780+
val inst = instType(tv)
781781
if (inst.exists) stripInst(inst) else tv
782782
case _ => tp
783783
}
@@ -821,12 +821,11 @@ object Contexts {
821821
else if (isUpper) addLess(symTvar.origin, boundTvar.origin)
822822
else addLess(boundTvar.origin, symTvar.origin)
823823
case bound =>
824-
if (cautiousSubtype(symTvar, bound, isSubtype = !isUpper)) { instantiate(symTvar, bound); true }
824+
if (cautiousSubtype(symTvar, bound, isSubtype = !isUpper)) { unify(symTvar, bound); true }
825825
else if (isUpper) addUpperBound(symTvar.origin, bound)
826826
else addLowerBound(symTvar.origin, bound)
827827
}
828828

829-
vacuum()
830829
res
831830
}
832831

@@ -835,24 +834,9 @@ object Contexts {
835834
case _ => false
836835
}
837836

838-
def instantiate(tv: TypeVar, tp: Type): Unit = {
839-
// instantiating one TypeVar to another makes us actually lose information
840-
// that is, we need to know that both TypeVars are equal to another
841-
// *and* be able to record further bounds on either one
842-
if (tp.isInstanceOf[TypeVar]) return
843-
val externalizedTp = (new TypeVarRemovingMap)(tp)
844-
gadts.println(i"instantiating $tv to $externalizedTp ( $tp )")
845-
tv.inst = externalizedTp
846-
constraint = constraint.replace(tv.origin, externalizedTp)
847-
}
848-
849-
def vacuum(): Unit = {
850-
constraint.foreachTypeVar { tv =>
851-
if (!tv.inst.exists) {
852-
val inst = instType(tv)
853-
if (inst.exists) instantiate(tv, inst)
854-
}
855-
}
837+
def unify(tv: TypeVar, tp: Type): Unit = {
838+
gadts.println(i"manually unifying $tv with $tp")
839+
constraint = constraint.updateEntry(tv.origin, tp)
856840
}
857841

858842
val tvarBound = (new TypeVarInsertingMap)(bound)
@@ -877,8 +861,7 @@ object Contexts {
877861
mapping(sym) match {
878862
case null => null
879863
case tv =>
880-
val tb =
881-
if (tv.inst.exists) TypeAlias(tv.inst) else constraint.fullBounds(tv.origin)
864+
val tb = constraint.fullBounds(tv.origin)
882865
val res = (new TypeVarRemovingMap)(tb).asInstanceOf[TypeBounds]
883866
// gadts.println(i"gadt bounds $sym: $res\t( $tv: $tb )")
884867
res

tests/gadt/GadtStlc.scala

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
object GadtStlc {
2+
// creates type-level "strings" like M[M[M[W]]]
3+
object W
4+
type W = W.type
5+
class M[A]
6+
7+
8+
// variable with name A
9+
// Var[W]
10+
// Var[M[W]]
11+
sealed trait Var[A]
12+
object VarW extends Var[W]
13+
case class VarM[A] extends Var[M[A]]
14+
15+
// \s.e
16+
sealed trait Abs[S, E]
17+
case class AbsC[S, E](v: Var[S], b: E) extends Abs[Var[S], E]
18+
19+
// e1 e2
20+
case class App[E1, E2](e1: E1, e2: E2)
21+
22+
// T1 -> T2
23+
case class TyFun[T1, T2](t1: T1, t2: T2)
24+
25+
// arbitrary base literal
26+
case object Lit
27+
type Lit = Lit.type
28+
29+
// arbitrary base type
30+
case object TyBase
31+
type TyBase = TyBase.type
32+
33+
// IN[G, (X, TY)] === evidence that binding (X, TY) is in environment G
34+
// x: ty \in G
35+
sealed trait IN[G, P]
36+
case class INOne[G, X, TY]() extends IN[(G, (X,TY)), (X, TY)]
37+
// this is wrong - we need evidence that A does not contain a binding for X
38+
case class INShift[G0, A, X, TY](in: IN[G0, (X, TY)]) extends IN[(G0, A), (X, TY)]
39+
40+
// DER[G, E, TY] === evidence that G |- E : TY
41+
sealed trait DER[G, E, TY]
42+
case class DVar[G, G0, X, TY](
43+
in: IN[(G, G0), (Var[X], TY)]
44+
) extends DER[(G, G0), Var[X], TY]
45+
46+
case class DApp[G, E1, E2, TY1, TY2](
47+
d1: DER[G, E1, TyFun[TY1, TY2]],
48+
d2: DER[G, E2, TY1]
49+
) extends DER[G, App[E1, E2], TY2]
50+
51+
case class DAbs[G, X, E, TY1, TY2](
52+
d1: DER[(G, (Var[X], TY1)), E, TY2]
53+
) extends DER[G, Abs[Var[X], E], TyFun[TY1, TY2]]
54+
55+
case class DLit[G]() extends DER[G, Lit, TyBase]
56+
57+
// forall G, a. G |- \x.x : a -> a
58+
def test1[G, TY]: DER[G, Abs[Var[W], Var[W]], TyFun[TY, TY]] =
59+
DAbs(DVar(INOne()))
60+
61+
// forall G. G |- \x.x : Lit -> Lit
62+
def test2[G]: DER[G, App[Abs[Var[W], Var[W]], Lit], TyBase] =
63+
DApp(DAbs(DVar(INOne())), DLit())
64+
65+
// forall G, c. G |- \x.\y. x y : (c -> c) -> c -> c
66+
def test3[G, TY]: DER[G,
67+
Abs[Var[W],
68+
Abs[Var[M[W]],
69+
App[Var[W], Var[M[W]]]
70+
]
71+
],
72+
TyFun[TyFun[TY, TY], TyFun[TY, TY]]
73+
] = DAbs(DAbs(DApp(DVar(INShift(INOne())), DVar(INOne()))))
74+
75+
76+
// evidence that E is a value
77+
sealed trait ISVAL[E]
78+
case class ISVAL_Abs[X, E]() extends ISVAL[Abs[Var[X], E]]
79+
case object ISVAL_Lit extends ISVAL[Lit]
80+
81+
// evidence that E1 reduces to E2
82+
sealed trait REDUDER[E1, E2]
83+
case class EApp1[E1a, E1b, E2](
84+
ed: REDUDER[E1a, E1b]
85+
) extends REDUDER[App[E1a, E2], App[E1b, E2]]
86+
87+
case class EApp2[V1, E2a, E2b](
88+
isval: ISVAL[V1],
89+
ed: REDUDER[E2a, E2b]
90+
) extends REDUDER[App[V1, E2a], App[V1, E2b]]
91+
92+
case class EAppAbs[X, E, V2, R](
93+
isval: ISVAL[V2]
94+
// cheating - subst is hard
95+
// , subst: SUBST[E, X, V2, R]
96+
) extends REDUDER[App[Abs[Var[X], E], V2], R]
97+
98+
// evidence that V is a lambda
99+
sealed trait ISLAMBDA[V]
100+
case class ISLAMBDAC[X, E]() extends ISLAMBDA[Abs[Var[X], E]]
101+
102+
// evidence that E reduces
103+
type REDUCES[E] = REDUDER[E, _]
104+
105+
def followsIsLambda[G, V, TY1, TY2](
106+
isval: ISVAL[V],
107+
der: DER[G, V, TyFun[TY1, TY2]]
108+
): ISLAMBDA[V] = (isval, der) match {
109+
case (_: ISVAL_Abs[x, e], _) => ISLAMBDAC[x, e]()
110+
}
111+
112+
// \empty |- E : TY ==> E is a value /\ E reduces to some E1
113+
def progress[E, TY](der: DER[Unit, E, TY]): Either[ISVAL[E], REDUCES[E]] =
114+
der match {
115+
case _: DAbs[g, a, e, ty1, ty2] => Left(ISVAL_Abs[a, e]())
116+
case DLit() => Left(ISVAL_Lit)
117+
case dapp: DApp[Unit, a, b, ty1, ty2] => progress(dapp.d1) match {
118+
case Right(r1) => Right(EApp1[E2 = b](r1))
119+
case Left(isv1) => progress(dapp.d2) match {
120+
case Right(r2) => Right(EApp2(isv1, r2))
121+
case Left(isv2) => followsIsLambda(isv1, dapp.d1) match {
122+
case _: ISLAMBDAC[x, e] => Right(EAppAbs[x, e, b, _](isv2))
123+
}
124+
}
125+
}
126+
}
127+
}

tests/gadt/TypeSafeLambda.scala

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
object TypeSafeLambda {
2+
3+
trait Category[Arr[_, _]] {
4+
def id[A]: Arr[A, A]
5+
def comp[A, B, C](ab: Arr[A, B], bc: Arr[B, C]): Arr[A, C]
6+
}
7+
8+
trait Terminal[Term, Arr[_, _]] extends Category[Arr] {
9+
def terminal[A]: Arr[A, Term]
10+
}
11+
12+
trait ProductCategory[Prod[_, _], Arr[_, _]] extends Category[Arr] {
13+
def first[A, B]: Arr[Prod[A, B], A]
14+
def second[A, B]: Arr[Prod[A, B], B]
15+
def pair[A, B, C](ab: Arr[A, B], ac: Arr[A, C]): Arr[A, Prod[B, C]]
16+
}
17+
18+
trait Exponential[Exp[_, _], Prod[_, _], Arr[_, _]]
19+
extends ProductCategory[Prod, Arr] {
20+
def eval[A, B]: Arr[Prod[Exp[A, B], A], B]
21+
def curry[A, B, C](a: Arr[Prod[C, A], B]): Arr[C, Exp[A, B]]
22+
}
23+
24+
trait CartesianClosed[Term, Exp[_, _], Prod[_, _], Arr[_, _]]
25+
extends Exponential[Exp, Prod, Arr] with Terminal[Term, Arr]
26+
27+
sealed trait V[Prod[_, _], Env, R]
28+
case class Zero[Prod[_, _], Env, R]() extends V[Prod, Prod[Env, R], R]
29+
case class Succ[Prod[_, _], Env, R, X](
30+
v: V[Prod, Env, R]
31+
) extends V[Prod, Prod[Env, X], R]
32+
33+
sealed trait Lambda[Terminal, Exp[_, _], Prod[_, _], Env, R]
34+
35+
case class LUnit[Terminal, Exp[_, _], Prod[_, _], Env]()
36+
extends Lambda[Terminal, Exp, Prod, Env, Terminal]
37+
38+
case class Var[Terminal, Exp[_, _], Prod[_, _], Env, R](
39+
v: V[Prod, Env, R]
40+
) extends Lambda[Terminal, Exp, Prod, Env, R]
41+
42+
case class Lam[Terminal, Exp[_, _], Prod[_, _], Env, R, A](
43+
lam: Lambda[Terminal, Exp, Prod, Prod[Env, A], R]
44+
) extends Lambda[Terminal, Exp, Prod, Env, Exp[A, R]]
45+
46+
case class App[Terminal, Exp[_, _], Prod[_, _], Env, R, R_](
47+
f: Lambda[Terminal, Exp, Prod, Env, Exp[R, R_]],
48+
a: Lambda[Terminal, Exp, Prod, Env, R]
49+
) extends Lambda[Terminal, Exp, Prod, Env, R_]
50+
51+
def interp[Term, Exp[_, _], Prod[_, _], Arr[_, _], S, T](
52+
c: CartesianClosed[Term, Exp, Prod, Arr],
53+
exp: Lambda[Term, Exp, Prod, S, T]
54+
): Arr[S, T] = exp match {
55+
case LUnit() => c.terminal
56+
case v: Var[t, e, var_p, env, r] => v.v match {
57+
case _: Zero[z_p, z_env, z_r] => c.second[z_env, z_r]
58+
case s: Succ[s_prod, s_env, s_r, x] =>
59+
c.comp[A = s_prod[s_env, x], C = s_r](
60+
c.first,
61+
interp(c, Var[Term, Exp, Prod, s_env, s_r](s.v))
62+
)
63+
}
64+
case Lam(lam) => c.curry(interp(c, lam))
65+
case app: App[t, e, p, env, r, r_] =>
66+
c.comp(
67+
c.pair(
68+
interp(c, app.f),
69+
interp(c, app.a)),
70+
c.eval[r, r_]
71+
)
72+
}
73+
74+
object example {
75+
type Term = Unit
76+
type Prod[A, B] = (A, B)
77+
type Exp[A, B] = A => B
78+
type Arr[A, B] = A => B
79+
80+
val c = new CartesianClosed[Term, Exp, Prod, Arr] {
81+
def id[A]: A => A = a => a
82+
def comp[A, B, C](f: A => B, g: B => C): A => C = f andThen g
83+
84+
def terminal[A]: A => Unit = a => ()
85+
86+
def first[A, B]: ((A, B)) => A = { case (a, _) => a }
87+
def second[A, B]: ((A, B)) => B = { case (_, b) => b }
88+
def pair[A, B, C](f: A => B, g: A => C): A => (B, C) =
89+
a => (f(a), g(a))
90+
91+
def eval[A, B]: ((A => B, A)) => B = { case (f, a) => f(a) }
92+
def curry[A, B, C](f: ((C, A)) => B): C => A => B =
93+
c => a => f((c, a))
94+
}
95+
96+
type Env = Unit Prod Int Prod (Int => String)
97+
val exp = App[Term, Exp, Prod, Env, Int, String](
98+
// args to Var are RHS "indices" into Env
99+
Var(Zero()),
100+
Var(Succ(Zero()))
101+
)
102+
103+
val interped: (Env) => String =
104+
interp[Term, Exp, Prod, Arr, Env, String] (c, exp)
105+
106+
interped((((), 1), { i: Int => i.toString })) : String // "1"
107+
}
108+
109+
}

tests/gadt/complexEQ.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
object complexEQ {
2+
sealed trait EQ[A, B]
3+
final case class Refl[A]() extends EQ[A, A]
4+
5+
def m[A, B, C, D](e1: EQ[A, (B, C)], e2: EQ[A, (C, D)], d: D): A =
6+
e1 match {
7+
case Refl() => e2 match {
8+
case Refl() =>
9+
val r1: (B, B) = (d, d)
10+
val r2: (C, C) = r1
11+
val r3: (D, D) = r1
12+
r1
13+
}
14+
}
15+
16+
def m2[Z, A, B, C, D](e0: EQ[Z, A], e1: EQ[A, (B, C)], e2: EQ[Z, (C, D)], d: D): Z =
17+
(e0, e1, e2) match {
18+
case (Refl(), Refl(), Refl()) =>
19+
val r1: (B, B) = (d, d)
20+
val r2: (C, C) = r1
21+
val r3: (D, D) = r1
22+
r1
23+
}
24+
}

0 commit comments

Comments
 (0)