Skip to content

Commit cb84fb1

Browse files
committed
Apply this adaptation also to assignments
1 parent e41ab66 commit cb84fb1

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ abstract class Recheck extends Phase, IdentityDenotTransformer:
195195

196196
def recheckAssign(tree: Assign)(using Context): Type =
197197
val lhsType = recheck(tree.lhs)
198-
recheck(tree.rhs, lhsType.widen)
198+
recheckRHS(tree.rhs, lhsType.widen, tree.lhs.symbol)
199199
defn.UnitType
200200

201201
def recheckBlock(stats: List[Tree], expr: Tree, pt: Type)(using Context): Type =

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,12 @@ class CheckCaptures extends Recheck:
213213
interpolateVarsIn(tree.tpt)
214214
curEnv = saved
215215

216+
/** Capture check right hand side of the definition of `sym`, or of an assignment
217+
* to `sym`. If `sym` is a member of a final class with self type `cs T`, recheck `tree`
218+
* with an expected type that allows as a captureset all references in `cs`
219+
* except references that are covered by some parameter of `sym`. See #13657 for
220+
* a more detailed explanation of why we want to do this, and why it looks sound.
221+
*/
216222
override def recheckRHS(tree: Tree, pt: Type, sym: Symbol)(using Context): Type =
217223
val pt1 = pt match
218224
case CapturingType(core, refs, _)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import language.experimental.saferExceptions
2+
import annotation.unchecked.uncheckedVariance
3+
4+
trait LazyList[+A]:
5+
this: {*} LazyList[A] =>
6+
7+
def isEmpty: Boolean
8+
def head: A
9+
def tail: {this} LazyList[A]
10+
11+
object LazyNil extends LazyList[Nothing]:
12+
def isEmpty: Boolean = true
13+
def head = ???
14+
def tail = ???
15+
16+
final class LazyCons[+T](val x: T, val xs: () => {*} LazyList[T]) extends LazyList[T]:
17+
this: {*} LazyList[T] =>
18+
19+
var forced = false
20+
var cache: {this} LazyList[T @uncheckedVariance] = compiletime.uninitialized
21+
22+
private def force =
23+
if !forced then
24+
cache = xs()
25+
forced = true
26+
cache
27+
28+
def isEmpty = false
29+
def head = x
30+
def tail: {this} LazyList[T] = force
31+
32+
extension [A](xs: {*} LazyList[A])
33+
def map[B](f: A => B): {xs, f} LazyList[B] =
34+
if xs.isEmpty then LazyNil
35+
else LazyCons(f(xs.head), () => xs.tail.map(f))
36+
37+
class Ex1 extends Exception
38+
class Ex2 extends Exception
39+
40+
def test(using cap1: CanThrow[Ex1], cap2: CanThrow[Ex2]) =
41+
val xs = LazyCons(1, () => LazyNil)
42+
43+
def f(x: Int): Int throws Ex1 =
44+
if x < 0 then throw Ex1()
45+
x * x
46+
47+
val res = xs.map(f)
48+
res: {cap1} LazyList[Int]
49+

0 commit comments

Comments
 (0)