Skip to content

Commit 761b98e

Browse files
Ang9876michelou
authored andcommitted
Feature GADT support for class type parameters
- Register class type parameters from outer contexts in typedDefdef. - Remove a condition to support gadt approximation for class type parameters. - Add test cases in tests/pos/class-gadt. - Ignore test cases tests/pos/i4345.scala and i5735.scala (issue scala#11220 and scala#11221)
1 parent 1388371 commit 761b98e

File tree

9 files changed

+186
-2
lines changed

9 files changed

+186
-2
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,7 @@ object Inferencing {
250250
* approx, see gadt-approximation-interaction.scala).
251251
*/
252252
def apply(tp: Type): Type = tp.dealias match {
253-
case tp @ TypeRef(qual, nme) if (qual eq NoPrefix)
254-
&& variance != 0
253+
case tp @ TypeRef(qual, nme) if variance != 0
255254
&& ctx.gadt.contains(tp.symbol)
256255
=>
257256
val sym = tp.symbol

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2030,6 +2030,15 @@ class Typer extends Namer
20302030
val tparamss = paramss1.collect {
20312031
case untpd.TypeDefs(tparams) => tparams
20322032
}
2033+
2034+
// Register GADT constraint for class type parameters from outer to inner class definition. (Useful when nested classes exist.) But do not cross a function definition.
2035+
if sym.flags.is(Method) then
2036+
rhsCtx.setFreshGADTBounds
2037+
ctx.outer.outersIterator.takeWhile(!_.owner.is(Method))
2038+
.filter(ctx => ctx.owner.isClass && ctx.owner.typeParams.nonEmpty)
2039+
.toList.reverse
2040+
.map(ctx => rhsCtx.gadt.addToConstraint(ctx.owner.typeParams))
2041+
20332042
if tparamss.nonEmpty then
20342043
rhsCtx.setFreshGADTBounds
20352044
val tparamSyms = tparamss.flatten.map(_.symbol)

tests/pos/class-gadt/basic.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
object basic {
2+
enum Expr[A] {
3+
case IntExpr(value: Int) extends Expr[Int]
4+
case Other[T](value: T) extends Expr[T]
5+
}
6+
7+
class C[A] {
8+
def eval(e: Expr[A]): A =
9+
e match {
10+
case Expr.IntExpr(i) => i + 2
11+
case Expr.Other(v) => v
12+
}
13+
}
14+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
object inheritance{
2+
enum SUB[-A, +B]:
3+
case Refl[S]() extends SUB[S, S]
4+
5+
class A[T](val v: T) {
6+
val foo1: T = v
7+
}
8+
9+
class C[T](val v1: T) extends A[T](v1) {
10+
def eval1(t: T, e: SUB[T, Int]): Int =
11+
e match {
12+
case SUB.Refl() => foo1
13+
}
14+
}
15+
}

tests/pos/class-gadt/member.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
object member{
2+
enum SUB[-A, +B]:
3+
case Refl[S]() extends SUB[S, S]
4+
5+
class C[T] {
6+
def eval1(t: T, e: SUB[T, Int]): Int =
7+
e match {
8+
case SUB.Refl() => t + 2
9+
}
10+
}
11+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Nested class for GADT constraining
2+
object nestedClass{
3+
enum Expr[A] {
4+
case IntExpr(value: Int) extends Expr[Int]
5+
case Other[T](value: T) extends Expr[T]
6+
}
7+
8+
class Outer1[C] {
9+
class Inner1 {
10+
def eval(e: Expr[C]): C =
11+
e match {
12+
case Expr.IntExpr(i) => i + 2
13+
case Expr.Other(v) => v
14+
}
15+
}
16+
}
17+
18+
def foo2[C](): Unit =
19+
class Outer2 {
20+
class Inner2 {
21+
def eval(e: Expr[C]): C =
22+
e match {
23+
case Expr.IntExpr(i) => i + 2
24+
case Expr.Other(v) => v
25+
}
26+
}
27+
}
28+
29+
class Outer3[C] {
30+
def foo3(): Unit =
31+
class Inner3 {
32+
def eval(e: Expr[C]): C =
33+
e match {
34+
case Expr.IntExpr(i) => i + 2
35+
case Expr.Other(v) => v
36+
}
37+
}
38+
}
39+
40+
trait Outer4[C] {
41+
class Inner4 {
42+
def eval(e: Expr[C]): C =
43+
e match {
44+
case Expr.IntExpr(i) => i + 2
45+
case Expr.Other(v) => v
46+
}
47+
}
48+
}
49+
50+
class Outer5[C] {
51+
object Inner5 {
52+
def eval(e: Expr[C]): C =
53+
e match {
54+
case Expr.IntExpr(i) => i + 2
55+
case Expr.Other(v) => v
56+
}
57+
}
58+
}
59+
}

tests/pos/class-gadt/variance.scala

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
object variance {
2+
enum SUB[-A, +B]:
3+
case Refl[S]() extends SUB[S, S]
4+
// Covariant
5+
class C1[+T](v: T){
6+
def foo(ev: T SUB Int): Int =
7+
ev match {
8+
case SUB.Refl() => v
9+
}
10+
}
11+
12+
// Contravariant
13+
class B2[-T](v: T){}
14+
15+
class C2[-T](v: T){
16+
def foo(ev: Int SUB T): B2[T] =
17+
ev match {
18+
case SUB.Refl() => new B2(v)
19+
}
20+
}
21+
22+
// Variance with inheritance
23+
24+
// superclass covariant and subclass covariant
25+
26+
class A3[+T](v: T) {
27+
val value = v
28+
}
29+
30+
class C3[+T](v: T) extends A3[T](v){
31+
def foo(ev: T SUB Int): Int =
32+
ev match {
33+
case SUB.Refl() => value
34+
}
35+
}
36+
37+
38+
// superclass covariant and subclass invariant
39+
class A4[+T](v: T) {
40+
val value = v
41+
}
42+
43+
class C4[T](v: T) extends A4[T](v){
44+
def foo(ev: T SUB Int): Int =
45+
ev match {
46+
case SUB.Refl() => value
47+
}
48+
}
49+
50+
// superclass contravariant and subclass contravariant
51+
class B5[-T](v: T){}
52+
53+
class A5[-T](v: T) {
54+
val value = new B5(v)
55+
}
56+
57+
class C5[-T](v: T) extends A5[T](v){
58+
def foo(ev: Int SUB T): B5[T] =
59+
ev match {
60+
case SUB.Refl() => value
61+
}
62+
}
63+
64+
// superclass contravariant and subclass invariant
65+
class B6[-T](v: T){}
66+
67+
class A6[-T](v: T) {
68+
val value = new B6(v)
69+
}
70+
71+
class C6[-T](v: T) extends A6[T](v){
72+
def foo(ev: Int SUB T): B6[T] =
73+
ev match {
74+
case SUB.Refl() => value
75+
}
76+
}
77+
}
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)