From c56803801d145f5f9f8c36cd8d58d11577618cec Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 2 Oct 2021 13:56:57 +0200 Subject: [PATCH] Refine rule for this widening We now widen the expected type of the right hand side of a class member as follows: Add all references of the declared type of this that are not subsumed by a capture set of a parameter type. --- compiler/src/dotty/tools/dotc/cc/CaptureSet.scala | 9 ++++++++- .../src/dotty/tools/dotc/typer/CheckCaptures.scala | 12 ++++++++---- tests/neg-custom-args/captures/lazylists1.check | 2 +- tests/pos-custom-args/captures/lazylists.scala | 1 + 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index f8ca2f87e3c5..a26d0a952332 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -49,7 +49,14 @@ sealed abstract class CaptureSet extends Showable: /** Is this capture set definitely non-empty? */ final def isNotEmpty: Boolean = !elems.isEmpty - /** Cast to variable. @pre: @isConst */ + /** Cast to Const. @pre: isConst */ + def asConst: Const = this match + case c: Const => c + case v: Var => + assert(v.isConst) + Const(v.elems) + + /** Cast to variable. @pre: !isConst */ def asVar: Var = assert(!isConst) asInstanceOf[Var] diff --git a/compiler/src/dotty/tools/dotc/typer/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/typer/CheckCaptures.scala index fd419d6f1e03..070d46b08ead 100644 --- a/compiler/src/dotty/tools/dotc/typer/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/typer/CheckCaptures.scala @@ -325,10 +325,14 @@ class CheckCaptures extends Recheck: override def recheckRHS(tree: Tree, pt: Type, sym: Symbol)(using Context): Type = val pt1 = pt match case CapturingType(core, refs, _) - if sym.owner.isClass - && refs.elems.contains(sym.owner.thisType) - && sym.paramSymss.forall(_.forall(p => p.isType || p.info.captureSet.isAlwaysEmpty)) => - pt.derivedCapturingType(core, refs ++ sym.owner.asClass.givenSelfType.captureSet) + if sym.owner.isClass && refs.elems.contains(sym.owner.thisType) => + val paramCaptures = + sym.paramSymss.flatten.foldLeft(CaptureSet.empty) { (cs, p) => + val pcs = p.info.captureSet + (cs ++ (if pcs.isConst then pcs else CaptureSet.universal)).asConst + } + val declaredCaptures = sym.owner.asClass.givenSelfType.captureSet + pt.derivedCapturingType(core, refs ++ (declaredCaptures -- paramCaptures)) case _ => pt recheck(tree, pt1) diff --git a/tests/neg-custom-args/captures/lazylists1.check b/tests/neg-custom-args/captures/lazylists1.check index 2fabd54ba680..29291c8044c0 100644 --- a/tests/neg-custom-args/captures/lazylists1.check +++ b/tests/neg-custom-args/captures/lazylists1.check @@ -2,6 +2,6 @@ 25 | def concat(other: {f} LazyList[A]): {this} LazyList[A] = ??? : ({xs, f} LazyList[A]) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Found: {xs, f} LazyList[A] - | Required: {Mapped.this} LazyList[A] + | Required: {Mapped.this, xs} LazyList[A] longer explanation available when compiling with `-explain` diff --git a/tests/pos-custom-args/captures/lazylists.scala b/tests/pos-custom-args/captures/lazylists.scala index 917e1d73cd26..0f12bb4f7b0f 100644 --- a/tests/pos-custom-args/captures/lazylists.scala +++ b/tests/pos-custom-args/captures/lazylists.scala @@ -21,6 +21,7 @@ extension [A](xs: {*} LazyList[A]) def isEmpty = false def head: B = f(xs.head) def tail: {this} LazyList[B] = xs.tail.map(f) // OK + def concat(other: {f} LazyList[A]): {this, f} LazyList[A] = ??? : ({xs, f} LazyList[A]) // OK new Mapped def test(cap1: Cap, cap2: Cap) =