Skip to content

Commit 49adb26

Browse files
committed
Reflect inferred parameter types in enclosing method type
The variables in the inferred parameter type of an anonymous function need to also show up in the closure type itself, so that they can be constrained.
1 parent 6d892f5 commit 49adb26

File tree

3 files changed

+94
-20
lines changed

3 files changed

+94
-20
lines changed

compiler/src/dotty/tools/dotc/transform/Recheck.scala

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ abstract class Recheck extends Phase, IdentityDenotTransformer:
7272
def knownType(tree: Tree) =
7373
tree.attachmentOrElse(RecheckedType, tree.tpe)
7474

75+
def isUpdated(sym: Symbol)(using Context) =
76+
val symd = sym.denot
77+
symd.validFor.firstPhaseId == thisPhase.id && (sym.originDenotation ne symd)
78+
7579
def transformType(tp: Type, inferred: Boolean)(using Context): Type = tp
7680

7781
object transformTypes extends TreeTraverser:
@@ -104,16 +108,27 @@ abstract class Recheck extends Phase, IdentityDenotTransformer:
104108
transformType(tree.tpe, tree.isInstanceOf[InferredTypeTree]).rememberFor(tree)
105109
case tree: ValOrDefDef =>
106110
val sym = tree.symbol
107-
def integrateRT(restp: Type, info: Type, psymss: List[List[Symbol]]): Type = info match
108-
case info: MethodOrPoly =>
109-
info.derivedLambdaType(resType =
110-
integrateRT(SubstParams(psymss.head, info)(restp), info.resType, psymss.tail))
111+
def integrateRT(restp: Type, info: Type, psymss: List[List[Symbol]], pinfoss: List[List[Type]]): Type = info match
112+
case mt: MethodOrPoly =>
113+
val psyms = psymss.head
114+
val subst = SubstParams(psyms, mt)
115+
mt.derivedLambdaType(
116+
paramInfos =
117+
if psyms.exists(isUpdated) then pinfoss.head.asInstanceOf[List[mt.PInfo]]
118+
else mt.paramInfos,
119+
resType = integrateRT(
120+
subst(restp), mt.resType, psymss.tail, pinfoss.tail.nestedMap(subst)))
111121
case info: ExprType =>
112122
info.derivedExprType(resType = restp)
113123
case _ =>
114124
restp
115125
if tree.tpt.hasAttachment(RecheckedType) && !sym.isConstructor then
116-
val newInfo = integrateRT(knownType(tree.tpt), sym.info, sym.paramSymss)
126+
val newInfo = integrateRT(
127+
knownType(tree.tpt),
128+
sym.info,
129+
sym.paramSymss,
130+
sym.paramSymss.nestedMap(_.info)
131+
)
117132
.showing(i"update info $sym: ${sym.info} --> $result", recheckr)
118133
if newInfo ne sym.info then
119134
val completer = new LazyType:
@@ -331,10 +346,7 @@ abstract class Recheck extends Phase, IdentityDenotTransformer:
331346
case tree: ValOrDefDef =>
332347
if tree.isEmpty then NoType
333348
else
334-
val isUpdated =
335-
val symd = sym.denot
336-
symd.validFor.firstPhaseId == thisPhase.id && (sym.originDenotation ne symd)
337-
if isUpdated then sym.ensureCompleted()
349+
if isUpdated(sym) then sym.ensureCompleted()
338350
else recheckDef(tree, sym)
339351
sym.termRef
340352
case tree: TypeDef =>
Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,35 @@
1+
abstract class LIST[+T]:
2+
def isEmpty: Boolean
3+
def head: T
4+
def tail: LIST[T]
5+
def map[U](f: {*} T => U): LIST[U] =
6+
if isEmpty then NIL
7+
else CONS(f(head), tail.map(f))
8+
9+
class CONS[+T](x: T, xs: LIST[T]) extends LIST[T]:
10+
def isEmpty = false
11+
def head = x
12+
def tail = xs
13+
object NIL extends LIST[Nothing]:
14+
def isEmpty = true
15+
def head = ???
16+
def tail = ???
17+
18+
def map[A, B](f: {*} A => B)(xs: LIST[A]): LIST[B] =
19+
xs.map(f)
20+
121
class C
222
type Cap = {*} C
3-
class A
4-
class B
5-
def m[A, B] =
6-
??? : ((f: {*} A => B) => {f} (xs: List[A]) => List[B])
7-
//: (f: {*} A => B) => {f} (List[A] => List[B])
823

