From d38f257603acc27d9cc0e83b1b7fd98dce17aabf Mon Sep 17 00:00:00 2001 From: noti0na1 <noti0na1@users.noreply.github.com> Date: Fri, 23 May 2025 14:13:14 +0200 Subject: [PATCH 1/4] Add handling for classes deriving from Capability in capturing type logic --- compiler/src/dotty/tools/dotc/cc/Setup.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index c9b4546c8c37..8c622cfe8c8d 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -728,6 +728,9 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: else if cls.isPureClass then // is cls is known to be pure, nothing needs to be added to self type selfInfo + else if cls.derivesFrom(defn.Caps_Capability) then + CapturingType(cinfo.selfType, CaptureSet.csImpliedByCapability) + // CapturingType(cinfo.selfType, CaptureSet.universal) else if !cls.isEffectivelySealed && !cls.baseClassHasExplicitNonUniversalSelfType then // assume {cap} for completely unconstrained self types of publicly extensible classes CapturingType(cinfo.selfType, CaptureSet.universal) From 5995e3b02ce9b291a13540927e02c3adca10da16 Mon Sep 17 00:00:00 2001 From: noti0na1 <noti0na1@users.noreply.github.com> Date: Fri, 23 May 2025 16:20:12 +0200 Subject: [PATCH 2/4] Make self fresh in capability classes --- compiler/src/dotty/tools/dotc/cc/Setup.scala | 7 +++++-- tests/neg-custom-args/captures/i23223.scala | 12 ++++++++++++ tests/pos-custom-args/captures/i20237.scala | 4 ++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/neg-custom-args/captures/i23223.scala diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 8c622cfe8c8d..d91e235841ec 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -729,8 +729,11 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: // is cls is known to be pure, nothing needs to be added to self type selfInfo else if cls.derivesFrom(defn.Caps_Capability) then - CapturingType(cinfo.selfType, CaptureSet.csImpliedByCapability) - // CapturingType(cinfo.selfType, CaptureSet.universal) + // If cls is a capability class, we need to add a fresh capability to + // ensure we cannot tread itself as pure. + CapturingType(cinfo.selfType, + CaptureSet.fresh(Origin.InDecl(cls)).readOnly + ++ CaptureSet.Var(cls, level = ccState.currentLevel)) else if !cls.isEffectivelySealed && !cls.baseClassHasExplicitNonUniversalSelfType then // assume {cap} for completely unconstrained self types of publicly extensible classes CapturingType(cinfo.selfType, CaptureSet.universal) diff --git a/tests/neg-custom-args/captures/i23223.scala b/tests/neg-custom-args/captures/i23223.scala new file mode 100644 index 000000000000..e606875b840a --- /dev/null +++ b/tests/neg-custom-args/captures/i23223.scala @@ -0,0 +1,12 @@ +import language.experimental.captureChecking +import caps.* + +class A: + def a: A = this + +class B extends A, Capability // error + +def leak(b: B): A = b.a + +class C extends Capability: + def c: C^{} = this // error diff --git a/tests/pos-custom-args/captures/i20237.scala b/tests/pos-custom-args/captures/i20237.scala index 973f5d2025e3..53efbb2e7583 100644 --- a/tests/pos-custom-args/captures/i20237.scala +++ b/tests/pos-custom-args/captures/i20237.scala @@ -1,8 +1,12 @@ import language.experimental.captureChecking +import caps.* class Cap extends caps.Capability: def use[T](body: Cap ?=> T) = body(using this) +class Cap2 extends caps.Capability: + def use[T](body: Cap2 => T) = body(this) + class Box[T](body: Cap ?=> T): inline def open(using cap: Cap) = cap.use(body) From da5a9025e182beb1bc20595265940c329d7a9227 Mon Sep 17 00:00:00 2001 From: noti0na1 <noti0na1@users.noreply.github.com> Date: Fri, 23 May 2025 16:26:18 +0200 Subject: [PATCH 3/4] Update compiler/src/dotty/tools/dotc/cc/Setup.scala Fix typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- compiler/src/dotty/tools/dotc/cc/Setup.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index d91e235841ec..c5125b8eab07 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -730,7 +730,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: selfInfo else if cls.derivesFrom(defn.Caps_Capability) then // If cls is a capability class, we need to add a fresh capability to - // ensure we cannot tread itself as pure. + // ensure we cannot treat itself as pure. CapturingType(cinfo.selfType, CaptureSet.fresh(Origin.InDecl(cls)).readOnly ++ CaptureSet.Var(cls, level = ccState.currentLevel)) From 8670b999e03c329cc981db3a6119301638c0b36b Mon Sep 17 00:00:00 2001 From: odersky <odersky@gmail.com> Date: Mon, 26 May 2025 19:37:21 +0200 Subject: [PATCH 4/4] Tweak so that we only add one capture set variable to self types --- compiler/src/dotty/tools/dotc/cc/Setup.scala | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index c5125b8eab07..69bfa96df836 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -728,12 +728,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: else if cls.isPureClass then // is cls is known to be pure, nothing needs to be added to self type selfInfo - else if cls.derivesFrom(defn.Caps_Capability) then - // If cls is a capability class, we need to add a fresh capability to - // ensure we cannot treat itself as pure. - CapturingType(cinfo.selfType, - CaptureSet.fresh(Origin.InDecl(cls)).readOnly - ++ CaptureSet.Var(cls, level = ccState.currentLevel)) else if !cls.isEffectivelySealed && !cls.baseClassHasExplicitNonUniversalSelfType then // assume {cap} for completely unconstrained self types of publicly extensible classes CapturingType(cinfo.selfType, CaptureSet.universal) @@ -741,7 +735,12 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: // Infer the self type for the rest, which is all classes without explicit // self types (to which we also add nested module classes), provided they are // neither pure, nor are publicily extensible with an unconstrained self type. - CapturingType(cinfo.selfType, CaptureSet.Var(cls, level = ccState.currentLevel)) + val cs = CaptureSet.Var(cls, level = ccState.currentLevel) + if cls.derivesFrom(defn.Caps_Capability) then + // If cls is a capability class, we need to add a fresh readonly capability to + // ensure we cannot treat the class as pure. + CaptureSet.fresh(Origin.InDecl(cls)).readOnly.subCaptures(cs) + CapturingType(cinfo.selfType, cs) // Compute new parent types val ps1 = inContext(ctx.withOwner(cls)):