Skip to content

Commit 7ac9c7b

Browse files
committed
Fix glb logic involving capturing types
1 parent da65097 commit 7ac9c7b

File tree

4 files changed

+57
-6
lines changed

4 files changed

+57
-6
lines changed

compiler/src/dotty/tools/dotc/cc/CaptureOps.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,14 @@ extension (tp: Type)
227227
case tp: OrType => tp.tp1.isBoxedCapturing || tp.tp2.isBoxedCapturing
228228
case _ => false
229229

230+
/** Is the box status of `tp` and `tp2` compatible? I.ee they are
231+
* box boxed, or both unboxed, or one of them has an empty capture set.
232+
*/
233+
def isBoxCompatibleWith(tp2: Type)(using Context): Boolean =
234+
isBoxedCapturing == tp2.isBoxedCapturing
235+
|| tp.captureSet.isAlwaysEmpty
236+
|| tp2.captureSet.isAlwaysEmpty
237+
230238
/** If this type is a capturing type, the version with boxed statues as given by `boxed`.
231239
* If it is a TermRef of a capturing type, and the box status flips, widen to a capturing
232240
* type that captures the TermRef.

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2403,7 +2403,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
24032403
else
24042404
def mergedGlb(tp1: Type, tp2: Type): Type =
24052405
val tp1a = dropIfSuper(tp1, tp2)
2406-
if tp1a ne tp1 then glb(tp1a, tp2)
2406+
if tp1a ne tp1 then
2407+
glb(tp1a, tp2)
24072408
else
24082409
val tp2a = dropIfSuper(tp2, tp1)
24092410
if tp2a ne tp2 then glb(tp1, tp2a)
@@ -2721,11 +2722,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
27212722
case tp1: TypeVar if tp1.isInstantiated =>
27222723
tp1.underlying & tp2
27232724
case CapturingType(parent1, refs1) =>
2724-
val refs2 = tp2.captureSet
2725-
if subCaptures(refs2, refs1, frozen = true).isOK
2726-
&& tp1.isBoxedCapturing == tp2.isBoxedCapturing
2727-
then (parent1 & tp2).capturing(refs2)
2728-
else tp1.derivedCapturingType(parent1 & tp2, refs1)
2725+
val jointRefs = refs1 ** tp2.captureSet
2726+
if jointRefs.isAlwaysEmpty then parent1 & tp2
2727+
else if tp1.isBoxCompatibleWith(tp2) then
2728+
tp1.derivedCapturingType(parent1 & tp2, jointRefs)
2729+
else NoType
27292730
case tp1: AnnotatedType if !tp1.isRefining =>
27302731
tp1.underlying & tp2
27312732
case _ =>

tests/pos/gears-probem-1.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import language.experimental.captureChecking
2+
3+
trait Future[+T]:
4+
def await: T
5+
6+
trait Channel[+T]:
7+
def read(): Ok[T]
8+
9+
class Collector[T](val futures: Seq[Future[T]^]):
10+
val results: Channel[Future[T]^{futures*}] = ???
11+
end Collector
12+
13+
class Result[+T, +E]:
14+
def get: T = ???
15+
16+
case class Err[+E](e: E) extends Result[Nothing, E]
17+
case class Ok[+T](x: T) extends Result[T, Nothing]
18+
19+
extension [T](fs: Seq[Future[T]^])
20+
def awaitAll =
21+
val collector//: Collector[T]{val futures: Seq[Future[T]^{fs*}]}
22+
= Collector(fs)
23+
// val ch = collector.results // also errors
24+
val fut: Future[T]^{fs*} = collector.results.read().get // found ...^{caps.cap}

tests/pos/gears-probem.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import language.experimental.captureChecking
2+
3+
trait Future[+T]:
4+
def await: T
5+
6+
trait Channel[T]:
7+
def read(): Either[Nothing, T]
8+
9+
class Collector[T](val futures: Seq[Future[T]^]):
10+
val results: Channel[Future[T]^{futures*}] = ???
11+
end Collector
12+
13+
extension [T](fs: Seq[Future[T]^])
14+
def awaitAll =
15+
val collector: Collector[T]{val futures: Seq[Future[T]^{fs*}]}
16+
= Collector(fs)
17+
// val ch = collector.results // also errors
18+
val fut: Future[T]^{fs*} = collector.results.read().right.get // found ...^{caps.cap}

0 commit comments

Comments
 (0)