9-
def mc: (f: {*} String => Int) => {f} List[String] => List[Int] = m[String, Int]
24+
def test(c: Cap, d: Cap) =
25+
def f(x: Cap): Unit = if c == x then ()
26+
def g(x: Cap): Unit = if d == x then ()
27+
val y = f
28+
val ys = CONS(y, NIL)
29+
val zs =
30+
val z = g
31+
CONS(z, ys)
32+
val zsc: LIST[{d, y} Cap => Unit] = zs
33+
34+
val a4 = zs.map(identity)
35+
val a4c: LIST[{d, y} Cap => Unit] = a4

tests/pos-custom-args/captures/lists.scala

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ def map[A, B](f: {*} A => B)(xs: LIST[A]): LIST[B] =
2121
class C
2222
type Cap = {*} C
2323

24-
def test(c: Cap, d: Cap) =
24+
def test(c: Cap, d: Cap, e: Cap) =
2525
def f(x: Cap): Unit = if c == x then ()
2626
def g(x: Cap): Unit = if d == x then ()
2727
val y = f
2828
val ys = CONS(y, NIL)
2929
val zs =
3030
val z = g
3131
CONS(z, ys)
32+
val zsc: LIST[{d, y} Cap => Unit] = zs
3233
val z1 = zs.head
3334
val z1c: {y, d} Cap => Unit = z1
3435
val ys1 = zs.tail
@@ -45,11 +46,46 @@ def test(c: Cap, d: Cap) =
4546

4647
def m2c: [A, B] => (f: {*} A => B) => {f} LIST[A] => LIST[B] = m2
4748

49+
def eff[A](x: A) = if x == e then x else x
50+
51+
val eff2 = [A] => (x: A) => if x == e then x else x
52+
4853
val a0 = identity[{d, y} Cap => Unit]
54+
val a0c: ({d, y} Cap => Unit) => {d, y} Cap => Unit = a0
4955
val a1 = zs.map[{d, y} Cap => Unit](a0)
50-
//val a2 = zs.map[{d, y} Cap => Unit](identity[{d, y} Cap => Unit]) // fails
56+
val a1c: LIST[{d, y} Cap => Unit] = a1
57+
val a2 = zs.map[{d, y} Cap => Unit](identity[{d, y} Cap => Unit])
58+
val a2c: LIST[{d, y} Cap => Unit] = a2
5159
val a3 = zs.map(identity[{d, y} Cap => Unit])
52-
//val a4 = zs.map(identity) // fails
53-
val a5 = map(identity)(zs)
54-
val a6 = m1(identity)(zs)
60+
val a3c: LIST[{d, y} Cap => Unit] = a3
61+
val a4 = zs.map(identity)
62+
val a4c: LIST[{d, c} Cap => Unit] = a4
63+
val a5 = map[{d, y} Cap => Unit, {d, y} Cap => Unit](identity)(zs)
64+
val a5c: LIST[{d, c} Cap => Unit] = a5
65+
val a6 = m1[{d, y} Cap => Unit, {d, y} Cap => Unit](identity)(zs)
66+
val a6c: LIST[{d, c} Cap => Unit] = a6
67+
68+
val b0 = eff[{d, y} Cap => Unit]
69+
val b0c: {e} ({d, y} Cap => Unit) => {d, y} Cap => Unit = b0
70+
val b1 = zs.map[{d, y} Cap => Unit](a0)
71+
val b1c: {e} LIST[{d, y} Cap => Unit] = b1
72+
val b2 = zs.map[{d, y} Cap => Unit](eff[{d, y} Cap => Unit])
73+
val b2c: {e} LIST[{d, y} Cap => Unit] = b2
74+
val b3 = zs.map(eff[{d, y} Cap => Unit])
75+
val b3c: {e} LIST[{d, y} Cap => Unit] = b3
76+
val b4 = zs.map(eff)
77+
val b4c: {e} LIST[{d, c} Cap => Unit] = b4
78+
val b5 = map[{d, y} Cap => Unit, {d, y} Cap => Unit](eff)(zs)
79+
val b5c: {e} LIST[{d, c} Cap => Unit] = b5
80+
val b6 = m1[{d, y} Cap => Unit, {d, y} Cap => Unit](eff)(zs)
81+
val b6c: {e} LIST[{d, c} Cap => Unit] = b6
82+
83+
val c0 = eff2[{d, y} Cap => Unit]
84+
val c0c: {e} ({d, y} Cap => Unit) => {d, y} Cap => Unit = c0
85+
val c1 = zs.map[{d, y} Cap => Unit](a0)
86+
val c1c: {e} LIST[{d, y} Cap => Unit] = c1
87+
val c2 = zs.map[{d, y} Cap => Unit](eff2[{d, y} Cap => Unit])
88+
val c2c: {e} LIST[{d, y} Cap => Unit] = c2
89+
val c3 = zs.map(eff2[{d, y} Cap => Unit])
90+
val c3c: {e} LIST[{d, y} Cap => Unit] = c3
5591

0 commit comments

Comments
 (0)