From 4981f8d7142e9e4f0d331822214e7ef5f46353ba Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 12 Aug 2024 15:53:12 +0200 Subject: [PATCH 1/2] Use default self type more often We now also use cap as the default for the self type's capture set if a base class has an explicit self type, but that type's capture set is universal. This requires fewer self type annotations. --- compiler/src/dotty/tools/dotc/cc/CaptureOps.scala | 6 ++++-- compiler/src/dotty/tools/dotc/cc/Setup.scala | 2 +- scala2-library-cc/src/scala/collection/IndexedSeqView.scala | 5 +---- scala2-library-cc/src/scala/collection/SeqView.scala | 2 -- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index 9b7d2b90ed1a..29c6528e36de 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -528,9 +528,11 @@ extension (cls: ClassSymbol) // and err on the side of impure. && selfType.exists && selfType.captureSet.isAlwaysEmpty - def baseClassHasExplicitSelfType(using Context): Boolean = + def baseClassHasExplicitNonUniversalSelfType(using Context): Boolean = cls.baseClasses.exists: bc => - bc.is(CaptureChecked) && bc.givenSelfType.exists + bc.is(CaptureChecked) + && bc.givenSelfType.exists + && !bc.givenSelfType.captureSet.isUniversal def matchesExplicitRefsInBaseClass(refs: CaptureSet)(using Context): Boolean = cls.baseClasses.tail.exists: bc => diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 91671d7d7776..f578d10702e9 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -570,7 +570,7 @@ 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.isEffectivelySealed && !cls.baseClassHasExplicitSelfType then + else if !cls.isEffectivelySealed && !cls.baseClassHasExplicitNonUniversalSelfType then // assume {cap} for completely unconstrained self types of publicly extensible classes CapturingType(cinfo.selfType, CaptureSet.universal) else diff --git a/scala2-library-cc/src/scala/collection/IndexedSeqView.scala b/scala2-library-cc/src/scala/collection/IndexedSeqView.scala index 0b6f1bc8e64e..78f8abb8e327 100644 --- a/scala2-library-cc/src/scala/collection/IndexedSeqView.scala +++ b/scala2-library-cc/src/scala/collection/IndexedSeqView.scala @@ -16,13 +16,10 @@ package collection import scala.annotation.nowarn import language.experimental.captureChecking -trait IndexedSeqViewOps[+A, +CC[_], +C] extends Any with SeqViewOps[A, CC, C] { - self: IndexedSeqViewOps[A, CC, C]^ => -} +trait IndexedSeqViewOps[+A, +CC[_], +C] extends Any with SeqViewOps[A, CC, C] /** View defined in terms of indexing a range */ trait IndexedSeqView[+A] extends IndexedSeqViewOps[A, View, View[A]] with SeqView[A] { - self: IndexedSeqView[A]^ => override def view: IndexedSeqView[A]^{this} = this diff --git a/scala2-library-cc/src/scala/collection/SeqView.scala b/scala2-library-cc/src/scala/collection/SeqView.scala index c7af0077ce1a..292dc61ddaa8 100644 --- a/scala2-library-cc/src/scala/collection/SeqView.scala +++ b/scala2-library-cc/src/scala/collection/SeqView.scala @@ -25,7 +25,6 @@ import scala.annotation.unchecked.uncheckedCaptures * mapping a SeqView with an impure function gives an impure view). */ trait SeqViewOps[+A, +CC[_], +C] extends Any with IterableOps[A, CC, C] { - self: SeqViewOps[A, CC, C]^ => def length: Int def apply(x: Int): A @@ -75,7 +74,6 @@ trait SeqViewOps[+A, +CC[_], +C] extends Any with IterableOps[A, CC, C] { } trait SeqView[+A] extends SeqViewOps[A, View, View[A]] with View[A] { - self: SeqView[A]^ => override def view: SeqView[A]^{this} = this From 5dd0bf7bf252255d7d760c09c2402a249d304eda Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 12 Aug 2024 21:02:00 +0200 Subject: [PATCH 2/2] Expand Capability types T to T^ only if not explicit capture set is given --- .../src/dotty/tools/dotc/cc/CheckCaptures.scala | 6 +++--- compiler/src/dotty/tools/dotc/cc/Setup.scala | 16 ++++++++++++---- .../src/dotty/tools/dotc/core/Definitions.scala | 3 +++ tests/pos-custom-args/captures/cap-refine.scala | 12 ++++++++++++ 4 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 tests/pos-custom-args/captures/cap-refine.scala diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 51cf362ca667..27a3d6024b65 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -632,7 +632,7 @@ class CheckCaptures extends Recheck, SymTransformer: def addParamArgRefinements(core: Type, initCs: CaptureSet): (Type, CaptureSet) = var refined: Type = core var allCaptures: CaptureSet = - if core.derivesFromCapability then CaptureSet.universal else initCs + if core.derivesFromCapability then defn.universalCSImpliedByCapability else initCs for (getterName, argType) <- mt.paramNames.lazyZip(argTypes) do val getter = cls.info.member(getterName).suchThat(_.isRefiningParamAccessor).symbol if !getter.is(Private) && getter.hasTrackedParts then @@ -809,7 +809,7 @@ class CheckCaptures extends Recheck, SymTransformer: val localSet = capturedVars(sym) if !localSet.isAlwaysEmpty then curEnv = Env(sym, EnvKind.Regular, localSet, curEnv) - + // ctx with AssumedContains entries for each Contains parameter val bodyCtx = var ac = CaptureSet.assumedContains @@ -828,7 +828,7 @@ class CheckCaptures extends Recheck, SymTransformer: interpolateVarsIn(tree.tpt) curEnv = saved end recheckDefDef - + /** If val or def definition with inferred (result) type is visible * in other compilation units, check that the actual inferred type * conforms to the expected type where all inferred capture sets are dropped. diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index f578d10702e9..22e7899eeea1 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -299,16 +299,24 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: CapturingType(fntpe, cs, boxed = false) else fntpe + def stripImpliedCaptureSet(tp: Type): Type = tp match + case tp @ CapturingType(parent, refs) + if (refs eq defn.universalCSImpliedByCapability) && !tp.isBoxedCapturing => + parent + case tp @ CapturingType(parent, refs) => tp + case _ => tp + def apply(t: Type) = t match case t @ CapturingType(parent, refs) => - t.derivedCapturingType(this(parent), refs) + t.derivedCapturingType(stripImpliedCaptureSet(this(parent)), refs) case t @ AnnotatedType(parent, ann) => val parent1 = this(parent) if ann.symbol.isRetains then + val parent2 = stripImpliedCaptureSet(parent1) for tpt <- tptToCheck do - checkWellformedLater(parent1, ann.tree, tpt) - CapturingType(parent1, ann.tree.toCaptureSet) + checkWellformedLater(parent2, ann.tree, tpt) + CapturingType(parent2, ann.tree.toCaptureSet) else t.derivedAnnotatedType(parent1, ann) case throwsAlias(res, exc) => @@ -316,7 +324,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: case t => // Map references to capability classes C to C^ if t.derivesFromCapability && !t.isSingleton && t.typeSymbol != defn.Caps_Exists - then CapturingType(t, CaptureSet.universal, boxed = false) + then CapturingType(t, defn.universalCSImpliedByCapability, boxed = false) else normalizeCaptures(mapOver(t)) end toCapturing diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 8981aa4aa6ac..f95bb3cea351 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1005,6 +1005,9 @@ class Definitions { @tu lazy val Caps_ContainsTrait: TypeSymbol = CapsModule.requiredType("Contains") @tu lazy val Caps_containsImpl: TermSymbol = CapsModule.requiredMethod("containsImpl") + /** The same as CaptureSet.universal but generated implicitly for references of Capability subtypes */ + @tu lazy val universalCSImpliedByCapability = CaptureSet(captureRoot.termRef) + @tu lazy val PureClass: Symbol = requiredClass("scala.Pure") // Annotation base classes diff --git a/tests/pos-custom-args/captures/cap-refine.scala b/tests/pos-custom-args/captures/cap-refine.scala new file mode 100644 index 000000000000..ed0b4d018b88 --- /dev/null +++ b/tests/pos-custom-args/captures/cap-refine.scala @@ -0,0 +1,12 @@ +//> using options -Werror +import caps.Capability + +trait Buffer[T] extends Capability: + def append(x: T): this.type + +def f(buf: Buffer[Int]) = + val buf1 = buf.append(1).append(2) + val buf2: Buffer[Int]^{buf1} = buf1 + + +