Skip to content

Commit 5bbca48

Browse files
committed
Avoid spurious empty sets on MutableType in TypeComparer
1 parent c1c4d98 commit 5bbca48

File tree

3 files changed

+30
-4
lines changed

3 files changed

+30
-4
lines changed

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
554554
if tp1.isBoxedCapturing && !parent1.isBoxedCapturing
555555
then tp2.unboxed
556556
else tp2
557-
recur(parent1, tp2a)
557+
if parent1.captureSet.isAlwaysEmpty then
558+
recur(parent1, tp2a.stripCapturing) // drop rhs capset to avoid possibe read-only adaptations
559+
else
560+
recur(parent1, tp2a)
558561
else thirdTry
559562
compareCapturing
560563
case tp1: AnnotatedType if !tp1.isRefining =>
@@ -862,8 +865,13 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
862865
def compareCapturing: Boolean =
863866
val refs1 = tp1.captureSet
864867
try
865-
if refs1.isAlwaysEmpty then recur(tp1, parent2)
868+
if refs1.isAlwaysEmpty
869+
//&& !(parent2.derivesFromMutable && refs2.mutability == CaptureSet.Mutability.Mutable)
870+
then//
871+
recur(tp1, parent2)
866872
else
873+
if false && refs1.isAlwaysEmpty then
874+
throw AssertionError(i"NO FAST PATH FOR $tp1 <:< $tp2")
867875
// The singletonOK branch is because we sometimes have a larger capture set in a singleton
868876
// than in its underlying type. An example is `f: () -> () ->{x} T`, which might be
869877
// the type of a closure (in one of the variants we are considering). In that case the
@@ -2811,7 +2819,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
28112819
tp1.underlying & tp2
28122820
case CapturingType(parent1, refs1) =>
28132821
val jointRefs = refs1 ** tp2.captureSet
2814-
if jointRefs.isAlwaysEmpty then parent1 & tp2
2822+
if jointRefs.isAlwaysEmpty
2823+
&& !parent1.derivesFromCapability
2824+
&& !tp2.derivesFromCapability
2825+
then parent1 & tp2
28152826
else if tp1.isBoxCompatibleWith(tp2) then
28162827
tp1.derivedCapturingType(parent1 & tp2, jointRefs)
28172828
else NoType

tests/neg-custom-args/captures/leaky.check

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,18 @@
2525
| Note that capability x is not included in capture set {}.
2626
|
2727
| longer explanation available when compiling with `-explain`
28+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/leaky.scala:36:16 ----------------------------------------
29+
36 | val t = withA(leak) // error
30+
| ^^^^
31+
|Capability cap outlives its scope: it leaks into outer capture set 's1 which is owned by value t.
32+
|The leakage occurred when trying to match the following types:
33+
|
34+
|Found: (a: test.runnable.A^) ->'s2 test.runnable.Transform{val fun: Any => Any}^'s3
35+
|Required: test.runnable.A^ =>² test.runnable.Transform{val fun: Any ->'s1 Any}^'s4
36+
|
37+
|where: => refers to a root capability associated with the result type of (a: test.runnable.A^): test.runnable.Transform{val fun: Any => Any}^'s3
38+
| =>² refers to a fresh root capability created in value t when checking argument to parameter body of method withA
39+
| ^ refers to the universal root capability
40+
| cap is a root capability associated with the result type of (a: test.runnable.A^): test.runnable.Transform{val fun: Any ->'s1 Any}^'s4
41+
|
42+
| longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/leaky.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,5 @@ def leak2(a: A): Transform^{} =
3333
def withA[T](body: A => T): T = body(A())
3434

3535
@main def Main() =
36-
val t = withA(leak)
36+
val t = withA(leak) // error
3737
t.run()

0 commit comments

Comments
 (0)