From 7f20c7f13d04ef71b0241dba12fdef4f6f6e45a5 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Thu, 20 Jul 2023 10:31:28 +0800 Subject: [PATCH 1/3] Fix capture set handling in `distributeAnd` --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index fd8158b19631..661f1317f696 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -2610,10 +2610,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling case tp1: TypeVar if tp1.isInstantiated => tp1.underlying & tp2 case CapturingType(parent1, refs1) => - if subCaptures(tp2.captureSet, refs1, frozen = true).isOK + val refs2 = tp2.captureSet + if subCaptures(refs2, refs1, frozen = true).isOK && tp1.isBoxedCapturing == tp2.isBoxedCapturing then - parent1 & tp2 + if refs2.isAlwaysEmpty then parent1 & tp2 + else (parent1 & tp2).capturing(refs2) else tp1.derivedCapturingType(parent1 & tp2, refs1) case tp1: AnnotatedType if !tp1.isRefining => From efe28db88f59b7f03ba4b69ffb3be9465105df8c Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Thu, 20 Jul 2023 13:00:02 +0800 Subject: [PATCH 2/3] Add test case for #18246 --- tests/neg-custom-args/captures/cc-glb.check | 7 +++++++ tests/neg-custom-args/captures/cc-glb.scala | 8 ++++++++ 2 files changed, 15 insertions(+) create mode 100644 tests/neg-custom-args/captures/cc-glb.check create mode 100644 tests/neg-custom-args/captures/cc-glb.scala diff --git a/tests/neg-custom-args/captures/cc-glb.check b/tests/neg-custom-args/captures/cc-glb.check new file mode 100644 index 000000000000..7e0d2ff85691 --- /dev/null +++ b/tests/neg-custom-args/captures/cc-glb.check @@ -0,0 +1,7 @@ +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-glb.scala:7:19 ---------------------------------------- +7 | val x2: Foo[T] = x1 // error + | ^^ + | Found: (x1 : (Foo[T]^) & (Foo[Any]^{io})) + | Required: Foo[T] + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/cc-glb.scala b/tests/neg-custom-args/captures/cc-glb.scala new file mode 100644 index 000000000000..c22f1a36a300 --- /dev/null +++ b/tests/neg-custom-args/captures/cc-glb.scala @@ -0,0 +1,8 @@ +import language.experimental.captureChecking +trait Cap +trait Foo[+T] + +def magic[T](io: Cap^, x: Foo[T]^{io}): Foo[T]^{} = + val x1: Foo[T]^{cap} & Foo[Any]^{io} = x + val x2: Foo[T] = x1 // error + x2 // boom, an impure value becomes pure From 4c807c5e651c41d1bba3809e328d8538a188b309 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Mon, 24 Jul 2023 00:55:58 +0800 Subject: [PATCH 3/3] Roll the `isAlwaysEmpty` optimisation into `capturing` --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 7 ++----- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 661f1317f696..e37e7acf8d5e 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -2613,11 +2613,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling val refs2 = tp2.captureSet if subCaptures(refs2, refs1, frozen = true).isOK && tp1.isBoxedCapturing == tp2.isBoxedCapturing - then - if refs2.isAlwaysEmpty then parent1 & tp2 - else (parent1 & tp2).capturing(refs2) - else - tp1.derivedCapturingType(parent1 & tp2, refs1) + then (parent1 & tp2).capturing(refs2) + else tp1.derivedCapturingType(parent1 & tp2, refs1) case tp1: AnnotatedType if !tp1.isRefining => tp1.underlying & tp2 case _ => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index abade1dab82f..cd05acdbe80f 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1940,7 +1940,7 @@ object Types { * the two capture sets are combined. */ def capturing(cs: CaptureSet)(using Context): Type = - if cs.isConst && cs.subCaptures(captureSet, frozen = true).isOK then this + if cs.isAlwaysEmpty || cs.isConst && cs.subCaptures(captureSet, frozen = true).isOK then this else this match case CapturingType(parent, cs1) => parent.capturing(cs1 ++ cs) case _ => CapturingType(this, cs)