From 0d42da527da69ca432fd1614376d8a965f13b2ae Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Mon, 10 Feb 2025 11:33:06 +0100 Subject: [PATCH] Revert "Exclusive capabilities revised" --- .../tools/backend/jvm/BTypesFromSymbols.scala | 2 +- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 - .../src/dotty/tools/dotc/ast/TreeInfo.scala | 2 +- compiler/src/dotty/tools/dotc/ast/untpd.scala | 3 - .../src/dotty/tools/dotc/cc/CaptureOps.scala | 232 ++--- .../src/dotty/tools/dotc/cc/CaptureRef.scala | 183 +--- .../src/dotty/tools/dotc/cc/CaptureSet.scala | 269 ++---- .../dotty/tools/dotc/cc/CheckCaptures.scala | 297 +++--- .../src/dotty/tools/dotc/cc/Existential.scala | 24 +- compiler/src/dotty/tools/dotc/cc/Fresh.scala | 142 --- .../src/dotty/tools/dotc/cc/SepCheck.scala | 849 ------------------ compiler/src/dotty/tools/dotc/cc/Setup.scala | 122 +-- .../src/dotty/tools/dotc/cc/Synthetics.scala | 10 +- .../dotty/tools/dotc/core/Definitions.scala | 21 +- .../src/dotty/tools/dotc/core/Flags.scala | 1 + .../src/dotty/tools/dotc/core/StdNames.scala | 3 - .../tools/dotc/core/SymDenotations.scala | 7 - .../src/dotty/tools/dotc/core/SymUtils.scala | 2 +- .../dotty/tools/dotc/core/TypeComparer.scala | 46 +- .../src/dotty/tools/dotc/core/TypeOps.scala | 4 +- .../src/dotty/tools/dotc/core/Types.scala | 35 +- .../dotty/tools/dotc/parsing/Parsers.scala | 44 +- .../dotty/tools/dotc/parsing/Scanners.scala | 5 +- .../tools/dotc/printing/Formatting.scala | 5 +- .../tools/dotc/printing/PlainPrinter.scala | 81 +- .../tools/dotc/printing/RefinedPrinter.scala | 4 +- .../dotty/tools/dotc/reporting/messages.scala | 2 +- .../src/dotty/tools/dotc/sbt/ExtractAPI.scala | 2 +- .../tools/dotc/transform/CapturedVars.scala | 2 +- .../tools/dotc/transform/CheckReentrant.scala | 2 +- .../tools/dotc/transform/CheckStatic.scala | 2 +- .../tools/dotc/transform/Constructors.scala | 2 +- .../dotty/tools/dotc/transform/LazyVals.scala | 4 +- .../tools/dotc/transform/MoveStatics.scala | 2 +- .../dotty/tools/dotc/transform/Recheck.scala | 6 +- .../dotc/transform/UninitializedDefs.scala | 2 +- .../tools/dotc/transform/init/Objects.scala | 2 +- .../tools/dotc/transform/init/Util.scala | 2 +- .../src/dotty/tools/dotc/typer/Checking.scala | 10 +- .../tools/dotc/typer/ErrorReporting.scala | 2 +- .../dotty/tools/dotc/typer/Nullables.scala | 6 +- .../tools/dotc/typer/QuotesAndSplices.scala | 2 +- .../dotty/tools/dotc/typer/RefChecks.scala | 9 +- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- .../tools/dotc/typer/VarianceChecker.scala | 2 +- .../tools/dotc/util/SimpleIdentitySet.scala | 5 - .../_docs/internals/exclusive-capabilities.md | 551 ------------ .../annotation/internal/freshCapability.scala | 10 - .../internal/readOnlyCapability.scala | 7 - library/src/scala/caps.scala | 38 +- project/MiMaFilters.scala | 2 - .../src/scala/collection/IterableOnce.scala | 2 +- .../src/scala/collection/View.scala | 11 +- .../immutable/LazyListIterable.scala | 14 +- .../mutable/CheckedIndexedSeqView.scala | 29 +- .../neg-custom-args/captures/bad-uses-2.scala | 10 +- .../captures/box-adapt-cases.check | 15 +- .../captures/box-adapt-cases.scala | 3 +- .../captures/box-adapt-contra.scala | 5 +- tests/neg-custom-args/captures/byname.check | 4 +- .../captures/capt-depfun.check | 12 - .../captures/capt-depfun.scala | 4 +- tests/neg-custom-args/captures/capt1.check | 68 +- tests/neg-custom-args/captures/capt1.scala | 12 +- .../captures/caseclass/Test_2.scala | 2 +- .../captures/cc-ex-conformance.scala | 3 +- .../captures/cc-subst-param-exact.scala | 6 +- tests/neg-custom-args/captures/cc-this5.check | 2 +- .../captures/consume-overrides.scala | 15 - .../captures/delayedRunops.check | 16 +- .../captures/delayedRunops.scala | 7 +- .../captures/depfun-reach.check | 9 +- .../captures/depfun-reach.scala | 2 +- .../captures/effect-swaps-explicit.scala | 2 +- .../captures/effect-swaps.check | 24 +- .../captures/effect-swaps.scala | 4 +- .../captures/existential-mapping.check | 24 +- .../captures/explain-under-approx.check | 14 + .../captures/extending-cap-classes.check | 6 +- .../captures/filevar-expanded.check | 19 - tests/neg-custom-args/captures/filevar.check | 9 - tests/neg-custom-args/captures/i15772.check | 28 +- tests/neg-custom-args/captures/i15772.scala | 4 +- .../captures/i19330-alt2.scala | 2 +- tests/neg-custom-args/captures/i19330.check | 12 - tests/neg-custom-args/captures/i19330.scala | 6 +- tests/neg-custom-args/captures/i21442.check | 19 +- tests/neg-custom-args/captures/i21442.scala | 3 +- tests/neg-custom-args/captures/i21614.check | 10 +- tests/neg-custom-args/captures/i22005.scala | 3 +- .../captures/lazylists-exceptions.check | 2 +- tests/neg-custom-args/captures/lazyref.check | 31 +- tests/neg-custom-args/captures/lazyref.scala | 3 +- .../captures/linear-buffer-2.check | 29 - .../captures/linear-buffer-2.scala | 42 - .../captures/linear-buffer.check | 44 - .../captures/linear-buffer.scala | 48 - .../captures/mut-outside-mutable.check | 8 - .../captures/mut-outside-mutable.scala | 10 - .../captures/mut-override.scala | 19 - .../captures/non-local-consume.scala | 29 - .../neg-custom-args/captures/outer-var.check | 22 +- .../neg-custom-args/captures/outer-var.scala | 1 - .../captures/path-patmat-should-be-pos.scala | 4 +- tests/neg-custom-args/captures/reaches.check | 88 +- tests/neg-custom-args/captures/reaches.scala | 13 +- tests/neg-custom-args/captures/reaches2.check | 35 +- tests/neg-custom-args/captures/reaches2.scala | 4 +- tests/neg-custom-args/captures/readOnly.check | 19 - tests/neg-custom-args/captures/readOnly.scala | 22 - tests/neg-custom-args/captures/real-try.check | 10 +- .../captures/sep-compose.check | 120 --- .../captures/sep-compose.scala | 45 - tests/neg-custom-args/captures/sep-use.check | 36 - tests/neg-custom-args/captures/sep-use.scala | 27 - tests/neg-custom-args/captures/sep-use2.scala | 29 - .../neg-custom-args/captures/sepchecks.scala | 62 -- .../neg-custom-args/captures/sepchecks2.check | 52 -- .../neg-custom-args/captures/sepchecks2.scala | 35 - .../neg-custom-args/captures/sepchecks3.scala | 12 - .../neg-custom-args/captures/sepchecks4.check | 25 - .../neg-custom-args/captures/sepchecks4.scala | 16 - .../neg-custom-args/captures/sepchecks5.check | 10 - .../neg-custom-args/captures/sepchecks5.scala | 21 - .../captures/shared-capability.check | 4 - .../captures/shared-capability.scala | 10 - .../captures/unsound-reach-2.scala | 4 +- .../captures/unsound-reach-3.scala | 9 +- .../captures/unsound-reach-4.check | 22 +- .../captures/unsound-reach-4.scala | 9 +- .../captures/unsound-reach-6.check | 13 - .../captures/unsound-reach-6.scala | 9 +- .../captures/unsound-reach.check | 7 - .../captures/unsound-reach.scala | 4 +- .../captures/update-call.scala | 19 - tests/neg-custom-args/captures/vars.check | 7 +- .../captures/boxmap-paper.scala | 5 +- tests/pos-custom-args/captures/capt1.scala | 12 +- .../captures/cc-dep-param.scala | 3 +- tests/pos-custom-args/captures/cc-this.scala | 5 +- .../captures/eta-expansions.scala | 2 +- .../captures/filevar-expanded.scala | 2 +- .../captures/filter-iterable.scala | 11 - tests/pos-custom-args/captures/foreach2.scala | 7 - tests/pos-custom-args/captures/i15749a.scala | 3 +- tests/pos-custom-args/captures/lazyref.scala | 5 +- tests/pos-custom-args/captures/mutRef.scala | 5 - .../captures/nested-classes-2.scala | 18 +- tests/pos-custom-args/captures/reaches.scala | 7 +- .../captures/sep-compose.scala | 21 - tests/pos-custom-args/captures/sep-eq.scala | 20 - .../captures/simple-apply.scala | 6 - tests/pos-custom-args/captures/skolems2.scala | 19 - tests/pos-special/stdlib/Test2.scala | 9 +- .../colltest5/CollectionStrawManCC5_1.scala | 39 +- .../captures/colltest5/Test_2.scala | 5 +- 156 files changed, 744 insertions(+), 4061 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/cc/Fresh.scala delete mode 100644 compiler/src/dotty/tools/dotc/cc/SepCheck.scala delete mode 100644 docs/_docs/internals/exclusive-capabilities.md delete mode 100644 library/src/scala/annotation/internal/freshCapability.scala delete mode 100644 library/src/scala/annotation/internal/readOnlyCapability.scala delete mode 100644 tests/neg-custom-args/captures/capt-depfun.check delete mode 100644 tests/neg-custom-args/captures/consume-overrides.scala create mode 100644 tests/neg-custom-args/captures/explain-under-approx.check delete mode 100644 tests/neg-custom-args/captures/filevar-expanded.check delete mode 100644 tests/neg-custom-args/captures/filevar.check delete mode 100644 tests/neg-custom-args/captures/linear-buffer-2.check delete mode 100644 tests/neg-custom-args/captures/linear-buffer-2.scala delete mode 100644 tests/neg-custom-args/captures/linear-buffer.check delete mode 100644 tests/neg-custom-args/captures/linear-buffer.scala delete mode 100644 tests/neg-custom-args/captures/mut-outside-mutable.check delete mode 100644 tests/neg-custom-args/captures/mut-outside-mutable.scala delete mode 100644 tests/neg-custom-args/captures/mut-override.scala delete mode 100644 tests/neg-custom-args/captures/non-local-consume.scala delete mode 100644 tests/neg-custom-args/captures/readOnly.check delete mode 100644 tests/neg-custom-args/captures/readOnly.scala delete mode 100644 tests/neg-custom-args/captures/sep-compose.check delete mode 100644 tests/neg-custom-args/captures/sep-compose.scala delete mode 100644 tests/neg-custom-args/captures/sep-use.check delete mode 100644 tests/neg-custom-args/captures/sep-use.scala delete mode 100644 tests/neg-custom-args/captures/sep-use2.scala delete mode 100644 tests/neg-custom-args/captures/sepchecks.scala delete mode 100644 tests/neg-custom-args/captures/sepchecks2.check delete mode 100644 tests/neg-custom-args/captures/sepchecks2.scala delete mode 100644 tests/neg-custom-args/captures/sepchecks3.scala delete mode 100644 tests/neg-custom-args/captures/sepchecks4.check delete mode 100644 tests/neg-custom-args/captures/sepchecks4.scala delete mode 100644 tests/neg-custom-args/captures/sepchecks5.check delete mode 100644 tests/neg-custom-args/captures/sepchecks5.scala delete mode 100644 tests/neg-custom-args/captures/shared-capability.check delete mode 100644 tests/neg-custom-args/captures/shared-capability.scala delete mode 100644 tests/neg-custom-args/captures/unsound-reach-6.check delete mode 100644 tests/neg-custom-args/captures/update-call.scala rename tests/{neg-custom-args => pos-custom-args}/captures/filevar-expanded.scala (94%) delete mode 100644 tests/pos-custom-args/captures/filter-iterable.scala delete mode 100644 tests/pos-custom-args/captures/foreach2.scala delete mode 100644 tests/pos-custom-args/captures/mutRef.scala delete mode 100644 tests/pos-custom-args/captures/sep-compose.scala delete mode 100644 tests/pos-custom-args/captures/sep-eq.scala delete mode 100644 tests/pos-custom-args/captures/simple-apply.scala delete mode 100644 tests/pos-custom-args/captures/skolems2.scala diff --git a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala index 817d0be54d26..97934935f352 100644 --- a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala +++ b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala @@ -285,7 +285,7 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I, val frontendAcce // tests/run/serialize.scala and https://github.com/typelevel/cats-effect/pull/2360). val privateFlag = !sym.isClass && (sym.is(Private) || (sym.isPrimaryConstructor && sym.owner.isTopLevelModuleClass)) - val finalFlag = sym.is(Final) && !toDenot(sym).isClassConstructor && !sym.isMutableVar && !sym.enclosingClass.is(Trait) + val finalFlag = sym.is(Final) && !toDenot(sym).isClassConstructor && !sym.is(Mutable, butNot = Accessor) && !sym.enclosingClass.is(Trait) import asm.Opcodes.* import GenBCodeOps.addFlagIf diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 15f41abb1cd2..ec65224ac93d 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -2243,8 +2243,6 @@ object desugar { New(ref(defn.RepeatedAnnot.typeRef), Nil :: Nil)) else if op.name == nme.CC_REACH then Apply(ref(defn.Caps_reachCapability), t :: Nil) - else if op.name == nme.CC_READONLY then - Apply(ref(defn.Caps_readOnlyCapability), t :: Nil) else assert(ctx.mode.isExpr || ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive), ctx.mode) Select(t, op.name) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 45e17794ec96..32ab8378ae16 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -759,7 +759,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => */ def isVariableOrGetter(tree: Tree)(using Context): Boolean = { def sym = tree.symbol - def isVar = sym.isMutableVarOrAccessor + def isVar = sym.is(Mutable) def isGetter = mayBeVarGetter(sym) && sym.owner.info.member(sym.name.asTermName.setterName).exists diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index e89dc2c1cdb5..2acfc4cf86e3 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -206,8 +206,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class Var()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Mutable) - case class Mut()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Mutable) - case class Implicit()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Implicit) case class Given()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Given) @@ -334,7 +332,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def isEnumCase: Boolean = isEnum && is(Case) def isEnumClass: Boolean = isEnum && !is(Case) - def isMutableVar: Boolean = is(Mutable) && mods.exists(_.isInstanceOf[Mod.Var]) } @sharable val EmptyModifiers: Modifiers = Modifiers() diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index 349711ef21b0..92cd40a65d5a 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -16,14 +16,9 @@ import config.Feature import collection.mutable import CCState.* import reporting.Message -import CaptureSet.VarState -/** Attachment key for capturing type trees */ private val Captures: Key[CaptureSet] = Key() -/** Context property to print Fresh.Cap as "fresh" instead of "cap" */ -val PrintFresh: Key[Unit] = Key() - object ccConfig: /** If true, allow mapping capture set variables under captureChecking with maps that are neither @@ -52,10 +47,6 @@ object ccConfig: def useSealed(using Context) = Feature.sourceVersion.stable != SourceVersion.`3.5` - /** If true, turn on separation checking */ - def useSepChecks(using Context): Boolean = - Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.7`) - end ccConfig /** Are we at checkCaptures phase? */ @@ -145,8 +136,6 @@ extension (tree: Tree) def toCaptureRefs(using Context): List[CaptureRef] = tree match case ReachCapabilityApply(arg) => arg.toCaptureRefs.map(_.reach) - case ReadOnlyCapabilityApply(arg) => - arg.toCaptureRefs.map(_.readOnly) case CapsOfApply(arg) => arg.toCaptureRefs case _ => tree.tpe.dealiasKeepAnnots match @@ -195,14 +184,16 @@ extension (tp: Type) case tp: TermRef => ((tp.prefix eq NoPrefix) || tp.symbol.isField && !tp.symbol.isStatic && tp.prefix.isTrackableRef - || tp.isCap + || tp.isRootCapability ) && !tp.symbol.isOneOf(UnstableValueFlags) case tp: TypeRef => tp.symbol.isType && tp.derivesFrom(defn.Caps_CapSet) case tp: TypeParamRef => tp.derivesFrom(defn.Caps_CapSet) case AnnotatedType(parent, annot) => - defn.capabilityWrapperAnnots.contains(annot.symbol) && parent.isTrackableRef + (annot.symbol == defn.ReachCapabilityAnnot + || annot.symbol == defn.MaybeCapabilityAnnot + ) && parent.isTrackableRef case _ => false @@ -231,10 +222,6 @@ extension (tp: Type) else tp match case tp @ ReachCapability(_) => tp.singletonCaptureSet - case ReadOnlyCapability(ref) => - val refDcs = ref.deepCaptureSet(includeTypevars) - if refDcs.isConst then CaptureSet(refDcs.elems.map(_.readOnly)) - else refDcs // this case should not happen for correct programs case tp: SingletonCaptureRef if tp.isTrackableRef => tp.reach.singletonCaptureSet case _ => @@ -252,7 +239,7 @@ extension (tp: Type) * the two capture sets are combined. */ def capturing(cs: CaptureSet)(using Context): Type = - if (cs.isAlwaysEmpty || cs.isConst && cs.subCaptures(tp.captureSet, VarState.Separate).isOK) + if (cs.isAlwaysEmpty || cs.isConst && cs.subCaptures(tp.captureSet, frozen = true).isOK) && !cs.keepAlways then tp else tp match @@ -281,24 +268,11 @@ extension (tp: Type) case _ => tp - /** The first element of this path type. Note that class parameter references - * are of the form this.C but their pathroot is still this.C, not this. - */ + /** The first element of this path type */ final def pathRoot(using Context): Type = tp.dealias match - case tp1: TermRef if tp1.symbol.maybeOwner.isClass => tp1.prefix.pathRoot - case tp1: TypeRef if !tp1.symbol.is(Param) => tp1.prefix.pathRoot + case tp1: NamedType if tp1.symbol.owner.isClass => tp1.prefix.pathRoot case tp1 => tp1 - /** The first element of a path type, but stop at references extending - * SharedCapability. - */ - final def pathRootOrShared(using Context): Type = - if tp.derivesFromSharedCapability then tp - else tp.dealias match - case tp1: TermRef if tp1.symbol.maybeOwner.isClass => tp1.prefix.pathRoot - case tp1: TypeRef if !tp1.symbol.is(Param) => tp1.prefix.pathRoot - case tp1 => tp1 - /** If this part starts with `C.this`, the class `C`. * Otherwise, if it starts with a reference `r`, `r`'s owner. * Otherwise NoSymbol. @@ -371,8 +345,7 @@ extension (tp: Type) def forceBoxStatus(boxed: Boolean)(using Context): Type = tp.widenDealias match case tp @ CapturingType(parent, refs) if tp.isBoxed != boxed => val refs1 = tp match - case ref: CaptureRef if ref.isTracked || ref.isReach || ref.isReadOnly => - ref.singletonCaptureSet + case ref: CaptureRef if ref.isTracked || ref.isReach => ref.singletonCaptureSet case _ => refs CapturingType(parent, refs1, boxed) case _ => @@ -406,33 +379,23 @@ extension (tp: Type) case _ => false - /** Is this a type extending `Mutable` that has update methods? */ - def isMutableType(using Context): Boolean = - tp.derivesFrom(defn.Caps_Mutable) - && tp.membersBasedOnFlags(Mutable | Method, EmptyFlags) - .exists(_.hasAltWith(_.symbol.isUpdateMethod)) - /** Tests whether the type derives from `caps.Capability`, which means * references of this type are maximal capabilities. */ - def derivesFromCapTrait(cls: ClassSymbol)(using Context): Boolean = tp.dealias match + def derivesFromCapability(using Context): Boolean = tp.dealias match case tp: (TypeRef | AppliedType) => val sym = tp.typeSymbol - if sym.isClass then sym.derivesFrom(cls) - else tp.superType.derivesFromCapTrait(cls) + if sym.isClass then sym.derivesFrom(defn.Caps_Capability) + else tp.superType.derivesFromCapability case tp: (TypeProxy & ValueType) => - tp.superType.derivesFromCapTrait(cls) + tp.superType.derivesFromCapability case tp: AndType => - tp.tp1.derivesFromCapTrait(cls) || tp.tp2.derivesFromCapTrait(cls) + tp.tp1.derivesFromCapability || tp.tp2.derivesFromCapability case tp: OrType => - tp.tp1.derivesFromCapTrait(cls) && tp.tp2.derivesFromCapTrait(cls) + tp.tp1.derivesFromCapability && tp.tp2.derivesFromCapability case _ => false - def derivesFromCapability(using Context): Boolean = derivesFromCapTrait(defn.Caps_Capability) - def derivesFromMutable(using Context): Boolean = derivesFromCapTrait(defn.Caps_Mutable) - def derivesFromSharedCapability(using Context): Boolean = derivesFromCapTrait(defn.Caps_SharedCapability) - /** Drop @retains annotations everywhere */ def dropAllRetains(using Context): Type = // TODO we should drop retains from inferred types before unpickling val tm = new TypeMap: @@ -443,6 +406,17 @@ extension (tp: Type) mapOver(t) tm(tp) + /** If `x` is a capture ref, its reach capability `x*`, represented internally + * as `x @reachCapability`. `x*` stands for all capabilities reachable through `x`". + * We have `{x} <: {x*} <: dcs(x)}` where the deep capture set `dcs(x)` of `x` + * is the union of all capture sets that appear in covariant position in the + * type of `x`. If `x` and `y` are different variables then `{x*}` and `{y*}` + * are unrelated. + */ + def reach(using Context): CaptureRef = tp match + case tp: CaptureRef if tp.isTrackableRef => + if tp.isReach then tp else ReachCapability(tp) + /** If `x` is a capture ref, its maybe capability `x?`, represented internally * as `x @maybeCapability`. `x?` stands for a capability `x` that might or might * not be part of a capture set. We have `{} <: {x?} <: {x}`. Maybe capabilities @@ -462,54 +436,42 @@ extension (tp: Type) * but it has fewer issues with type inference. */ def maybe(using Context): CaptureRef = tp match - case tp @ AnnotatedType(_, annot) if annot.symbol == defn.MaybeCapabilityAnnot => tp - case _ => MaybeCapability(tp) + case tp: CaptureRef if tp.isTrackableRef => + if tp.isMaybe then tp else MaybeCapability(tp) - /** If `x` is a capture ref, its reach capability `x*`, represented internally - * as `x @reachCapability`. `x*` stands for all capabilities reachable through `x`". - * We have `{x} <: {x*} <: dcs(x)}` where the deep capture set `dcs(x)` of `x` - * is the union of all capture sets that appear in covariant position in the - * type of `x`. If `x` and `y` are different variables then `{x*}` and `{y*}` - * are unrelated. + /** If `ref` is a trackable capture ref, and `tp` has only covariant occurrences of a + * universal capture set, replace all these occurrences by `{ref*}`. This implements + * the new aspect of the (Var) rule, which can now be stated as follows: * - * Reach capabilities cannot wrap read-only capabilities or maybe capabilities. - * We have - * (x.rd).reach = x*.rd - * (x.rd)? = (x*)? - */ - def reach(using Context): CaptureRef = tp match - case tp @ AnnotatedType(tp1: CaptureRef, annot) - if annot.symbol == defn.MaybeCapabilityAnnot => - tp1.reach.maybe - case tp @ AnnotatedType(tp1: CaptureRef, annot) - if annot.symbol == defn.ReadOnlyCapabilityAnnot => - tp1.reach.readOnly - case tp @ AnnotatedType(tp1: CaptureRef, annot) - if annot.symbol == defn.ReachCapabilityAnnot => - tp - case _ => - ReachCapability(tp) - - /** If `x` is a capture ref, its read-only capability `x.rd`, represented internally - * as `x @readOnlyCapability`. We have {x.rd} <: {x}. If `x` is a reach capability `y*`, - * then its read-only version is `x.rd*`. + * x: T in E + * ----------- + * E |- x: T' * - * Read-only capabilities cannot wrap maybe capabilities - * but they can wrap reach capabilities. We have - * (x?).readOnly = (x.rd)? - */ - def readOnly(using Context): CaptureRef = tp match - case tp @ AnnotatedType(tp1: CaptureRef, annot) - if annot.symbol == defn.MaybeCapabilityAnnot => - tp1.readOnly.maybe - case tp @ AnnotatedType(tp1: CaptureRef, annot) - if annot.symbol == defn.ReadOnlyCapabilityAnnot => - tp - case _ => - ReadOnlyCapability(tp) - - /** If `x` is a capture ref, replace all no-flip covariant occurrences of `cap` - * in type `tp` with `x*`. + * where T' is T with (1) the toplevel capture set replaced by `{x}` and + * (2) all covariant occurrences of cap replaced by `x*`, provided there + * are no occurrences in `T` at other variances. (1) is standard, whereas + * (2) is new. + * + * For (2), multiple-flipped covariant occurrences of cap won't be replaced. + * In other words, + * + * - For xs: List[File^] ==> List[File^{xs*}], the cap is replaced; + * - while f: [R] -> (op: File^ => R) -> R remains unchanged. + * + * Without this restriction, the signature of functions like withFile: + * + * (path: String) -> [R] -> (op: File^ => R) -> R + * + * could be refined to + * + * (path: String) -> [R] -> (op: File^{withFile*} => R) -> R + * + * which is clearly unsound. + * + * Why is this sound? Covariant occurrences of cap must represent capabilities + * that are reachable from `x`, so they are included in the meaning of `{x*}`. + * At the same time, encapsulation is still maintained since no covariant + * occurrences of cap are allowed in instance types of type variables. */ def withReachCaptures(ref: Type)(using Context): Type = object narrowCaps extends TypeMap: @@ -517,10 +479,9 @@ extension (tp: Type) def apply(t: Type) = if variance <= 0 then t else t.dealiasKeepAnnots match - case t @ CapturingType(p, cs) if cs.containsRootCapability => + case t @ CapturingType(p, cs) if cs.isUniversal => change = true - val reachRef = if cs.isReadOnly then ref.reach.readOnly else ref.reach - t.derivedCapturingType(apply(p), reachRef.singletonCaptureSet) + t.derivedCapturingType(apply(p), ref.reach.singletonCaptureSet) case t @ AnnotatedType(parent, ann) => // Don't map annotations, which includes capture sets t.derivedAnnotatedType(this(parent), ann) @@ -545,24 +506,6 @@ extension (tp: Type) tp case _ => tp - end withReachCaptures - - /** Does this type contain no-flip covariant occurrences of `cap`? */ - def containsCap(using Context): Boolean = - val acc = new TypeAccumulator[Boolean]: - def apply(x: Boolean, t: Type) = - x - || variance > 0 && t.dealiasKeepAnnots.match - case t @ CapturingType(p, cs) if cs.containsCap => - true - case t @ AnnotatedType(parent, ann) => - // Don't traverse annotations, which includes capture sets - this(x, parent) - case Existential(_, _) => - false - case _ => - foldOver(x, t) - acc(false, tp) def level(using Context): Level = tp match @@ -672,16 +615,6 @@ extension (sym: Symbol) case c: TypeRef => c.symbol == sym case _ => false - def isUpdateMethod(using Context): Boolean = - sym.isAllOf(Mutable | Method, butNot = Accessor) - - def isReadOnlyMethod(using Context): Boolean = - sym.is(Method, butNot = Mutable | Accessor) && sym.owner.derivesFrom(defn.Caps_Mutable) - - def isInReadOnlyMethod(using Context): Boolean = - if sym.is(Method) && sym.owner.isClass then isReadOnlyMethod - else sym.owner.isInReadOnlyMethod - extension (tp: AnnotatedType) /** Is this a boxed capturing type? */ def isBoxed(using Context): Boolean = tp.annot match @@ -717,14 +650,6 @@ object ReachCapabilityApply: case Apply(reach, arg :: Nil) if reach.symbol == defn.Caps_reachCapability => Some(arg) case _ => None -/** An extractor for `caps.readOnlyCapability(ref)`, which is used to express a read-only - * capability as a tree in a @retains annotation. - */ -object ReadOnlyCapabilityApply: - def unapply(tree: Apply)(using Context): Option[Tree] = tree match - case Apply(ro, arg :: Nil) if ro.symbol == defn.Caps_readOnlyCapability => Some(arg) - case _ => None - /** An extractor for `caps.capsOf[X]`, which is used to express a generic capture set * as a tree in a @retains annotation. */ @@ -733,41 +658,22 @@ object CapsOfApply: case TypeApply(capsOf, arg :: Nil) if capsOf.symbol == defn.Caps_capsOf => Some(arg) case _ => None -abstract class AnnotatedCapability(annotCls: Context ?=> ClassSymbol): - def apply(tp: Type)(using Context): AnnotatedType = - assert(tp.isTrackableRef) - tp match - case AnnotatedType(_, annot) => - assert(!unwrappable.contains(annot.symbol), i"illegal combination of derived capabilities: $annotCls over ${annot.symbol}") - case _ => - tp match - case tp: CaptureRef => tp.derivedRef(annotCls) - case _ => AnnotatedType(tp, Annotation(annotCls, util.Spans.NoSpan)) - +class AnnotatedCapability(annot: Context ?=> ClassSymbol): + def apply(tp: Type)(using Context) = + AnnotatedType(tp, Annotation(annot, util.Spans.NoSpan)) def unapply(tree: AnnotatedType)(using Context): Option[CaptureRef] = tree match - case AnnotatedType(parent: CaptureRef, ann) if ann.hasSymbol(annotCls) => Some(parent) + case AnnotatedType(parent: CaptureRef, ann) if ann.symbol == annot => Some(parent) case _ => None - protected def unwrappable(using Context): Set[Symbol] -end AnnotatedCapability +/** An extractor for `ref @annotation.internal.reachCapability`, which is used to express + * the reach capability `ref*` as a type. + */ +object ReachCapability extends AnnotatedCapability(defn.ReachCapabilityAnnot) /** An extractor for `ref @maybeCapability`, which is used to express * the maybe capability `ref?` as a type. */ -object MaybeCapability extends AnnotatedCapability(defn.MaybeCapabilityAnnot): - protected def unwrappable(using Context) = Set() - -/** An extractor for `ref @readOnlyCapability`, which is used to express - * the read-only capability `ref.rd` as a type. - */ -object ReadOnlyCapability extends AnnotatedCapability(defn.ReadOnlyCapabilityAnnot): - protected def unwrappable(using Context) = Set(defn.MaybeCapabilityAnnot) - -/** An extractor for `ref @annotation.internal.reachCapability`, which is used to express - * the reach capability `ref*` as a type. - */ -object ReachCapability extends AnnotatedCapability(defn.ReachCapabilityAnnot): - protected def unwrappable(using Context) = Set(defn.MaybeCapabilityAnnot, defn.ReadOnlyCapabilityAnnot) +object MaybeCapability extends AnnotatedCapability(defn.MaybeCapabilityAnnot) /** Offers utility method to be used for type maps that follow aliases */ trait ConservativeFollowAliasMap(using Context) extends TypeMap: diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala b/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala index a2ceb1f20372..2caba4cf7d89 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala @@ -13,30 +13,14 @@ import CCState.* import Periods.NoRunId import compiletime.uninitialized import StdNames.nme -import CaptureSet.VarState -import Annotations.Annotation /** A trait for references in CaptureSets. These can be NamedTypes, ThisTypes or ParamRefs, - * as well as three kinds of AnnotatedTypes representing readOnly, reach, and maybe capabilities. - * If there are several annotations they come with an order: - * `*` first, `.rd` next, `?` last. + * as well as two kinds of AnnotatedTypes representing reach and maybe capabilities. */ trait CaptureRef extends TypeProxy, ValueType: private var myCaptureSet: CaptureSet | Null = uninitialized private var myCaptureSetRunId: Int = NoRunId private var mySingletonCaptureSet: CaptureSet.Const | Null = null - private var myDerivedRefs: List[AnnotatedType] = Nil - - /** A derived reach, readOnly or maybe reference. Derived references are cached. */ - def derivedRef(annotCls: ClassSymbol)(using Context): AnnotatedType = - def recur(refs: List[AnnotatedType]): AnnotatedType = refs match - case ref :: refs1 => - if ref.annot.symbol == annotCls then ref else recur(refs1) - case Nil => - val derived = AnnotatedType(this, Annotation(annotCls, util.Spans.NoSpan)) - myDerivedRefs = derived :: myDerivedRefs - derived - recur(myDerivedRefs) /** Is the reference tracked? This is true if it can be tracked and the capture * set of the underlying type is not always empty. @@ -44,85 +28,40 @@ trait CaptureRef extends TypeProxy, ValueType: final def isTracked(using Context): Boolean = this.isTrackableRef && (isMaxCapability || !captureSetOfInfo.isAlwaysEmpty) - /** Is this a maybe reference of the form `x?`? */ - final def isMaybe(using Context): Boolean = this ne stripMaybe - - /** Is this a read-only reference of the form `x.rd` or a capture set variable - * with only read-ony references in its upper bound? - */ - final def isReadOnly(using Context): Boolean = this match - case tp: TypeRef => tp.captureSetOfInfo.isReadOnly - case _ => this ne stripReadOnly - /** Is this a reach reference of the form `x*`? */ - final def isReach(using Context): Boolean = this ne stripReach + final def isReach(using Context): Boolean = this match + case AnnotatedType(_, annot) => annot.symbol == defn.ReachCapabilityAnnot + case _ => false - final def stripMaybe(using Context): CaptureRef = this match - case AnnotatedType(tp1: CaptureRef, annot) if annot.symbol == defn.MaybeCapabilityAnnot => - tp1 - case _ => - this + /** Is this a maybe reference of the form `x?`? */ + final def isMaybe(using Context): Boolean = this match + case AnnotatedType(_, annot) => annot.symbol == defn.MaybeCapabilityAnnot + case _ => false - final def stripReadOnly(using Context): CaptureRef = this match - case tp @ AnnotatedType(tp1: CaptureRef, annot) => - val sym = annot.symbol - if sym == defn.ReadOnlyCapabilityAnnot then - tp1 - else if sym == defn.MaybeCapabilityAnnot then - tp.derivedAnnotatedType(tp1.stripReadOnly, annot) - else - this - case _ => - this + final def stripReach(using Context): CaptureRef = + if isReach then + val AnnotatedType(parent: CaptureRef, _) = this: @unchecked + parent + else this - final def stripReach(using Context): CaptureRef = this match - case tp @ AnnotatedType(tp1: CaptureRef, annot) => - val sym = annot.symbol - if sym == defn.ReachCapabilityAnnot then - tp1 - else if sym == defn.ReadOnlyCapabilityAnnot || sym == defn.MaybeCapabilityAnnot then - tp.derivedAnnotatedType(tp1.stripReach, annot) - else - this - case _ => - this + final def stripMaybe(using Context): CaptureRef = + if isMaybe then + val AnnotatedType(parent: CaptureRef, _) = this: @unchecked + parent + else this /** Is this reference the generic root capability `cap` ? */ - final def isCap(using Context): Boolean = this match + final def isRootCapability(using Context): Boolean = this match case tp: TermRef => tp.name == nme.CAPTURE_ROOT && tp.symbol == defn.captureRoot case _ => false - /** Is this reference a Fresh.Cap instance? */ - final def isFresh(using Context): Boolean = this match - case Fresh.Cap(_) => true - case _ => false - - /** Is this reference the generic root capability `cap` or a Fresh.Cap instance? */ - final def isCapOrFresh(using Context): Boolean = isCap || isFresh - - /** Is this reference one of the generic root capabilities `cap` or `cap.rd` ? */ - final def isRootCapability(using Context): Boolean = this match - case ReadOnlyCapability(tp1) => tp1.isCapOrFresh - case _ => isCapOrFresh - - /** Is this reference a capability that does not derive from another capability? - * Includes read-only versions of maximal capabilities. - */ + /** Is this reference capability that does not derive from another capability ? */ final def isMaxCapability(using Context): Boolean = this match - case tp: TermRef => tp.isCap || tp.info.derivesFrom(defn.Caps_Exists) + case tp: TermRef => tp.isRootCapability || tp.info.derivesFrom(defn.Caps_Exists) case tp: TermParamRef => tp.underlying.derivesFrom(defn.Caps_Exists) - case Fresh.Cap(_) => true - case ReadOnlyCapability(tp1) => tp1.isMaxCapability case _ => false - /** An exclusive capability is a capability that derives - * indirectly from a maximal capability without going through - * a read-only capability first. - */ - final def isExclusive(using Context): Boolean = - !isReadOnly && (isMaxCapability || captureSetOfInfo.isExclusive) - - // With the support of paths, we don't need to normalize the `TermRef`s anymore. + // With the support of pathes, we don't need to normalize the `TermRef`s anymore. // /** Normalize reference so that it can be compared with `eq` for equality */ // final def normalizedRef(using Context): CaptureRef = this match // case tp @ AnnotatedType(parent: CaptureRef, annot) if tp.isTrackableRef => @@ -165,35 +104,35 @@ trait CaptureRef extends TypeProxy, ValueType: * X: CapSet^c1...CapSet^c2, (CapSet^c1) subsumes y ==> X subsumes y * Y: CapSet^c1...CapSet^c2, x subsumes (CapSet^c2) ==> x subsumes Y * Contains[X, y] ==> X subsumes y + * + * TODO: Document cases with more comments. */ - final def subsumes(y: CaptureRef)(using ctx: Context, vs: VarState = VarState.Separate): Boolean = - + final def subsumes(y: CaptureRef)(using Context): Boolean = def subsumingRefs(x: Type, y: Type): Boolean = x match case x: CaptureRef => y match case y: CaptureRef => x.subsumes(y) case _ => false case _ => false - def viaInfo(info: Type)(test: Type => Boolean): Boolean = info.dealias match + def viaInfo(info: Type)(test: Type => Boolean): Boolean = info.match case info: SingletonCaptureRef => test(info) - case CapturingType(parent, _) => - if this.derivesFrom(defn.Caps_CapSet) then test(info) - /* - If `this` is a capture set variable `C^`, then it is possible that it can be - reached from term variables in a reachability chain through the context. - For instance, in `def test[C^](src: Foo^{C^}) = { val x: Foo^{src} = src; val y: Foo^{x} = x; y }` - we expect that `C^` subsumes `x` and `y` in the body of the method - (cf. test case cc-poly-varargs.scala for a more involved example). - */ - else viaInfo(parent)(test) case info: AndType => viaInfo(info.tp1)(test) || viaInfo(info.tp2)(test) case info: OrType => viaInfo(info.tp1)(test) && viaInfo(info.tp2)(test) + case info @ CapturingType(_,_) if this.derivesFrom(defn.Caps_CapSet) => + /* + If `this` is a capture set variable `C^`, then it is possible that it can be + reached from term variables in a reachability chain through the context. + For instance, in `def test[C^](src: Foo^{C^}) = { val x: Foo^{src} = src; val y: Foo^{x} = x; y }` + we expect that `C^` subsumes `x` and `y` in the body of the method + (cf. test case cc-poly-varargs.scala for a more involved example). + */ + test(info) case _ => false (this eq y) - || maxSubsumes(y, canAddHidden = !vs.isOpen) + || this.isRootCapability || y.match - case y: TermRef if !y.isCap => + case y: TermRef if !y.isRootCapability => y.prefix.match case ypre: CaptureRef => this.subsumes(ypre) @@ -211,7 +150,6 @@ trait CaptureRef extends TypeProxy, ValueType: case _ => false || viaInfo(y.info)(subsumingRefs(this, _)) case MaybeCapability(y1) => this.stripMaybe.subsumes(y1) - case ReadOnlyCapability(y1) => this.stripReadOnly.subsumes(y1) case y: TypeRef if y.derivesFrom(defn.Caps_CapSet) => // The upper and lower bounds don't have to be in the form of `CapSet^{...}`. // They can be other capture set variables, which are bounded by `CapSet`, @@ -242,53 +180,6 @@ trait CaptureRef extends TypeProxy, ValueType: case _ => false end subsumes - /** This is a maximal capability that subsumes `y` in given context and VarState. - * @param canAddHidden If true we allow maximal capabilities to subsume all other capabilities. - * We add those capabilities to the hidden set if this is Fresh.Cap - * If false we only accept `y` elements that are already in the - * hidden set of this Fresh.Cap. The idea is that in a VarState that - * accepts additions we first run `maxSubsumes` with `canAddHidden = false` - * so that new variables get added to the sets. If that fails, we run - * the test again with canAddHidden = true as a last effort before we - * fail a comparison. - */ - def maxSubsumes(y: CaptureRef, canAddHidden: Boolean)(using ctx: Context, vs: VarState = VarState.Separate): Boolean = - this.match - case Fresh.Cap(hidden) => - vs.ifNotSeen(this)(hidden.elems.exists(_.subsumes(y))) - || !y.stripReadOnly.isCap && canAddHidden && vs.addHidden(hidden, y) - case _ => - this.isCap && canAddHidden - || y.match - case ReadOnlyCapability(y1) => this.stripReadOnly.maxSubsumes(y1, canAddHidden) - case _ => false - - /** `x covers y` if we should retain `y` when computing the overlap of - * two footprints which have `x` respectively `y` as elements. - * We assume that .rd have already been stripped on both sides. - * We have: - * - * x covers x - * x covers y ==> x covers y.f - * x covers y ==> x* covers y*, x? covers y? - * TODO what other clauses from subsumes do we need to port here? - */ - final def covers(y: CaptureRef)(using Context): Boolean = - (this eq y) - || y.match - case y @ TermRef(ypre: CaptureRef, _) if !y.isCap => - this.covers(ypre) - case ReachCapability(y1) => - this match - case ReachCapability(x1) => x1.covers(y1) - case _ => false - case MaybeCapability(y1) => - this match - case MaybeCapability(x1) => x1.covers(y1) - case _ => false - case _ => - false - def assumedContainsOf(x: TypeRef)(using Context): SimpleIdentitySet[CaptureRef] = CaptureSet.assumedContains.getOrElse(x, SimpleIdentitySet.empty) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index 55fda0f22a08..39c41c369864 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -14,6 +14,7 @@ import printing.{Showable, Printer} import printing.Texts.* import util.{SimpleIdentitySet, Property} import typer.ErrorReporting.Addenda +import TypeComparer.subsumesExistentially import util.common.alwaysTrue import scala.collection.{mutable, immutable} import CCState.* @@ -80,35 +81,13 @@ sealed abstract class CaptureSet extends Showable: assert(!isConst) asInstanceOf[Var] - /** Convert to Const with current elements unconditionally */ - def toConst: Const = this match - case c: Const => c - case v: Var => Const(v.elems) - /** Does this capture set contain the root reference `cap` as element? */ final def isUniversal(using Context) = - elems.exists(_.isCap) - - /** Does this capture set contain the root reference `cap` as element? */ - final def isUniversalOrFresh(using Context) = - elems.exists(_.isCapOrFresh) - - /** Does this capture set contain a root reference `cap` or `cap.rd` as element? */ - final def containsRootCapability(using Context) = elems.exists(_.isRootCapability) - final def containsCap(using Context) = - elems.exists(_.stripReadOnly.isCap) - final def isUnboxable(using Context) = elems.exists(elem => elem.isRootCapability || Existential.isExistentialVar(elem)) - final def isReadOnly(using Context): Boolean = - elems.forall(_.isReadOnly) - - final def isExclusive(using Context): Boolean = - elems.exists(_.isExclusive) - final def keepAlways: Boolean = this.isInstanceOf[EmptyWithProvenance] /** Try to include an element in this capture set. @@ -146,8 +125,8 @@ sealed abstract class CaptureSet extends Showable: * element is not the root capability, try instead to include its underlying * capture set. */ - protected final def addNewElem(elem: CaptureRef)(using ctx: Context, vs: VarState): CompareResult = - if elem.isMaxCapability || !vs.isOpen then + protected final def addNewElem(elem: CaptureRef)(using Context, VarState): CompareResult = + if elem.isMaxCapability || summon[VarState] == FrozenState then addThisElem(elem) else addThisElem(elem).orElse: @@ -167,40 +146,27 @@ sealed abstract class CaptureSet extends Showable: */ protected def addThisElem(elem: CaptureRef)(using Context, VarState): CompareResult - protected def addHiddenElem(elem: CaptureRef)(using ctx: Context, vs: VarState): CompareResult = - if elems.exists(_.maxSubsumes(elem, canAddHidden = true)) - then CompareResult.OK - else CompareResult.Fail(this :: Nil) - /** If this is a variable, add `cs` as a dependent set */ protected def addDependent(cs: CaptureSet)(using Context, VarState): CompareResult /** If `cs` is a variable, add this capture set as one of its dependent sets */ protected def addAsDependentTo(cs: CaptureSet)(using Context): this.type = - cs.addDependent(this)(using ctx, VarState.Unrecorded) + cs.addDependent(this)(using ctx, UnrecordedState) this /** {x} <:< this where <:< is subcapturing, but treating all variables * as frozen. */ - def accountsFor(x: CaptureRef)(using ctx: Context, vs: VarState = VarState.Separate): Boolean = - + def accountsFor(x: CaptureRef)(using Context): Boolean = def debugInfo(using Context) = i"$this accountsFor $x, which has capture set ${x.captureSetOfInfo}" - def test(using Context) = reporting.trace(debugInfo): elems.exists(_.subsumes(x)) - || // Even though subsumes already follows captureSetOfInfo, this is not enough. - // For instance x: C^{y, z}. Then neither y nor z subsumes x but {y, z} accounts for x. - !x.isMaxCapability + || !x.isMaxCapability && !x.derivesFrom(defn.Caps_CapSet) - && !(vs == VarState.Separate && x.captureSetOfInfo.containsRootCapability) - // in VarState.Separate, don't try to widen to cap since that might succeed with {cap} <: {cap} - && x.captureSetOfInfo.subCaptures(this, VarState.Separate).isOK - + && x.captureSetOfInfo.subCaptures(this, frozen = true).isOK comparer match case comparer: ExplainingTypeComparer => comparer.traceIndented(debugInfo)(test) case _ => test - end accountsFor /** A more optimistic version of accountsFor, which does not take variable supersets * of the `x` reference into account. A set might account for `x` if it accounts @@ -210,13 +176,14 @@ sealed abstract class CaptureSet extends Showable: * root capability `cap`. */ def mightAccountFor(x: CaptureRef)(using Context): Boolean = - reporting.trace(i"$this mightAccountFor $x, ${x.captureSetOfInfo}?", show = true): - elems.exists(_.subsumes(x)(using ctx, VarState.ClosedUnrecorded)) + reporting.trace(i"$this mightAccountFor $x, ${x.captureSetOfInfo}?", show = true) { + elems.exists(_.subsumes(x)) || !x.isMaxCapability && { val elems = x.captureSetOfInfo.elems !elems.isEmpty && elems.forall(mightAccountFor) } + } /** A more optimistic version of subCaptures used to choose one of two typing rules * for selections and applications. `cs1 mightSubcapture cs2` if `cs2` might account for @@ -227,12 +194,16 @@ sealed abstract class CaptureSet extends Showable: elems.forall(that.mightAccountFor) && !that.elems.forall(this.mightAccountFor) - /** The subcapturing test, taking an explicit VarState. */ - final def subCaptures(that: CaptureSet, vs: VarState)(using Context): CompareResult = - subCaptures(that)(using ctx, vs) + /** The subcapturing test. + * @param frozen if true, no new variables or dependent sets are allowed to + * be added when making this test. An attempt to add either + * will result in failure. + */ + final def subCaptures(that: CaptureSet, frozen: Boolean)(using Context): CompareResult = + subCaptures(that)(using ctx, if frozen then FrozenState else VarState()) /** The subcapturing test, using a given VarState */ - final def subCaptures(that: CaptureSet)(using ctx: Context, vs: VarState = VarState()): CompareResult = + private def subCaptures(that: CaptureSet)(using Context, VarState): CompareResult = val result = that.tryInclude(elems, this) if result.isOK then addDependent(that) @@ -246,22 +217,19 @@ sealed abstract class CaptureSet extends Showable: * in a frozen state. */ def =:= (that: CaptureSet)(using Context): Boolean = - this.subCaptures(that, VarState.Separate).isOK - && that.subCaptures(this, VarState.Separate).isOK + this.subCaptures(that, frozen = true).isOK + && that.subCaptures(this, frozen = true).isOK /** The smallest capture set (via <:<) that is a superset of both * `this` and `that` */ def ++ (that: CaptureSet)(using Context): CaptureSet = - if this.subCaptures(that, VarState.Separate).isOK then + if this.subCaptures(that, frozen = true).isOK then if that.isAlwaysEmpty && this.keepAlways then this else that - else if that.subCaptures(this, VarState.Separate).isOK then this + else if that.subCaptures(this, frozen = true).isOK then this else if this.isConst && that.isConst then Const(this.elems ++ that.elems) else Union(this, that) - def ++ (that: CaptureSet.Const)(using Context): CaptureSet.Const = - Const(this.elems ++ that.elems) - /** The smallest superset (via <:<) of this capture set that also contains `ref`. */ def + (ref: CaptureRef)(using Context): CaptureSet = @@ -270,8 +238,8 @@ sealed abstract class CaptureSet extends Showable: /** The largest capture set (via <:<) that is a subset of both `this` and `that` */ def **(that: CaptureSet)(using Context): CaptureSet = - if this.subCaptures(that, VarState.Closed()).isOK then this - else if that.subCaptures(this, VarState.Closed()).isOK then that + if this.subCaptures(that, frozen = true).isOK then this + else if that.subCaptures(this, frozen = true).isOK then that else if this.isConst && that.isConst then Const(elemIntersection(this, that)) else Intersection(this, that) @@ -342,8 +310,6 @@ sealed abstract class CaptureSet extends Showable: def maybe(using Context): CaptureSet = map(MaybeMap()) - def readOnly(using Context): CaptureSet = map(ReadOnlyMap()) - /** Invoke handler if this set has (or later aquires) the root capability `cap` */ def disallowRootCapability(handler: () => Context ?=> Unit)(using Context): this.type = if isUnboxable then handler() @@ -388,11 +354,6 @@ sealed abstract class CaptureSet extends Showable: override def toText(printer: Printer): Text = printer.toTextCaptureSet(this) ~~ description - /** Apply function `f` to the elements. Typically used for printing. - * Overridden in HiddenSet so that we don't run into infinite recursions - */ - def processElems[T](f: Refs => T): T = f(elems) - object CaptureSet: type Refs = SimpleIdentitySet[CaptureRef] type Vars = SimpleIdentitySet[Var] @@ -403,22 +364,15 @@ object CaptureSet: /** If set to `true`, capture stack traces that tell us where sets are created */ private final val debugSets = false - val emptyRefs: Refs = SimpleIdentitySet.empty + private val emptySet = SimpleIdentitySet.empty /** The empty capture set `{}` */ - val empty: CaptureSet.Const = Const(emptyRefs) + val empty: CaptureSet.Const = Const(emptySet) /** The universal capture set `{cap}` */ def universal(using Context): CaptureSet = defn.captureRoot.termRef.singletonCaptureSet - def fresh(owner: Symbol = NoSymbol)(using Context): CaptureSet = - Fresh.Cap(owner).singletonCaptureSet - - /** The shared capture set `{cap.rd}` */ - def shared(using Context): CaptureSet = - defn.captureRoot.termRef.readOnly.singletonCaptureSet - /** Used as a recursion brake */ @sharable private[dotc] val Pending = Const(SimpleIdentitySet.empty) @@ -435,7 +389,7 @@ object CaptureSet: def isAlwaysEmpty = elems.isEmpty def addThisElem(elem: CaptureRef)(using Context, VarState): CompareResult = - addHiddenElem(elem) + CompareResult.Fail(this :: Nil) def addDependent(cs: CaptureSet)(using Context, VarState) = CompareResult.OK @@ -462,16 +416,16 @@ object CaptureSet: * nulls, this provides more lenient checking against compilation units that * were not yet compiled with capture checking on. */ - object Fluid extends Const(emptyRefs): + object Fluid extends Const(emptySet): override def isAlwaysEmpty = false override def addThisElem(elem: CaptureRef)(using Context, VarState) = CompareResult.OK - override def accountsFor(x: CaptureRef)(using Context, VarState): Boolean = true + override def accountsFor(x: CaptureRef)(using Context): Boolean = true override def mightAccountFor(x: CaptureRef)(using Context): Boolean = true override def toString = "" end Fluid /** The subclass of captureset variables with given initial elements */ - class Var(override val owner: Symbol = NoSymbol, initialElems: Refs = emptyRefs, val level: Level = undefinedLevel, underBox: Boolean = false)(using @constructorOnly ictx: Context) extends CaptureSet: + class Var(override val owner: Symbol = NoSymbol, initialElems: Refs = emptySet, val level: Level = undefinedLevel, underBox: Boolean = false)(using @constructorOnly ictx: Context) extends CaptureSet: /** A unique identification number for diagnostics */ val id = @@ -489,7 +443,7 @@ object CaptureSet: /** The sets currently known to be dependent sets (i.e. new additions to this set * are propagated to these dependent sets.) */ - var deps: Deps = SimpleIdentitySet.empty + var deps: Deps = emptySet def isConst = isSolved def isAlwaysEmpty = isSolved && elems.isEmpty @@ -531,16 +485,16 @@ object CaptureSet: deps = state.deps(this) final def addThisElem(elem: CaptureRef)(using Context, VarState): CompareResult = - if isConst || !recordElemsState() then // Fail if variable is solved or given VarState is frozen - addHiddenElem(elem) - else if Existential.isBadExistential(elem) then // Fail if `elem` is an out-of-scope existential + if isConst // Fail if variable is solved, + || !recordElemsState() // or given VarState is frozen, + || Existential.isBadExistential(elem) // or `elem` is an out-of-scope existential, + then CompareResult.Fail(this :: Nil) else if !levelOK(elem) then CompareResult.LevelError(this, elem) // or `elem` is not visible at the level of the set. else - // id == 108 then assert(false, i"trying to add $elem to $this") + //if id == 34 then assert(!elem.isUniversalRootCapability) assert(elem.isTrackableRef, elem) - assert(!this.isInstanceOf[HiddenSet] || summon[VarState] == VarState.Separate, summon[VarState]) elems += elem if elem.isRootCapability then rootAddedHandler() @@ -572,8 +526,6 @@ object CaptureSet: elem.cls.ccLevel.nextInner <= level case ReachCapability(elem1) => levelOK(elem1) - case ReadOnlyCapability(elem1) => - levelOK(elem1) case MaybeCapability(elem1) => levelOK(elem1) case _ => @@ -606,10 +558,8 @@ object CaptureSet: final def upperApprox(origin: CaptureSet)(using Context): CaptureSet = if isConst then this - else if isUniversal || computingApprox then + else if elems.exists(_.isRootCapability) || computingApprox then universal - else if containsCap && isReadOnly then - shared else computingApprox = true try @@ -633,12 +583,10 @@ object CaptureSet: def solve()(using Context): Unit = if !isConst then val approx = upperApprox(empty) - .map(Fresh.FromCap(NoSymbol).inverse) // Fresh.Cap --> cap .showing(i"solve $this = $result", capt) //println(i"solving var $this $approx ${approx.isConst} deps = ${deps.toList}") val newElems = approx.elems -- elems - given VarState() - if tryInclude(newElems, empty).isOK then + if tryInclude(newElems, empty)(using ctx, VarState()).isOK then markSolved() /** Mark set as solved and propagate this info to all dependent sets */ @@ -922,21 +870,6 @@ object CaptureSet: def elemIntersection(cs1: CaptureSet, cs2: CaptureSet)(using Context): Refs = cs1.elems.filter(cs2.mightAccountFor) ++ cs2.elems.filter(cs1.mightAccountFor) - /** A capture set variable used to record the references hidden by a Fresh.Cap instance */ - class HiddenSet(initialHidden: Refs = emptyRefs)(using @constructorOnly ictx: Context) - extends Var(initialElems = initialHidden): - - /** Apply function `f` to `elems` while setting `elems` to empty for the - * duration. This is used to escape infinite recursions if two Fresh.Caps - * refer to each other in their hidden sets. - */ - override def processElems[T](f: Refs => T): T = - val savedElems = elems - elems = emptyRefs - try f(savedElems) - finally elems = savedElems - end HiddenSet - /** Extrapolate tm(r) according to `variance`. Let r1 be the result of tm(r). * - If r1 is a tracked CaptureRef, return {r1} * - If r1 has an empty capture set, return {} @@ -972,7 +905,7 @@ object CaptureSet: */ def subCapturesRange(arg1: TypeBounds, arg2: Type)(using Context): Boolean = arg1 match case TypeBounds(CapturingType(lo, loRefs), CapturingType(hi, hiRefs)) if lo =:= hi => - given VarState() + given VarState = VarState() val cs2 = arg2.captureSet hiRefs.subCaptures(cs2).isOK && cs2.subCaptures(loRefs).isOK case _ => @@ -1048,7 +981,8 @@ object CaptureSet: def getElems(v: Var): Option[Refs] = elemsMap.get(v) /** Record elements, return whether this was allowed. - * By default, recording is allowed in regular but not in frozen states. + * By default, recording is allowed but the special state FrozenState + * overrides this. */ def putElems(v: Var, elems: Refs): Boolean = { elemsMap(v) = elems; true } @@ -1059,105 +993,58 @@ object CaptureSet: def getDeps(v: Var): Option[Deps] = depsMap.get(v) /** Record dependent sets, return whether this was allowed. - * By default, recording is allowed in regular but not in frozen states. + * By default, recording is allowed but the special state FrozenState + * overrides this. */ def putDeps(v: Var, deps: Deps): Boolean = { depsMap(v) = deps; true } - /** Does this state allow additions of elements to capture set variables? */ - def isOpen = true - - /** Add element to hidden set, recording it in elemsMap, - * return whether this was allowed. By default, recording is allowed - * but the special state VarState.Separate overrides this. - */ - def addHidden(hidden: HiddenSet, elem: CaptureRef)(using Context): Boolean = - elemsMap.get(hidden) match - case None => elemsMap(hidden) = hidden.elems - case _ => - hidden.elems += elem - true - /** Roll back global state to what was recorded in this VarState */ def rollBack(): Unit = elemsMap.keysIterator.foreach(_.resetElems()(using this)) depsMap.keysIterator.foreach(_.resetDeps()(using this)) - - private var seen: util.EqHashSet[CaptureRef] = new util.EqHashSet - - /** Run test `pred` unless `ref` was seen in an enclosing `ifNotSeen` operation */ - def ifNotSeen(ref: CaptureRef)(pred: => Boolean): Boolean = - if seen.add(ref) then - try pred finally seen -= ref - else false - - object VarState: - - /** A class for states that do not allow to record elements or dependent sets. - * In effect this means that no new elements or dependent sets can be added - * in these states (since the previous state cannot be recorded in a snapshot) - * On the other hand, these states do allow by default Fresh.Cap instances to - * subsume arbitary types, which are then recorded in their hidden sets. - */ - class Closed extends VarState: - override def putElems(v: Var, refs: Refs) = false - override def putDeps(v: Var, deps: Deps) = false - override def isOpen = false - - /** A closed state that allows a Fresh.Cap instance to subsume a - * reference `r` only if `r` is already present in the hidden set of the instance. - * No new references can be added. - */ - @sharable - object Separate extends Closed: - override def addHidden(hidden: HiddenSet, elem: CaptureRef)(using Context): Boolean = false - - /** A special state that turns off recording of elements. Used only - * in `addSub` to prevent cycles in recordings. - */ - @sharable - private[CaptureSet] object Unrecorded extends VarState: - override def putElems(v: Var, refs: Refs) = true - override def putDeps(v: Var, deps: Deps) = true - override def rollBack(): Unit = () - override def addHidden(hidden: HiddenSet, elem: CaptureRef)(using Context): Boolean = true - - /** A closed state that turns off recording of hidden elements (but allows - * adding them). Used in `mightAccountFor`. - */ - @sharable - private[CaptureSet] object ClosedUnrecorded extends Closed: - override def addHidden(hidden: HiddenSet, elem: CaptureRef)(using Context): Boolean = true - end VarState + /** A special state that does not allow to record elements or dependent sets. + * In effect this means that no new elements or dependent sets can be added + * in this state (since the previous state cannot be recorded in a snapshot) + */ + @sharable + object FrozenState extends VarState: + override def putElems(v: Var, refs: Refs) = false + override def putDeps(v: Var, deps: Deps) = false + override def rollBack(): Unit = () + @sharable + /** A special state that turns off recording of elements. Used only + * in `addSub` to prevent cycles in recordings. + */ + private object UnrecordedState extends VarState: + override def putElems(v: Var, refs: Refs) = true + override def putDeps(v: Var, deps: Deps) = true + override def rollBack(): Unit = () + /** The current VarState, as passed by the implicit context */ def varState(using state: VarState): VarState = state - /** A template for maps on capabilities where f(c) <: c and f(f(c)) = c */ - private abstract class NarrowingCapabilityMap(using Context) extends BiTypeMap: - - def mapRef(ref: CaptureRef): CaptureRef + /** Maps `x` to `x?` */ + private class MaybeMap(using Context) extends BiTypeMap: def apply(t: Type) = t match - case t: CaptureRef if t.isTrackableRef => mapRef(t) + case t: CaptureRef if t.isTrackableRef => t.maybe case _ => mapOver(t) + override def toString = "Maybe" + lazy val inverse = new BiTypeMap: - def apply(t: Type) = t // since f(c) <: c, this is the best inverse - def inverse = NarrowingCapabilityMap.this - override def toString = NarrowingCapabilityMap.this.toString ++ ".inverse" - end NarrowingCapabilityMap - /** Maps `x` to `x?` */ - private class MaybeMap(using Context) extends NarrowingCapabilityMap: - def mapRef(ref: CaptureRef): CaptureRef = ref.maybe - override def toString = "Maybe" + def apply(t: Type) = t match + case t: CaptureRef if t.isMaybe => t.stripMaybe + case t => mapOver(t) + + def inverse = MaybeMap.this - /** Maps `x` to `x.rd` */ - private class ReadOnlyMap(using Context) extends NarrowingCapabilityMap: - def mapRef(ref: CaptureRef): CaptureRef = ref.readOnly - override def toString = "ReadOnly" + override def toString = "Maybe.inverse" + end MaybeMap /* Not needed: def ofClass(cinfo: ClassInfo, argTypes: List[Type])(using Context): CaptureSet = @@ -1186,8 +1073,6 @@ object CaptureSet: case ReachCapability(ref1) => ref1.widen.deepCaptureSet(includeTypevars = true) .showing(i"Deep capture set of $ref: ${ref1.widen} = ${result}", capt) - case ReadOnlyCapability(ref1) => - ref1.captureSetOfInfo.map(ReadOnlyMap()) case _ => if ref.isMaxCapability then ref.singletonCaptureSet else ofType(ref.underlying, followResult = true) @@ -1206,9 +1091,6 @@ object CaptureSet: case CapturingType(parent, refs) => recur(parent) ++ refs case tp @ AnnotatedType(parent, ann) if ann.hasSymbol(defn.ReachCapabilityAnnot) => - // Note: we don't use the `ReachCapability(parent)` extractor here since that - // only works if `parent` is a CaptureRef, but in illegal programs it might not be. - // And then we do not want to fall back to empty. parent match case parent: SingletonCaptureRef if parent.isTrackableRef => tp.singletonCaptureSet @@ -1259,7 +1141,7 @@ object CaptureSet: case t: TypeRef if t.symbol.isAbstractOrParamType && !seen.contains(t.symbol) => seen += t.symbol val upper = t.info.bounds.hi - if includeTypevars && upper.isExactlyAny then CaptureSet.fresh(t.symbol) + if includeTypevars && upper.isExactlyAny then CaptureSet.universal else this(cs, upper) case t @ FunctionOrMethod(args, res @ Existential(_, _)) if args.forall(_.isAlwaysPure) => @@ -1314,10 +1196,9 @@ object CaptureSet: for CompareResult.LevelError(cs, ref) <- ccState.levelError.toList yield ccState.levelError = None if ref.isRootCapability then - def capStr = if ref.isReadOnly then "cap.rd" else "cap" i""" | - |Note that the universal capability `$capStr` + |Note that the universal capability `cap` |cannot be included in capture set $cs""" else val levelStr = ref match diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 7380996b3aed..830d9ad0a4d4 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -23,8 +23,6 @@ import CCState.* import StdNames.nme import NameKinds.{DefaultGetterName, WildcardParamName, UniqueNameKind} import reporting.{trace, Message, OverrideError} -import Existential.derivedExistentialType -import Annotations.Annotation /** The capture checker */ object CheckCaptures: @@ -90,7 +88,6 @@ object CheckCaptures: tp case _ => mapOver(tp) - override def toString = "SubstParamsMap" end SubstParamsMap /** Used for substituting parameters in a special case: when all actual arguments @@ -110,7 +107,6 @@ object CheckCaptures: tp case _ => mapOver(tp) - override def toString = "SubstParamsBiMap" lazy val inverse = new BiTypeMap: def apply(tp: Type): Type = tp match @@ -127,7 +123,6 @@ object CheckCaptures: tp case _ => mapOver(tp) - override def toString = "SubstParamsBiMap.inverse" def inverse = thisMap end SubstParamsBiMap @@ -155,7 +150,6 @@ object CheckCaptures: |is must be a type parameter or abstract type with a caps.CapSet upper bound.""", elem.srcPos) case ReachCapabilityApply(arg) => check(arg, elem.srcPos) - case ReadOnlyCapabilityApply(arg) => check(arg, elem.srcPos) case _ => check(elem, elem.srcPos) /** Under the sealed policy, report an error if some part of `tp` contains the @@ -243,21 +237,6 @@ object CheckCaptures: /** Was a new type installed for this tree? */ def hasNuType: Boolean - - /** Is this tree passed to a parameter or assigned to a value with a type - * that contains cap in no-flip covariant position, which will necessite - * a separation check? - */ - def needsSepCheck: Boolean - - /** If a tree is an argument for which needsSepCheck is true, - * the type of the formal paremeter corresponding to the argument. - */ - def formalType: Type - - /** The "use set", i.e. the capture set marked as free at this node. */ - def markedFree: CaptureSet - end CheckerAPI class CheckCaptures extends Recheck, SymTransformer: @@ -298,18 +277,6 @@ class CheckCaptures extends Recheck, SymTransformer: */ private val todoAtPostCheck = new mutable.ListBuffer[() => Unit] - /** Maps trees that need a separation check because they are arguments to - * polymorphic parameters. The trees are mapped to the formal parameter type. - */ - private val sepCheckFormals = util.EqHashMap[Tree, Type]() - - private val usedSet = util.EqHashMap[Tree, CaptureSet]() - - extension [T <: Tree](tree: T) - def needsSepCheck: Boolean = sepCheckFormals.contains(tree) - def formalType: Type = sepCheckFormals.getOrElse(tree, NoType) - def markedFree = usedSet.getOrElse(tree, CaptureSet.empty) - /** Instantiate capture set variables appearing contra-variantly to their * upper approximation. */ @@ -339,33 +306,32 @@ class CheckCaptures extends Recheck, SymTransformer: /** Assert subcapturing `cs1 <: cs2` (available for debugging, otherwise unused) */ def assertSub(cs1: CaptureSet, cs2: CaptureSet)(using Context) = - assert(cs1.subCaptures(cs2).isOK, i"$cs1 is not a subset of $cs2") + assert(cs1.subCaptures(cs2, frozen = false).isOK, i"$cs1 is not a subset of $cs2") /** If `res` is not CompareResult.OK, report an error */ - def checkOK(res: CompareResult, prefix: => String, added: CaptureRef | CaptureSet, pos: SrcPos, provenance: => String = "")(using Context): Unit = + def checkOK(res: CompareResult, prefix: => String, pos: SrcPos, provenance: => String = "")(using Context): Unit = if !res.isOK then - inContext(Fresh.printContext(added, res.blocking)): - def toAdd: String = CaptureSet.levelErrors.toAdd.mkString - def descr: String = - val d = res.blocking.description - if d.isEmpty then provenance else "" - report.error(em"$prefix included in the allowed capture set ${res.blocking}$descr$toAdd", pos) + def toAdd: String = CaptureSet.levelErrors.toAdd.mkString + def descr: String = + val d = res.blocking.description + if d.isEmpty then provenance else "" + report.error(em"$prefix included in the allowed capture set ${res.blocking}$descr$toAdd", pos) /** Check subcapturing `{elem} <: cs`, report error on failure */ def checkElem(elem: CaptureRef, cs: CaptureSet, pos: SrcPos, provenance: => String = "")(using Context) = checkOK( - elem.singletonCaptureSet.subCaptures(cs), + elem.singletonCaptureSet.subCaptures(cs, frozen = false), i"$elem cannot be referenced here; it is not", - elem, pos, provenance) + pos, provenance) /** Check subcapturing `cs1 <: cs2`, report error on failure */ def checkSubset(cs1: CaptureSet, cs2: CaptureSet, pos: SrcPos, provenance: => String = "", cs1description: String = "")(using Context) = checkOK( - cs1.subCaptures(cs2), + cs1.subCaptures(cs2, frozen = false), if cs1.elems.size == 1 then i"reference ${cs1.elems.toList.head}$cs1description is not" else i"references $cs1$cs1description are not all", - cs1, pos, provenance) + pos, provenance) /** If `sym` is a class or method nested inside a term, a capture set variable representing * the captured variables of the environment associated with `sym`. @@ -412,17 +378,17 @@ class CheckCaptures extends Recheck, SymTransformer: /** Include `sym` in the capture sets of all enclosing environments nested in the * the environment in which `sym` is defined. */ - def markFree(sym: Symbol, tree: Tree)(using Context): Unit = - markFree(sym, sym.termRef, tree) + def markFree(sym: Symbol, pos: SrcPos)(using Context): Unit = + markFree(sym, sym.termRef, pos) - def markFree(sym: Symbol, ref: CaptureRef, tree: Tree)(using Context): Unit = - if sym.exists && ref.isTracked then markFree(ref.captureSet, tree) + def markFree(sym: Symbol, ref: TermRef, pos: SrcPos)(using Context): Unit = + if sym.exists && ref.isTracked then markFree(ref.captureSet, pos) /** Make sure the (projected) `cs` is a subset of the capture sets of all enclosing * environments. At each stage, only include references from `cs` that are outside * the environment's owner */ - def markFree(cs: CaptureSet, tree: Tree)(using Context): Unit = + def markFree(cs: CaptureSet, pos: SrcPos)(using Context): Unit = // A captured reference with the symbol `sym` is visible from the environment // if `sym` is not defined inside the owner of the environment. inline def isVisibleFromEnv(sym: Symbol, env: Env) = @@ -444,7 +410,7 @@ class CheckCaptures extends Recheck, SymTransformer: val what = if ref.isType then "Capture set parameter" else "Local reach capability" report.error( em"""$what $c leaks into capture scope of ${env.ownerString}. - |To allow this, the ${ref.symbol} should be declared with a @use annotation""", tree.srcPos) + |To allow this, the ${ref.symbol} should be declared with a @use annotation""", pos) case _ => /** Avoid locally defined capability by charging the underlying type @@ -464,7 +430,7 @@ class CheckCaptures extends Recheck, SymTransformer: CaptureSet.ofType(c.widen, followResult = false) capt.println(i"Widen reach $c to $underlying in ${env.owner}") underlying.disallowRootCapability: () => - report.error(em"Local capability $c in ${env.ownerString} cannot have `cap` as underlying capture set", tree.srcPos) + report.error(em"Local capability $c in ${env.ownerString} cannot have `cap` as underlying capture set", pos) recur(underlying, env, lastEnv) /** Avoid locally defined capability if it is a reach capability or capture set @@ -487,7 +453,7 @@ class CheckCaptures extends Recheck, SymTransformer: val underlying = CaptureSet.ofTypeDeeply(c1.widen) capt.println(i"Widen reach $c to $underlying in ${env.owner}") underlying.disallowRootCapability: () => - report.error(em"Local reach capability $c leaks into capture scope of ${env.ownerString}", tree.srcPos) + report.error(em"Local reach capability $c leaks into capture scope of ${env.ownerString}", pos) recur(underlying, env, null) case c: TypeRef if c.isParamPath => checkUseDeclared(c, env, null) @@ -504,7 +470,7 @@ class CheckCaptures extends Recheck, SymTransformer: then avoidLocalCapability(c, env, lastEnv) else avoidLocalReachCapability(c, env) isVisible - checkSubset(included, env.captured, tree.srcPos, provenance(env)) + checkSubset(included, env.captured, pos, provenance(env)) capt.println(i"Include call or box capture $included from $cs in ${env.owner} --> ${env.captured}") if !isOfNestedMethod(env) then recur(included, nextEnvToCharge(env, !_.owner.isStaticOwner), env) @@ -512,14 +478,13 @@ class CheckCaptures extends Recheck, SymTransformer: // will be charged when that method is called. recur(cs, curEnv, null) - usedSet(tree) = tree.markedFree ++ cs end markFree /** Include references captured by the called method in the current environment stack */ - def includeCallCaptures(sym: Symbol, resType: Type, tree: Tree)(using Context): Unit = resType match + def includeCallCaptures(sym: Symbol, resType: Type, pos: SrcPos)(using Context): Unit = resType match case _: MethodOrPoly => // wait until method is fully applied case _ => - if sym.exists && curEnv.isOpen then markFree(capturedVars(sym), tree) + if sym.exists && curEnv.isOpen then markFree(capturedVars(sym), pos) /** Under the sealed policy, disallow the root capability in type arguments. * Type arguments come either from a TypeApply node or from an AppliedType @@ -543,57 +508,44 @@ class CheckCaptures extends Recheck, SymTransformer: for case (arg: TypeTree, pname) <- args.lazyZip(paramNames) do def where = if sym.exists then i" in an argument of $sym" else "" - val (addendum, errTree) = + val (addendum, pos) = if arg.isInferred - then ("\nThis is often caused by a local capability$where\nleaking as part of its result.", fn) - else if arg.span.exists then ("", arg) - else ("", fn) + then ("\nThis is often caused by a local capability$where\nleaking as part of its result.", fn.srcPos) + else if arg.span.exists then ("", arg.srcPos) + else ("", fn.srcPos) disallowRootCapabilitiesIn(arg.nuType, NoSymbol, - i"Type variable $pname of $sym", "be instantiated to", addendum, errTree.srcPos) + i"Type variable $pname of $sym", "be instantiated to", addendum, pos) val param = fn.symbol.paramNamed(pname) - if param.isUseParam then markFree(arg.nuType.deepCaptureSet, errTree) + if param.isUseParam then markFree(arg.nuType.deepCaptureSet, pos) end disallowCapInTypeArgs - /** Rechecking idents involves: - * - adding call captures for idents referring to methods - * - marking as free the identifier with any selections or .rd - * modifiers implied by the expected type - */ override def recheckIdent(tree: Ident, pt: Type)(using Context): Type = val sym = tree.symbol if sym.is(Method) then // If ident refers to a parameterless method, charge its cv to the environment - includeCallCaptures(sym, sym.info, tree) + includeCallCaptures(sym, sym.info, tree.srcPos) else if !sym.isStatic then - // Otherwise charge its symbol, but add all selections and also any `.rd` - // modifier implied by the expected type `pt`. - // Example: If we have `x` and the expected type says we select that with `.a.b` - // where `b` is a read-only method, we charge `x.a.b.rd` instead of `x`. - def addSelects(ref: TermRef, pt: Type): CaptureRef = pt match + // Otherwise charge its symbol, but add all selections implied by the e + // expected type `pt`. + // Example: If we have `x` and the expected type says we select that with `.a.b`, + // we charge `x.a.b` instead of `x`. + def addSelects(ref: TermRef, pt: Type): TermRef = pt match case pt: PathSelectionProto if ref.isTracked => - if pt.sym.isReadOnlyMethod then - ref.readOnly - else - // if `ref` is not tracked then the selection could not give anything new - // class SerializationProxy in stdlib-cc/../LazyListIterable.scala has an example where this matters. - addSelects(ref.select(pt.sym).asInstanceOf[TermRef], pt.pt) + // if `ref` is not tracked then the selection could not give anything new + // class SerializationProxy in stdlib-cc/../LazyListIterable.scala has an example where this matters. + addSelects(ref.select(pt.sym).asInstanceOf[TermRef], pt.pt) case _ => ref - var pathRef: CaptureRef = addSelects(sym.termRef, pt) - if pathRef.derivesFrom(defn.Caps_Mutable) && pt.isValueType && !pt.isMutableType then - pathRef = pathRef.readOnly - markFree(sym, pathRef, tree) + val pathRef = addSelects(sym.termRef, pt) + markFree(sym, pathRef, tree.srcPos) super.recheckIdent(tree, pt) /** The expected type for the qualifier of a selection. If the selection - * could be part of a capability path or is a a read-only method, we return - * a PathSelectionProto. + * could be part of a capabaility path, we return a PathSelectionProto. */ override def selectionProto(tree: Select, pt: Type)(using Context): Type = val sym = tree.symbol - if !sym.isOneOf(UnstableValueFlags) && !sym.isStatic - || sym.isReadOnlyMethod - then PathSelectionProto(sym, pt) + if !sym.isOneOf(UnstableValueFlags) && !sym.isStatic then PathSelectionProto(sym, pt) else super.selectionProto(tree, pt) /** A specialized implementation of the selection rule. @@ -621,15 +573,6 @@ class CheckCaptures extends Recheck, SymTransformer: } case _ => denot - // Don't allow update methods to be called unless the qualifier captures - // an exclusive reference. TODO This should probably rolled into - // qualifier logic once we have it. - if tree.symbol.isUpdateMethod && !qualType.captureSet.isExclusive then - report.error( - em"""cannot call update ${tree.symbol} from $qualType, - |since its capture set ${qualType.captureSet} is read-only""", - tree.srcPos) - val selType = recheckSelection(tree, qualType, name, disambiguate) val selWiden = selType.widen @@ -659,17 +602,15 @@ class CheckCaptures extends Recheck, SymTransformer: selType }//.showing(i"recheck sel $tree, $qualType = $result") - /** Hook for massaging a function before it is applied. Copies all @use and @consume - * annotations on method parameter symbols to the corresponding paramInfo types. + /** Hook for massaging a function before it is applied. Copies all @use annotations + * on method parameter symbols to the corresponding paramInfo types. */ override def prepareFunction(funtpe: MethodType, meth: Symbol)(using Context): MethodType = - val paramInfosWithUses = - funtpe.paramInfos.zipWithConserve(funtpe.paramNames): (formal, pname) => - val param = meth.paramNamed(pname) - def copyAnnot(tp: Type, cls: ClassSymbol) = param.getAnnotation(cls) match - case Some(ann) => AnnotatedType(tp, ann) - case _ => tp - copyAnnot(copyAnnot(formal, defn.UseAnnot), defn.ConsumeAnnot) + val paramInfosWithUses = funtpe.paramInfos.zipWithConserve(funtpe.paramNames): (formal, pname) => + val param = meth.paramNamed(pname) + param.getAnnotation(defn.UseAnnot) match + case Some(ann) => AnnotatedType(formal, ann) + case _ => formal funtpe.derivedLambdaType(paramInfos = paramInfosWithUses) /** Recheck applications, with special handling of unsafeAssumePure. @@ -679,31 +620,28 @@ class CheckCaptures extends Recheck, SymTransformer: val meth = tree.fun.symbol if meth == defn.Caps_unsafeAssumePure then val arg :: Nil = tree.args: @unchecked - val argType0 = recheck(arg, pt.stripCapturing.capturing(CaptureSet.universal)) + val argType0 = recheck(arg, pt.capturing(CaptureSet.universal)) val argType = if argType0.captureSet.isAlwaysEmpty then argType0 else argType0.widen.stripCapturing - capt.println(i"rechecking unsafeAssumePure of $arg with $pt: $argType") + capt.println(i"rechecking $arg with $pt: $argType") super.recheckFinish(argType, tree, pt) else val res = super.recheckApply(tree, pt) - includeCallCaptures(meth, res, tree) + includeCallCaptures(meth, res, tree.srcPos) res - /** Recheck argument against a "freshened" version of `formal` where toplevel `cap` - * occurrences are replaced by `Fresh.Cap`. Also, if formal parameter carries a `@use`, + /** Recheck argument, and, if formal parameter carries a `@use`, * charge the deep capture set of the actual argument to the environment. */ protected override def recheckArg(arg: Tree, formal: Type)(using Context): Type = - val freshenedFormal = Fresh.fromCap(formal) - val argType = recheck(arg, freshenedFormal) - .showing(i"recheck arg $arg vs $freshenedFormal", capt) - if formal.hasAnnotation(defn.UseAnnot) then - // The @use annotation is added to `formal` by `prepareFunction` - capt.println(i"charging deep capture set of $arg: ${argType} = ${argType.deepCaptureSet}") - markFree(argType.deepCaptureSet, arg) - if formal.containsCap then - sepCheckFormals(arg) = freshenedFormal + val argType = recheck(arg, formal) + formal match + case AnnotatedType(formal1, ann) if ann.symbol == defn.UseAnnot => + // The UseAnnot is added to `formal` by `prepareFunction` + capt.println(i"charging deep capture set of $arg: ${argType} = ${argType.deepCaptureSet}") + markFree(argType.deepCaptureSet, arg.srcPos) + case _ => argType /** Map existential captures in result to `cap` and implement the following @@ -733,7 +671,9 @@ class CheckCaptures extends Recheck, SymTransformer: val qualCaptures = qualType.captureSet val argCaptures = for (argType, formal) <- argTypes.lazyZip(funType.paramInfos) yield - if formal.hasAnnotation(defn.UseAnnot) then argType.deepCaptureSet else argType.captureSet + formal match + case AnnotatedType(_, ann) if ann.symbol == defn.UseAnnot => argType.deepCaptureSet + case _ => argType.captureSet appType match case appType @ CapturingType(appType1, refs) if qualType.exists @@ -782,28 +722,20 @@ class CheckCaptures extends Recheck, SymTransformer: /** First half of result pair: * Refine the type of a constructor call `new C(t_1, ..., t_n)` - * to C{val x_1: @refineOverride T_1, ..., x_m: @refineOverride T_m} - * where x_1, ..., x_m are the tracked parameters of C and - * T_1, ..., T_m are the types of the corresponding arguments. The @refineOveride - * annotations avoid problematic intersections of capture sets when those - * parameters are selected. + * to C{val x_1: T_1, ..., x_m: T_m} where x_1, ..., x_m are the tracked + * parameters of C and T_1, ..., T_m are the types of the corresponding arguments. * * Second half: union of initial capture set and all capture sets of arguments - * to tracked parameters. The initial capture set `initCs` is augmented with - * - Fresh.Cap if `core` extends Mutable - * - Fresh.Cap.rd if `core` extends Capability + * to tracked parameters. */ def addParamArgRefinements(core: Type, initCs: CaptureSet): (Type, CaptureSet) = var refined: Type = core var allCaptures: CaptureSet = - if core.derivesFromMutable then initCs ++ CaptureSet.fresh() - else if core.derivesFromCapability then initCs ++ Fresh.Cap().readOnly.singletonCaptureSet - 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 - refined = RefinedType(refined, getterName, - AnnotatedType(argType.unboxed, Annotation(defn.RefineOverrideAnnot, util.Spans.NoSpan))) // Yichen you might want to check this + refined = RefinedType(refined, getterName, argType.unboxed) // Yichen you might want to check this allCaptures ++= argType.captureSet (refined, allCaptures) @@ -819,8 +751,6 @@ class CheckCaptures extends Recheck, SymTransformer: // can happen for curried constructors if instantiate of a previous step // added capture set to result. augmentConstructorType(parent, initCs ++ refs) - case core @ Existential(boundVar, core1) => - core.derivedExistentialType(augmentConstructorType(core1, initCs)) case _ => val (refined, cs) = addParamArgRefinements(core, initCs) refined.capturing(cs) @@ -841,7 +771,7 @@ class CheckCaptures extends Recheck, SymTransformer: case fun => fun.symbol disallowCapInTypeArgs(tree.fun, meth, tree.args) val res = Existential.toCap(super.recheckTypeApply(tree, pt)) - includeCallCaptures(tree.symbol, res, tree) + includeCallCaptures(tree.symbol, res, tree.srcPos) checkContains(tree) res end recheckTypeApply @@ -1118,7 +1048,7 @@ class CheckCaptures extends Recheck, SymTransformer: case AnnotatedType(_, annot) if annot.symbol == defn.RequiresCapabilityAnnot => annot.tree match case Apply(_, cap :: Nil) => - markFree(cap.symbol, tree) + markFree(cap.symbol, tree.srcPos) case _ => case _ => super.recheckTyped(tree) @@ -1173,9 +1103,8 @@ class CheckCaptures extends Recheck, SymTransformer: super.recheck(tree, pt) finally curEnv = saved if tree.isTerm && !pt.isBoxedCapturing && pt != LhsProto then - markFree(res.boxedCaptureSet, tree) + markFree(res.boxedCaptureSet, tree.srcPos) res - end recheck /** Under the old unsealed policy: check that cap is ot unboxed */ override def recheckFinish(tpe: Type, tree: Tree, pt: Type)(using Context): Type = @@ -1240,7 +1169,7 @@ class CheckCaptures extends Recheck, SymTransformer: override def checkConformsExpr(actual: Type, expected: Type, tree: Tree, addenda: Addenda)(using Context): Type = var expected1 = alignDependentFunction(expected, actual.stripCapturing) val boxErrors = new mutable.ListBuffer[Message] - val actualBoxed = adapt(actual, expected1, tree, boxErrors) + val actualBoxed = adapt(actual, expected1, tree.srcPos, boxErrors) //println(i"check conforms $actualBoxed <<< $expected1") if actualBoxed eq actual then @@ -1254,11 +1183,10 @@ class CheckCaptures extends Recheck, SymTransformer: actualBoxed else capt.println(i"conforms failed for ${tree}: $actual vs $expected") - inContext(Fresh.printContext(actualBoxed, expected1)): - err.typeMismatch(tree.withType(actualBoxed), expected1, - addApproxAddenda( - addenda ++ CaptureSet.levelErrors ++ boxErrorAddenda(boxErrors), - expected1)) + err.typeMismatch(tree.withType(actualBoxed), expected1, + addApproxAddenda( + addenda ++ CaptureSet.levelErrors ++ boxErrorAddenda(boxErrors), + expected1)) actual end checkConformsExpr @@ -1360,7 +1288,7 @@ class CheckCaptures extends Recheck, SymTransformer: * * @param alwaysConst always make capture set variables constant after adaptation */ - def adaptBoxed(actual: Type, expected: Type, tree: Tree, covariant: Boolean, alwaysConst: Boolean, boxErrors: BoxErrors)(using Context): Type = + def adaptBoxed(actual: Type, expected: Type, pos: SrcPos, covariant: Boolean, alwaysConst: Boolean, boxErrors: BoxErrors)(using Context): Type = def recur(actual: Type, expected: Type, covariant: Boolean): Type = @@ -1424,10 +1352,10 @@ class CheckCaptures extends Recheck, SymTransformer: val cs = actual.captureSet if covariant then cs ++ leaked else - if !leaked.subCaptures(cs).isOK then + if !leaked.subCaptures(cs, frozen = false).isOK then report.error( em"""$expected cannot be box-converted to ${actual.capturing(leaked)} - |since the additional capture set $leaked resulted from box conversion is not allowed in $actual""", tree.srcPos) + |since the additional capture set $leaked resulted from box conversion is not allowed in $actual""", pos) cs def adaptedType(resultBoxed: Boolean) = @@ -1459,11 +1387,11 @@ class CheckCaptures extends Recheck, SymTransformer: return actual // Disallow future addition of `cap` to `criticalSet`. criticalSet.disallowRootCapability: () => - report.error(msg, tree.srcPos) + report.error(msg, pos) if !insertBox then // we are unboxing //debugShowEnvs() - markFree(criticalSet, tree) + markFree(criticalSet, pos) end if // Compute the adapted type. @@ -1499,42 +1427,21 @@ class CheckCaptures extends Recheck, SymTransformer: case _ => widened case _ => widened - /** If actual is a capturing type T^C extending Mutable, and expected is an - * unboxed non-singleton value type not extending mutable, narrow the capture - * set `C` to `ro(C)`. - * The unboxed condition ensures that the expected type is not a type variable - * that's upper bounded by a read-only type. In this case it would not be sound - * to narrow to the read-only set, since that set can be propagated - * by the type variable instantiation. - */ - private def improveReadOnly(actual: Type, expected: Type)(using Context): Type = actual match - case actual @ CapturingType(parent, refs) - if parent.derivesFrom(defn.Caps_Mutable) - && expected.isValueType - && !expected.isMutableType - && !expected.isSingleton - && !expected.isBoxedCapturing => - actual.derivedCapturingType(parent, refs.readOnly) - case _ => - actual - /** Adapt `actual` type to `expected` type. This involves: * - narrow toplevel captures of `x`'s underlying type to `{x}` according to CC's VAR rule * - narrow nested captures of `x`'s underlying type to `{x*}` * - do box adaptation */ - def adapt(actual: Type, expected: Type, tree: Tree, boxErrors: BoxErrors)(using Context): Type = + def adapt(actual: Type, expected: Type, pos: SrcPos, boxErrors: BoxErrors)(using Context): Type = if expected == LhsProto || expected.isSingleton && actual.isSingleton then actual else - val improvedVAR = improveCaptures(actual.widen.dealiasKeepAnnots, actual) - val improved = improveReadOnly(improvedVAR, expected) + val widened = improveCaptures(actual.widen.dealiasKeepAnnots, actual) val adapted = adaptBoxed( - improved.withReachCaptures(actual), expected, tree, + widened.withReachCaptures(actual), expected, pos, covariant = true, alwaysConst = false, boxErrors) - if adapted eq improvedVAR // no .rd improvement, no box-adaptation - then actual // might as well use actual instead of improved widened - else adapted.showing(i"adapt $actual vs $expected = $adapted", capt) + if adapted eq widened then actual + else adapted.showing(i"adapt boxed $actual vs $expected = $adapted", capt) end adapt // ---- Unit-level rechecking ------------------------------------------- @@ -1545,19 +1452,19 @@ class CheckCaptures extends Recheck, SymTransformer: * But maybe we can then elide the check during the RefChecks phase under captureChecking? */ def checkOverrides = new TreeTraverser: - class OverridingPairsCheckerCC(clazz: ClassSymbol, self: Type, tree: Tree)(using Context) extends OverridingPairsChecker(clazz, self): + class OverridingPairsCheckerCC(clazz: ClassSymbol, self: Type, srcPos: SrcPos)(using Context) extends OverridingPairsChecker(clazz, self): /** Check subtype with box adaptation. * This function is passed to RefChecks to check the compatibility of overriding pairs. * @param sym symbol of the field definition that is being checked */ override def checkSubType(actual: Type, expected: Type)(using Context): Boolean = - val expected1 = alignDependentFunction(addOuterRefs(expected, actual, tree.srcPos), actual.stripCapturing) + val expected1 = alignDependentFunction(addOuterRefs(expected, actual, srcPos), actual.stripCapturing) val actual1 = val saved = curEnv try curEnv = Env(clazz, EnvKind.NestedInOwner, capturedVars(clazz), outer0 = curEnv) val adapted = - adaptBoxed(actual, expected1, tree, covariant = true, alwaysConst = true, null) + adaptBoxed(actual, expected1, srcPos, covariant = true, alwaysConst = true, null) actual match case _: MethodType => // We remove the capture set resulted from box adaptation for method types, @@ -1575,23 +1482,20 @@ class CheckCaptures extends Recheck, SymTransformer: override def checkInheritedTraitParameters: Boolean = false - /** Check that overrides don't change the @use or @consume status of their parameters */ + /** Check that overrides don't change the @use status of their parameters */ override def additionalChecks(member: Symbol, other: Symbol)(using Context): Unit = for (params1, params2) <- member.rawParamss.lazyZip(other.rawParamss) (param1, param2) <- params1.lazyZip(params2) do - def checkAnnot(cls: ClassSymbol) = - if param1.hasAnnotation(cls) != param2.hasAnnotation(cls) then - report.error( - OverrideError( - i"has a parameter ${param1.name} with different @${cls.name} status than the corresponding parameter in the overridden definition", - self, member, other, self.memberInfo(member), self.memberInfo(other) - ), - if member.owner == clazz then member.srcPos else clazz.srcPos) - - checkAnnot(defn.UseAnnot) - checkAnnot(defn.ConsumeAnnot) + if param1.hasAnnotation(defn.UseAnnot) != param2.hasAnnotation(defn.UseAnnot) then + report.error( + OverrideError( + i"has a parameter ${param1.name} with different @use status than the corresponding parameter in the overridden definition", + self, member, other, self.memberInfo(member), self.memberInfo(other) + ), + if member.owner == clazz then member.srcPos else clazz.srcPos + ) end OverridingPairsCheckerCC def traverse(t: Tree)(using Context) = @@ -1622,7 +1526,7 @@ class CheckCaptures extends Recheck, SymTransformer: def traverse(tree: Tree)(using Context) = tree match case id: Ident => val sym = id.symbol - if sym.isMutableVar && sym.owner.isTerm then + if sym.is(Mutable, butNot = Method) && sym.owner.isTerm then val enclMeth = ctx.owner.enclosingMethod if sym.enclosingMethod != enclMeth then capturedBy(sym) = enclMeth @@ -1697,7 +1601,7 @@ class CheckCaptures extends Recheck, SymTransformer: selfType match case CapturingType(_, refs: CaptureSet.Var) if !root.isEffectivelySealed - && !refs.isUniversal + && !refs.elems.exists(_.isRootCapability) && !root.matchesExplicitRefsInBaseClass(refs) => // Forbid inferred self types unless they are already implied by an explicit @@ -1752,7 +1656,7 @@ class CheckCaptures extends Recheck, SymTransformer: val widened = ref.captureSetOfInfo val added = widened.filter(isAllowed(_)) capt.println(i"heal $ref in $cs by widening to $added") - if !added.subCaptures(cs).isOK then + if !added.subCaptures(cs, frozen = false).isOK then val location = if meth.exists then i" of ${meth.showLocated}" else "" val paramInfo = if ref.paramName.info.kind.isInstanceOf[UniqueNameKind] @@ -1839,7 +1743,6 @@ class CheckCaptures extends Recheck, SymTransformer: end checker checker.traverse(unit)(using ctx.withOwner(defn.RootClass)) - if ccConfig.useSepChecks then SepCheck(this).traverse(unit) if !ctx.reporter.errorsReported then // We dont report errors here if previous errors were reported, because other // errors often result in bad applied types, but flagging these bad types gives diff --git a/compiler/src/dotty/tools/dotc/cc/Existential.scala b/compiler/src/dotty/tools/dotc/cc/Existential.scala index f115adfa6421..ea979e0b9f7f 100644 --- a/compiler/src/dotty/tools/dotc/cc/Existential.scala +++ b/compiler/src/dotty/tools/dotc/cc/Existential.scala @@ -4,6 +4,7 @@ package cc import core.* import Types.*, Symbols.*, Contexts.*, Annotations.*, Flags.* +import CaptureSet.IdempotentCaptRefMap import StdNames.nme import ast.tpd.* import Decorators.* @@ -242,21 +243,29 @@ object Existential: case _ => core - /** Map top-level existentials to `Fresh.Cap`. */ + /** Map top-level existentials to `cap`. Do the same for existentials + * in function results if all preceding arguments are known to be always pure. + */ def toCap(tp: Type)(using Context): Type = tp.dealiasKeepAnnots match case Existential(boundVar, unpacked) => - unpacked.substParam(boundVar, Fresh.Cap()) + val transformed = unpacked.substParam(boundVar, defn.captureRoot.termRef) + transformed match + case FunctionOrMethod(args, res @ Existential(_, _)) + if args.forall(_.isAlwaysPure) => + transformed.derivedFunctionOrMethod(args, toCap(res)) + case _ => + transformed case tp1 @ CapturingType(parent, refs) => tp1.derivedCapturingType(toCap(parent), refs) case tp1 @ AnnotatedType(parent, ann) => tp1.derivedAnnotatedType(toCap(parent), ann) case _ => tp - /** Map existentials at the top-level and in all nested result types to `Fresh.Cap` + /** Map existentials at the top-level and in all nested result types to `cap` */ def toCapDeeply(tp: Type)(using Context): Type = tp.dealiasKeepAnnots match case Existential(boundVar, unpacked) => - toCapDeeply(unpacked.substParam(boundVar, Fresh.Cap())) + toCapDeeply(unpacked.substParam(boundVar, defn.captureRoot.termRef)) case tp1 @ FunctionOrMethod(args, res) => val tp2 = tp1.derivedFunctionOrMethod(args, toCapDeeply(res)) if tp2 ne tp1 then tp2 else tp @@ -273,7 +282,7 @@ object Existential: case AppliedType(tycon, _) => !defn.isFunctionSymbol(tycon.typeSymbol) case _ => false - /** Replace all occurrences of `cap` (or fresh) in parts of this type by an existentially bound + /** Replace all occurrences of `cap` in parts of this type by an existentially bound * variable. If there are such occurrences, or there might be in the future due to embedded * capture set variables, create an existential with the variable wrapping the type. * Stop at function or method types since these have been mapped before. @@ -294,7 +303,7 @@ object Existential: class Wrap(boundVar: TermParamRef) extends CapMap: def apply(t: Type) = t match - case t: CaptureRef if t.isCapOrFresh => // !!! we should map different fresh refs to different existentials + case t: TermRef if t.isRootCapability => if variance > 0 then needsWrap = true boundVar @@ -317,9 +326,8 @@ object Existential: //.showing(i"mapcap $t = $result") lazy val inverse = new BiTypeMap: - lazy val freshCap = Fresh.Cap() def apply(t: Type) = t match - case t: TermParamRef if t eq boundVar => freshCap + case t: TermParamRef if t eq boundVar => defn.captureRoot.termRef case _ => mapOver(t) def inverse = Wrap.this override def toString = "Wrap.inverse" diff --git a/compiler/src/dotty/tools/dotc/cc/Fresh.scala b/compiler/src/dotty/tools/dotc/cc/Fresh.scala deleted file mode 100644 index 48b20f18f027..000000000000 --- a/compiler/src/dotty/tools/dotc/cc/Fresh.scala +++ /dev/null @@ -1,142 +0,0 @@ -package dotty.tools -package dotc -package cc - -import core.* -import Types.*, Symbols.*, Contexts.*, Annotations.*, Flags.* -import StdNames.nme -import ast.tpd.* -import Decorators.* -import typer.ErrorReporting.errorType -import Names.TermName -import NameKinds.ExistentialBinderName -import NameOps.isImpureFunction -import reporting.Message -import util.SimpleIdentitySet.empty -import CaptureSet.{Refs, emptyRefs, NarrowingCapabilityMap} -import dotty.tools.dotc.util.SimpleIdentitySet - -/** A module for handling Fresh types. Fresh.Cap instances are top type that keep - * track of what they hide when capabilities get widened by subsumption to fresh. - * The module implements operations to convert between regular caps.cap and - * Fresh.Cap instances. Fresh.Cap is encoded as `caps.cap @freshCapability(...)` where - * `freshCapability(...)` is a special kind of annotation of type `Fresh.Annot` - * that contains a hidden set. - */ -object Fresh: - - /** The annotation of a Fresh.Cap instance */ - case class Annot(hidden: CaptureSet.HiddenSet) extends Annotation: - override def symbol(using Context) = defn.FreshCapabilityAnnot - override def tree(using Context) = New(symbol.typeRef, Nil) - override def derivedAnnotation(tree: Tree)(using Context): Annotation = this - - override def hash: Int = hidden.hashCode - override def eql(that: Annotation) = that match - case Annot(hidden) => this.hidden eq hidden - case _ => false - end Annot - - /** The initial elements (either 0 or 1) of a hidden set created for given `owner`. - * If owner `x` is a trackable this is `x*` if reach` is true, or `x` otherwise. - */ - private def ownerToHidden(owner: Symbol, reach: Boolean)(using Context): Refs = - val ref = owner.termRef - if reach then - if ref.isTrackableRef then SimpleIdentitySet(ref.reach) else emptyRefs - else - if ref.isTracked then SimpleIdentitySet(ref) else emptyRefs - - /** An extractor for "fresh" capabilities */ - object Cap: - - def apply(initialHidden: Refs = emptyRefs)(using Context): CaptureRef = - if ccConfig.useSepChecks then - AnnotatedType(defn.captureRoot.termRef, Annot(CaptureSet.HiddenSet(initialHidden))) - else - defn.captureRoot.termRef - - def apply(owner: Symbol, reach: Boolean)(using Context): CaptureRef = - apply(ownerToHidden(owner, reach)) - - def apply(owner: Symbol)(using Context): CaptureRef = - apply(ownerToHidden(owner, reach = false)) - - def unapply(tp: AnnotatedType)(using Context): Option[CaptureSet.HiddenSet] = tp.annot match - case Annot(hidden) => Some(hidden) - case _ => None - end Cap - - /** Map each occurrence of cap to a different Sep.Cap instance */ - class FromCap(owner: Symbol)(using Context) extends BiTypeMap, FollowAliasesMap: - thisMap => - - private var reach = false - - override def apply(t: Type) = - if variance <= 0 then t - else t match - case t: CaptureRef if t.isCap => - Cap(ownerToHidden(owner, reach)) - case t @ CapturingType(_, refs) => - val savedReach = reach - if t.isBoxed then reach = true - try mapOver(t) finally reach = savedReach - case t @ AnnotatedType(parent, ann) => - val parent1 = this(parent) - if ann.symbol.isRetains && ann.tree.toCaptureSet.containsCap then - this(CapturingType(parent1, ann.tree.toCaptureSet)) - else - t.derivedAnnotatedType(parent1, ann) - case _ => - mapFollowingAliases(t) - - override def toString = "CapToFresh" - - lazy val inverse: BiTypeMap & FollowAliasesMap = new BiTypeMap with FollowAliasesMap: - def apply(t: Type): Type = t match - case t @ Cap(_) => defn.captureRoot.termRef - case t @ CapturingType(_, refs) => mapOver(t) - case _ => mapFollowingAliases(t) - - def inverse = thisMap - override def toString = thisMap.toString + ".inverse" - - end FromCap - - /** Maps cap to fresh */ - def fromCap(tp: Type, owner: Symbol = NoSymbol)(using Context): Type = - if ccConfig.useSepChecks then FromCap(owner)(tp) else tp - - /** Maps fresh to cap */ - def toCap(tp: Type)(using Context): Type = - if ccConfig.useSepChecks then FromCap(NoSymbol).inverse(tp) else tp - - /** If `refs` contains an occurrence of `cap` or `cap.rd`, the current context - * with an added property PrintFresh. This addition causes all occurrences of - * `Fresh.Cap` to be printed as `fresh` instead of `cap`, so that one avoids - * confusion in error messages. - */ - def printContext(refs: (Type | CaptureSet)*)(using Context): Context = - def hasCap = new TypeAccumulator[Boolean]: - def apply(x: Boolean, t: Type) = - x || t.dealiasKeepAnnots.match - case Fresh.Cap(_) => false - case t: TermRef => t.isCap || this(x, t.widen) - case x: ThisType => false - case _ => foldOver(x, t) - def containsFresh(x: Type | CaptureSet): Boolean = x match - case tp: Type => - hasCap(false, tp) - case refs: CaptureSet => - refs.elems.exists(_.stripReadOnly.isCap) - - if refs.exists(containsFresh) then ctx.withProperty(PrintFresh, Some(())) - else ctx - end printContext -end Fresh - - - - - diff --git a/compiler/src/dotty/tools/dotc/cc/SepCheck.scala b/compiler/src/dotty/tools/dotc/cc/SepCheck.scala deleted file mode 100644 index b2318eb798dd..000000000000 --- a/compiler/src/dotty/tools/dotc/cc/SepCheck.scala +++ /dev/null @@ -1,849 +0,0 @@ -package dotty.tools -package dotc -package cc -import ast.tpd -import collection.mutable - -import core.* -import Symbols.*, Types.*, Flags.* -import Contexts.*, Names.*, Flags.*, Symbols.*, Decorators.* -import CaptureSet.{Refs, emptyRefs, HiddenSet} -import config.Printers.capt -import StdNames.nme -import util.{SimpleIdentitySet, EqHashMap, SrcPos} -import tpd.* -import reflect.ClassTag - -/** The separation checker is a tree traverser that is run after capture checking. - * It checks tree nodes for various separation conditions, explained in the - * methods below. Rough summary: - * - * - Hidden sets of arguments must not be referred to in the same application - * - Hidden sets of (result-) types must not be referred to alter in the same scope. - * - Returned hidden sets can only refer to @consume parameters. - * - If returned hidden sets refer to an encloding this, the reference must be - * from a @consume method. - * - Consumed entities cannot be used subsequently. - * - Entitites cannot be consumed in a loop. - */ -object SepCheck: - - /** Enumerates kinds of captures encountered so far */ - enum Captures: - case None - case Explicit // one or more explicitly declared captures - case Hidden // exacttly one hidden captures - case NeedsCheck // one hidden capture and one other capture (hidden or declared) - - def add(that: Captures): Captures = - if this == None then that - else if that == None then this - else if this == Explicit && that == Explicit then Explicit - else NeedsCheck - end Captures - - /** The role in which a checked type appears, used for composing error messages */ - enum TypeRole: - case Result(sym: Symbol, inferred: Boolean) - case Argument(arg: Tree) - case Qualifier(qual: Tree, meth: Symbol) - - /** If this is a Result tole, the associated symbol, otherwise NoSymbol */ - def dclSym = this match - case Result(sym, _) => sym - case _ => NoSymbol - - /** A textual description of this role */ - def description(using Context): String = this match - case Result(sym, inferred) => - def inferredStr = if inferred then " inferred" else "" - def resultStr = if sym.info.isInstanceOf[MethodicType] then " result" else "" - i"$sym's$inferredStr$resultStr type" - case TypeRole.Argument(_) => - "the argument's adapted type" - case TypeRole.Qualifier(_, meth) => - i"the type of the prefix to a call of $meth" - end TypeRole - - /** A class for segmented sets of consumed references. - * References are associated with the source positions where they first appeared. - * References are compared with `eq`. - */ - abstract class ConsumedSet: - /** The references in the set. The array should be treated as immutable in client code */ - def refs: Array[CaptureRef] - - /** The associated source positoons. The array should be treated as immutable in client code */ - def locs: Array[SrcPos] - - /** The number of references in the set */ - def size: Int - - def toMap: Map[CaptureRef, SrcPos] = refs.take(size).zip(locs).toMap - - def show(using Context) = - s"[${toMap.map((ref, loc) => i"$ref -> $loc").toList}]" - end ConsumedSet - - /** A fixed consumed set consisting of the given references `refs` and - * associated source positions `locs` - */ - class ConstConsumedSet(val refs: Array[CaptureRef], val locs: Array[SrcPos]) extends ConsumedSet: - def size = refs.size - - /** A mutable consumed set, which is initially empty */ - class MutConsumedSet extends ConsumedSet: - var refs: Array[CaptureRef] = new Array(4) - var locs: Array[SrcPos] = new Array(4) - var size = 0 - - private def double[T <: AnyRef : ClassTag](xs: Array[T]): Array[T] = - val xs1 = new Array[T](xs.length * 2) - xs.copyToArray(xs1) - xs1 - - private def ensureCapacity(added: Int): Unit = - if size + added > refs.length then - refs = double(refs) - locs = double(locs) - - /** If `ref` is in the set, its associated source position, otherwise `null` */ - def get(ref: CaptureRef): SrcPos | Null = - var i = 0 - while i < size && (refs(i) ne ref) do i += 1 - if i < size then locs(i) else null - - /** If `ref` is not yet in the set, add it with given source position */ - def put(ref: CaptureRef, loc: SrcPos): Unit = - if get(ref) == null then - ensureCapacity(1) - refs(size) = ref - locs(size) = loc - size += 1 - - /** Add all references with their associated positions from `that` which - * are not yet in the set. - */ - def ++= (that: ConsumedSet): Unit = - for i <- 0 until that.size do put(that.refs(i), that.locs(i)) - - /** Run `op` and return any new references it created in a separate `ConsumedSet`. - * The current mutable set is reset to its state before `op` was run. - */ - def segment(op: => Unit): ConsumedSet = - val start = size - try - op - if size == start then EmptyConsumedSet - else ConstConsumedSet(refs.slice(start, size), locs.slice(start, size)) - finally - size = start - end MutConsumedSet - - val EmptyConsumedSet = ConstConsumedSet(Array(), Array()) - -class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: - import checker.* - import SepCheck.* - - /** The set of capabilities that are hidden by a polymorphic result type - * of some previous definition. - */ - private var defsShadow: Refs = emptyRefs - - /** A map from definitions to their internal result types. - * Populated during separation checking traversal. - */ - private val resultType = EqHashMap[Symbol, Type]() - - /** The previous val or def definitions encountered during separation checking. - * These all enclose and precede the current traversal node. - */ - private var previousDefs: List[mutable.ListBuffer[ValOrDefDef]] = Nil - - /** The set of references that were consumed so far in the current method */ - private var consumed: MutConsumedSet = MutConsumedSet() - - /** Run `op`` with a fresh, initially empty consumed set. */ - private def withFreshConsumed(op: => Unit): Unit = - val saved = consumed - consumed = MutConsumedSet() - op - consumed = saved - - /** Infos aboput Labeled expressions enclosing the current traversal point. - * For each labeled expression, it's label name, and a list buffer containing - * all consumed sets of return expressions referring to that label. - */ - private var openLabeled: List[(Name, mutable.ListBuffer[ConsumedSet])] = Nil - - extension (refs: Refs) - - /** The footprint of a set of references `refs` the smallest set `F` such that - * - no maximal capability is in `F` - * - all non-maximal capabilities in `refs` are in `F` - * - if `f in F` then the footprint of `f`'s info is also in `F`. - */ - private def footprint(using Context): Refs = - def recur(elems: Refs, newElems: List[CaptureRef]): Refs = newElems match - case newElem :: newElems1 => - val superElems = newElem.captureSetOfInfo.elems.filter: superElem => - !superElem.isMaxCapability && !elems.contains(superElem) - recur(elems ++ superElems, newElems1 ++ superElems.toList) - case Nil => elems - val elems: Refs = refs.filter(!_.isMaxCapability) - recur(elems, elems.toList) - - /** The overlap of two footprint sets F1 and F2. This contains all exclusive references `r` - * such that one of the following is true: - * 1. - * - one of the sets contains `r` - * - the other contains a capability `s` or `s.rd` where `s` _covers_ `r` - * 2. - * - one of the sets contains `r.rd` - * - the other contains a capability `s` where `s` _covers_ `r` - * - * A capability `s` covers `r` if `r` can be seen as a path extension of `s`. E.g. - * if `s = x.a` and `r = x.a.b.c` then `s` covers `a`. - */ - private def overlapWith(other: Refs)(using Context): Refs = - val refs1 = refs - val refs2 = other - - /** Exclusive capabilities in refs1 that are covered by exclusive or - * stripped read-only capabilties in refs2 - * + stripped read-only capabilities in refs1 that are covered by an - * exclusive capability in refs2. - */ - def common(refs1: Refs, refs2: Refs) = - refs1.filter: ref => - ref.isExclusive && refs2.exists(_.stripReadOnly.covers(ref)) - ++ - refs1 - .filter: - case ReadOnlyCapability(ref @ TermRef(prefix: CaptureRef, _)) => - // We can get away testing only references with at least one field selection - // here since stripped readOnly references that equal a reference in refs2 - // are added by the first clause of the symmetric call to common. - !ref.isCap && refs2.exists(_.covers(prefix)) - case _ => - false - .map(_.stripReadOnly) - - common(refs, other) ++ common(other, refs) - end overlapWith - - /** The non-maximal elements hidden directly or indirectly by a maximal - * capability in `refs`. E g. if `R = {x, >}` then - * its hidden set is `{y, z}`. - */ - private def hidden(using Context): Refs = - val seen: util.EqHashSet[CaptureRef] = new util.EqHashSet - - def hiddenByElem(elem: CaptureRef): Refs = elem match - case Fresh.Cap(hcs) => hcs.elems.filter(!_.isRootCapability) ++ recur(hcs.elems) - case ReadOnlyCapability(ref1) => hiddenByElem(ref1).map(_.readOnly) - case _ => emptyRefs - - def recur(refs: Refs): Refs = - (emptyRefs /: refs): (elems, elem) => - if seen.add(elem) then elems ++ hiddenByElem(elem) else elems - - recur(refs) - end hidden - - /** Subtract all elements that are covered by some element in `others` from this set. */ - private def deduct(others: Refs)(using Context): Refs = - refs.filter: ref => - !others.exists(_.covers(ref)) - - /** Deduct the footprint of `sym` and `sym*` from `refs` */ - private def deductSym(sym: Symbol)(using Context): Refs = - val ref = sym.termRef - if ref.isTrackableRef then refs.deduct(CaptureSet(ref, ref.reach).elems.footprint) - else refs - - /** Deduct the footprint of all captures of trees in `deps` from `refs` */ - private def deductCapturesOf(deps: List[Tree])(using Context): Refs = - deps.foldLeft(refs): (refs, dep) => - refs.deduct(captures(dep).footprint) - end extension - - /** The deep capture set of an argument or prefix widened to the formal parameter, if - * the latter contains a cap. - */ - private def formalCaptures(arg: Tree)(using Context): Refs = - arg.formalType.orElse(arg.nuType).deepCaptureSet.elems - - /** The deep capture set if the type of `tree` */ - private def captures(tree: Tree)(using Context): Refs = - tree.nuType.deepCaptureSet.elems - - // ---- Error reporting TODO Once these are stabilized, move to messages ----- - - /** Report a separation failure in an application `fn(args)` - * @param fn the function - * @param args the flattened argument lists - * @param argIdx the index of the failing argument in `args`, starting at 0 - * @param overlap the overlap causing the failure - * @param hiddenInArg the hidxden set of the type of the failing argument - * @param footprints a sequence of partial footprints, and the index of the - * last argument they cover. - * @param deps cross argument dependencies: maps argument trees to - * those other arguments that where mentioned by coorresponding - * formal parameters. - */ - private def sepApplyError(fn: Tree, args: List[Tree], argIdx: Int, - overlap: Refs, hiddenInArg: Refs, footprints: List[(Refs, Int)], - deps: collection.Map[Tree, List[Tree]])(using Context): Unit = - val arg = args(argIdx) - def paramName(mt: Type, idx: Int): Option[Name] = mt match - case mt @ MethodType(pnames) => - if idx < pnames.length then Some(pnames(idx)) else paramName(mt.resType, idx - pnames.length) - case mt: PolyType => paramName(mt.resType, idx) - case _ => None - def formalName = paramName(fn.nuType.widen, argIdx) match - case Some(pname) => i"$pname " - case _ => "" - def whatStr = if overlap.size == 1 then "this capability is" else "these capabilities are" - def qualifier = methPart(fn) match - case Select(qual, _) => qual - case _ => EmptyTree - def isShowableMethod = fn.symbol.exists && !defn.isFunctionSymbol(fn.symbol.maybeOwner) - def funType = - if fn.symbol.exists && !qualifier.isEmpty then qualifier.nuType else fn.nuType - def funStr = - if isShowableMethod then i"${fn.symbol}: ${fn.symbol.info}" - else i"a function of type ${funType.widen}" - val clashIdx = footprints - .collect: - case (fp, idx) if !hiddenInArg.overlapWith(fp).isEmpty => idx - .head - def whereStr = clashIdx match - case 0 => "function prefix" - case 1 => "first argument " - case 2 => "second argument" - case 3 => "third argument " - case n => s"${n}th argument " - def clashTree = - if clashIdx == 0 then qualifier - else args(clashIdx - 1) - def clashTypeStr = - if clashIdx == 0 && !isShowableMethod then "" // we already mentioned the type in `funStr` - else i" with type ${clashTree.nuType}" - def clashCaptures = captures(clashTree) - def hiddenCaptures = formalCaptures(arg).hidden - def clashFootprint = clashCaptures.footprint - def hiddenFootprint = hiddenCaptures.footprint - def declaredFootprint = deps(arg).map(captures(_)).foldLeft(emptyRefs)(_ ++ _).footprint - def footprintOverlap = hiddenFootprint.overlapWith(clashFootprint).deduct(declaredFootprint) - report.error( - em"""Separation failure: argument of type ${arg.nuType} - |to $funStr - |corresponds to capture-polymorphic formal parameter ${formalName}of type ${arg.formalType} - |and captures ${CaptureSet(overlap)}, but $whatStr also passed separately - |in the ${whereStr.trim}$clashTypeStr. - | - | Capture set of $whereStr : ${CaptureSet(clashCaptures)} - | Hidden set of current argument : ${CaptureSet(hiddenCaptures)} - | Footprint of $whereStr : ${CaptureSet(clashFootprint)} - | Hidden footprint of current argument : ${CaptureSet(hiddenFootprint)} - | Declared footprint of current argument: ${CaptureSet(declaredFootprint)} - | Undeclared overlap of footprints : ${CaptureSet(footprintOverlap)}""", - arg.srcPos) - end sepApplyError - - /** Report a use/definition failure, where a previously hidden capability is - * used again. - * @param tree the tree where the capability is used - * @param used the footprint of all uses of `tree` - * @param globalOverlap the overlap between `used` and all capabilities hidden - * by previous definitions - */ - def sepUseError(tree: Tree, used: Refs, globalOverlap: Refs)(using Context): Unit = - val individualChecks = for mdefs <- previousDefs.iterator; mdef <- mdefs.iterator yield - val hiddenByDef = captures(mdef.tpt).hidden.footprint - val overlap = defUseOverlap(hiddenByDef, used, tree.symbol) - if !overlap.isEmpty then - def resultStr = if mdef.isInstanceOf[DefDef] then " result" else "" - report.error( - em"""Separation failure: Illegal access to ${CaptureSet(overlap)} which is hidden by the previous definition - |of ${mdef.symbol} with$resultStr type ${mdef.tpt.nuType}. - |This type hides capabilities ${CaptureSet(hiddenByDef)}""", - tree.srcPos) - true - else false - val clashes = individualChecks.filter(identity) - if clashes.hasNext then clashes.next // issues error as a side effect - else report.error( - em"""Separation failure: Illegal access to ${CaptureSet(globalOverlap)} which is hidden by some previous definitions - |No clashing definitions were found. This might point to an internal error.""", - tree.srcPos) - end sepUseError - - /** Report a failure where a previously consumed capability is used again, - * @param ref the capability that is used after being consumed - * @param loc the position where the capability was consumed - * @param pos the position where the capability was used again - */ - def consumeError(ref: CaptureRef, loc: SrcPos, pos: SrcPos)(using Context): Unit = - report.error( - em"""Separation failure: Illegal access to $ref, which was passed to a - |@consume parameter or was used as a prefix to a @consume method on line ${loc.line + 1} - |and therefore is no longer available.""", - pos) - - /** Report a failure where a capability is consumed in a loop. - * @param ref the capability - * @param loc the position where the capability was consumed - */ - def consumeInLoopError(ref: CaptureRef, pos: SrcPos)(using Context): Unit = - report.error( - em"""Separation failure: $ref appears in a loop, therefore it cannot - |be passed to a @consume parameter or be used as a prefix of a @consume method call.""", - pos) - - // ------------ Checks ----------------------------------------------------- - - /** Check separation between different arguments and between function - * prefix and arguments. A capability cannot be hidden by one of these arguments - * and also be either explicitly referenced or hidden by the prefix or another - * argument. "Hidden" means: the capability is in the deep capture set of the - * argument and appears in the hidden set of the corresponding (capture-polymorphic) - * formal parameter. Howeber, we do allow explicit references to a hidden - * capability in later arguments, if the corresponding formal parameter mentions - * the parameter where the capability was hidden. For instance in - * - * def seq(x: () => Unit; y ->{cap, x} Unit): Unit - * def f: () ->{io} Unit - * - * we do allow `seq(f, f)` even though `{f, io}` is in the hidden set of the - * first parameter `x`, since the second parameter explicitly mentions `x` in - * its capture set. - * - * Also check separation via checkType within individual arguments widened to their - * formal paramater types. - * - * @param fn the applied function - * @param args the flattened argument lists - * @param deps cross argument dependencies: maps argument trees to - * those other arguments that where mentioned by coorresponding - * formal parameters. - */ - private def checkApply(fn: Tree, args: List[Tree], deps: collection.Map[Tree, List[Tree]])(using Context): Unit = - val fnCaptures = methPart(fn) match - case Select(qual, _) => qual.nuType.captureSet - case _ => CaptureSet.empty - capt.println(i"check separate $fn($args), fnCaptures = $fnCaptures, argCaptures = ${args.map(arg => CaptureSet(formalCaptures(arg)))}, deps = ${deps.toList}") - var footprint = fnCaptures.elems.footprint - val footprints = mutable.ListBuffer[(Refs, Int)]((footprint, 0)) - val indexedArgs = args.zipWithIndex - - // First, compute all footprints of arguments to monomorphic pararameters, - // separately in `footprints`, and their union in `footprint`. - for (arg, idx) <- indexedArgs do - if !arg.needsSepCheck then - footprint = footprint ++ captures(arg).footprint.deductCapturesOf(deps(arg)) - footprints += ((footprint, idx + 1)) - - // Then, for each argument to a polymorphic parameter: - // - check formal type via checkType - // - check that hidden set of argument does not overlap with current footprint - // - add footprint of the deep capture set of actual type of argument - // to global footprint(s) - for (arg, idx) <- indexedArgs do - if arg.needsSepCheck then - val ac = formalCaptures(arg) - checkType(arg.formalType, arg.srcPos, TypeRole.Argument(arg)) - val hiddenInArg = ac.hidden.footprint - //println(i"check sep $arg: $ac, footprint so far = $footprint, hidden = $hiddenInArg") - val overlap = hiddenInArg.overlapWith(footprint).deductCapturesOf(deps(arg)) - if !overlap.isEmpty then - sepApplyError(fn, args, idx, overlap, hiddenInArg, footprints.toList, deps) - footprint ++= captures(arg).footprint - footprints += ((footprint, idx + 1)) - end checkApply - - /** The def/use overlap between the references `hiddenByDef` hidden by - * a previous definition and the `used` set of a tree with symbol `sym`. - * Deduct any capabilities referred to or hidden by the (result-) type of `sym`. - */ - def defUseOverlap(hiddenByDef: Refs, used: Refs, sym: Symbol)(using Context): Refs = - val overlap = hiddenByDef.overlapWith(used) - resultType.get(sym) match - case Some(tp) if !overlap.isEmpty => - val declared = tp.captureSet.elems - overlap.deduct(declared.footprint).deduct(declared.hidden.footprint) - case _ => - overlap - - /** 1. Check that the capabilities used at `tree` don't overlap with - * capabilities hidden by a previous definition. - * 2. Also check that none of the used capabilities was consumed before. - */ - def checkUse(tree: Tree)(using Context) = - val used = tree.markedFree - if !used.elems.isEmpty then - val usedFootprint = used.elems.footprint - val overlap = defUseOverlap(defsShadow, usedFootprint, tree.symbol) - if !overlap.isEmpty then - sepUseError(tree, usedFootprint, overlap) - for ref <- used.elems do - val pos = consumed.get(ref) - if pos != null then consumeError(ref, pos, tree.srcPos) - - /** If `tp` denotes some version of a singleton type `x.type` the set `{x}` - * otherwise the empty set. - */ - def explicitRefs(tp: Type): Refs = tp match - case tp: (TermRef | ThisType) => SimpleIdentitySet(tp) - case AnnotatedType(parent, _) => explicitRefs(parent) - case AndType(tp1, tp2) => explicitRefs(tp1) ++ explicitRefs(tp2) - case OrType(tp1, tp2) => explicitRefs(tp1) ** explicitRefs(tp2) - case _ => emptyRefs - - /** Deduct some elements from `refs` according to the role of the checked type `tpe`: - * - If the the type apears as a (result-) type of a definition of `x`, deduct - * `x` and `x*`. - * - If `tpe` is morally a singleton type deduct it as well. - */ - def prune(refs: Refs, tpe: Type, role: TypeRole)(using Context): Refs = - refs.deductSym(role.dclSym).deduct(explicitRefs(tpe)) - - /** Check validity of consumed references `refsToCheck`. The references are consumed - * because they are hidden in a Fresh.Cap result type or they are referred - * to in an argument to a @consume parameter or in a prefix of a @consume method -- - * which one applies is determined by the role parameter. - * - * This entails the following checks: - * - The reference must be defined in the same as method or class as - * the access. - * - If the reference is to a term parameter, that parameter must be - * marked as @consume as well. - * - If the reference is to a this type of the enclosing class, the - * access must be in a @consume method. - * - * References that extend SharedCapability are excluded from checking. - * As a side effect, add all checked references with the given position `pos` - * to the global `consumed` map. - * - * @param refsToCheck the referencves to check - * @param tpe the type containing those references - * @param role the role in which the type apears - * @param descr a textual description of the type and its relationship with the checked reference - * @param pos position for error reporting - */ - def checkConsumedRefs(refsToCheck: Refs, tpe: Type, role: TypeRole, descr: => String, pos: SrcPos)(using Context) = - val badParams = mutable.ListBuffer[Symbol]() - def currentOwner = role.dclSym.orElse(ctx.owner) - for hiddenRef <- prune(refsToCheck, tpe, role) do - val proot = hiddenRef.pathRootOrShared - if !proot.widen.derivesFromSharedCapability then - proot match - case ref: TermRef => - val refSym = ref.symbol - if currentOwner.enclosingMethodOrClass.isProperlyContainedIn(refSym.maybeOwner.enclosingMethodOrClass) then - report.error(em"""Separation failure: $descr non-local $refSym""", pos) - else if refSym.is(TermParam) - && !refSym.hasAnnotation(defn.ConsumeAnnot) - && currentOwner.isContainedIn(refSym.owner) - then - badParams += refSym - case ref: ThisType => - val encl = currentOwner.enclosingMethodOrClass - if encl.isProperlyContainedIn(ref.cls) - && !encl.is(Synthetic) - && !encl.hasAnnotation(defn.ConsumeAnnot) - then - report.error( - em"""Separation failure: $descr non-local this of class ${ref.cls}. - |The access must be in a @consume method to allow this.""", - pos) - case _ => - - if badParams.nonEmpty then - def paramsStr(params: List[Symbol]): String = (params: @unchecked) match - case p :: Nil => i"${p.name}" - case p :: p2 :: Nil => i"${p.name} and ${p2.name}" - case p :: ps => i"${p.name}, ${paramsStr(ps)}" - val (pluralS, singleS) = if badParams.tail.isEmpty then ("", "s") else ("s", "") - report.error( - em"""Separation failure: $descr parameter$pluralS ${paramsStr(badParams.toList)}. - |The parameter$pluralS need$singleS to be annotated with @consume to allow this.""", - pos) - - role match - case _: TypeRole.Argument | _: TypeRole.Qualifier => - for ref <- refsToCheck do - if !ref.pathRootOrShared.derivesFromSharedCapability then - consumed.put(ref, pos) - case _ => - end checkConsumedRefs - - /** Check separation conditions of type `tpe` that appears in `role`. - * 1. Check that the parts of type `tpe` are mutually separated, as defined in - * `checkParts` below. - * 2. Check that validity of all references consumed by the type as defined in - * `checkLegalRefs` below - */ - def checkType(tpe: Type, pos: SrcPos, role: TypeRole)(using Context): Unit = - - /** Check that the parts of type `tpe` are mutually separated. - * This means that references hidden in some part of the type may not - * be explicitly referenced or hidden in some other part. - */ - def checkParts(parts: List[Type]): Unit = - var footprint: Refs = emptyRefs - var hiddenSet: Refs = emptyRefs - var checked = 0 - for part <- parts do - - /** Report an error if `current` and `next` overlap. - * @param current the footprint or hidden set seen so far - * @param next the footprint or hidden set of the next part - * @param mapRefs a function over the capture set elements of the next part - * that returns the references of the same kind as `current` - * (i.e. the part's footprint or hidden set) - * @param prevRel a verbal description of current ("references or "hides") - * @param nextRel a verbal descriiption of next - */ - def checkSep(current: Refs, next: Refs, mapRefs: Refs => Refs, prevRel: String, nextRel: String): Unit = - val globalOverlap = current.overlapWith(next) - if !globalOverlap.isEmpty then - val (prevStr, prevRefs, overlap) = parts.iterator.take(checked) - .map: prev => - val prevRefs = prune(mapRefs(prev.deepCaptureSet.elems).footprint, tpe, role) - (i", $prev , ", prevRefs, prevRefs.overlapWith(next)) - .dropWhile(_._3.isEmpty) - .nextOption - .getOrElse(("", current, globalOverlap)) - report.error( - em"""Separation failure in ${role.description} $tpe. - |One part, $part , $nextRel ${CaptureSet(next)}. - |A previous part$prevStr $prevRel ${CaptureSet(prevRefs)}. - |The two sets overlap at ${CaptureSet(overlap)}.""", - pos) - - val partRefs = part.deepCaptureSet.elems - val partFootprint = prune(partRefs.footprint, tpe, role) - val partHidden = prune(partRefs.hidden.footprint, tpe, role).deduct(partFootprint) - - checkSep(footprint, partHidden, identity, "references", "hides") - checkSep(hiddenSet, partHidden, _.hidden, "also hides", "hides") - checkSep(hiddenSet, partFootprint, _.hidden, "hides", "references") - - footprint ++= partFootprint - hiddenSet ++= partHidden - checked += 1 - end for - end checkParts - - /** A traverser that collects part lists to check for separation conditions. - * The accumulator of type `Captures` indicates what kind of captures were - * encountered in previous parts. - */ - object traverse extends TypeAccumulator[Captures]: - - /** A stack of part lists to check. We maintain this since immediately - * checking parts when traversing the type would check innermost to outermost. - * But we want to check outermost parts first since this prioritizes errors - * that are more obvious. - */ - var toCheck: List[List[Type]] = Nil - - private val seen = util.HashSet[Symbol]() - - def apply(c: Captures, t: Type) = - if variance < 0 then c - else - val t1 = t.dealias - t1 match - case t @ AppliedType(tycon, args) => - val c1 = foldOver(Captures.None, t) - if c1 == Captures.NeedsCheck then - toCheck = (tycon :: args) :: toCheck - c.add(c1) - case t @ CapturingType(parent, cs) => - val c1 = this(c, parent) - if cs.containsRootCapability then c1.add(Captures.Hidden) - else if !cs.elems.isEmpty then c1.add(Captures.Explicit) - else c1 - case t: TypeRef if t.symbol.isAbstractOrParamType => - if seen.contains(t.symbol) then c - else - seen += t.symbol - apply(apply(c, t.prefix), t.info.bounds.hi) - case t => - foldOver(c, t) - - /** If `tpe` appears as a (result-) type of a definition, treat its - * hidden set minus its explicitly declared footprint as consumed. - * If `tpe` appears as an argument to a @consume parameter, treat - * its footprint as consumed. - */ - def checkLegalRefs() = role match - case TypeRole.Result(sym, _) => - if !sym.isAnonymousFunction // we don't check return types of anonymous functions - && !sym.is(Case) // We don't check so far binders in patterns since they - // have inferred universal types. TODO come back to this; - // either infer more precise types for such binders or - // "see through them" when we look at hidden sets. - then - val refs = tpe.deepCaptureSet.elems - val toCheck = refs.hidden.footprint.deduct(refs.footprint) - checkConsumedRefs(toCheck, tpe, role, i"${role.description} $tpe hides", pos) - case TypeRole.Argument(arg) => - if tpe.hasAnnotation(defn.ConsumeAnnot) then - val capts = captures(arg).footprint - checkConsumedRefs(capts, tpe, role, i"argument to @consume parameter with type ${arg.nuType} refers to", pos) - case _ => - - if !tpe.hasAnnotation(defn.UntrackedCapturesAnnot) then - traverse(Captures.None, tpe) - traverse.toCheck.foreach(checkParts) - checkLegalRefs() - end checkType - - /** Check the (result-) type of a definition of symbol `sym` */ - def checkType(tpt: Tree, sym: Symbol)(using Context): Unit = - checkType(tpt.nuType, tpt.srcPos, - TypeRole.Result(sym, inferred = tpt.isInstanceOf[InferredTypeTree])) - - /** The list of all individual method types making up some potentially - * curried method type. - */ - private def collectMethodTypes(tp: Type): List[TermLambda] = tp match - case tp: MethodType => tp :: collectMethodTypes(tp.resType) - case tp: PolyType => collectMethodTypes(tp.resType) - case _ => Nil - - /** The inter-parameter dependencies of the function reference `fn` applied - * to the argument lists `argss`. For instance, if `f` has type - * - * f(x: A, y: B^{cap, x}, z: C^{x, y}): D - * - * then the dependencies of an application `f(a, b)` is a map that takes - * `b` to `List(a)` and `c` to `List(a, b)`. - */ - private def dependencies(fn: Tree, argss: List[List[Tree]])(using Context): collection.Map[Tree, List[Tree]] = - val mtpe = - if fn.symbol.exists then fn.symbol.info - else fn.tpe.widen // happens for PolyFunction applies - val mtps = collectMethodTypes(mtpe) - assert(mtps.hasSameLengthAs(argss), i"diff for $fn: ${fn.symbol} /// $mtps /// $argss") - val mtpsWithArgs = mtps.zip(argss) - val argMap = mtpsWithArgs.toMap - val deps = mutable.HashMap[Tree, List[Tree]]().withDefaultValue(Nil) - for - (mt, args) <- mtpsWithArgs - (formal, arg) <- mt.paramInfos.zip(args) - dep <- formal.captureSet.elems.toList - do - val referred = dep.stripReach match - case dep: TermParamRef => - argMap(dep.binder)(dep.paramNum) :: Nil - case dep: ThisType if dep.cls == fn.symbol.owner => - val Select(qual, _) = fn: @unchecked - qual :: Nil - case _ => - Nil - deps(arg) ++= referred - deps - - /** Decompose an application into a function prefix and a list of argument lists. - * If some of the arguments need a separation check because they are capture polymorphic, - * perform a separation check with `checkApply` - */ - private def traverseApply(tree: Tree, argss: List[List[Tree]])(using Context): Unit = tree match - case Apply(fn, args) => traverseApply(fn, args :: argss) - case TypeApply(fn, args) => traverseApply(fn, argss) // skip type arguments - case _ => - if argss.nestedExists(_.needsSepCheck) then - checkApply(tree, argss.flatten, dependencies(tree, argss)) - - /** Is `tree` an application of `caps.unsafe.unsafeAssumeSeparate`? */ - def isUnsafeAssumeSeparate(tree: Tree)(using Context): Boolean = tree match - case tree: Apply => tree.symbol == defn.Caps_unsafeAssumeSeparate - case _ => false - - /** Check (result-) type of `tree` for separation conditions using `checkType`. - * Excluded are parameters and definitions that have an =unsafeAssumeSeparate - * application as right hand sides. - * Hidden sets of checked definitions are added to `defsShadow`. - */ - def checkValOrDefDef(tree: ValOrDefDef)(using Context): Unit = - if !tree.symbol.isOneOf(TermParamOrAccessor) && !isUnsafeAssumeSeparate(tree.rhs) then - checkType(tree.tpt, tree.symbol) - if previousDefs.nonEmpty then - capt.println(i"sep check def ${tree.symbol}: ${tree.tpt} with ${captures(tree.tpt).hidden.footprint}") - defsShadow ++= captures(tree.tpt).hidden.footprint.deductSym(tree.symbol) - resultType(tree.symbol) = tree.tpt.nuType - previousDefs.head += tree - - /** Traverse `tree` and perform separation checks everywhere */ - def traverse(tree: Tree)(using Context): Unit = - if isUnsafeAssumeSeparate(tree) then return - checkUse(tree) - tree match - case tree @ Select(qual, _) if tree.symbol.is(Method) && tree.symbol.hasAnnotation(defn.ConsumeAnnot) => - traverseChildren(tree) - checkConsumedRefs( - captures(qual).footprint, qual.nuType, - TypeRole.Qualifier(qual, tree.symbol), - i"call prefix of @consume ${tree.symbol} refers to", qual.srcPos) - case tree: GenericApply => - traverseChildren(tree) - tree.tpe match - case _: MethodOrPoly => - case _ => traverseApply(tree, Nil) - case tree: Block => - val saved = defsShadow - previousDefs = mutable.ListBuffer() :: previousDefs - try traverseChildren(tree) - finally - previousDefs = previousDefs.tail - defsShadow = saved - case tree: ValDef => - traverseChildren(tree) - checkValOrDefDef(tree) - case tree: DefDef => - withFreshConsumed: - traverseChildren(tree) - checkValOrDefDef(tree) - case If(cond, thenp, elsep) => - traverse(cond) - val thenConsumed = consumed.segment(traverse(thenp)) - val elseConsumed = consumed.segment(traverse(elsep)) - consumed ++= thenConsumed - consumed ++= elseConsumed - case tree @ Labeled(bind, expr) => - val consumedBuf = mutable.ListBuffer[ConsumedSet]() - openLabeled = (bind.name, consumedBuf) :: openLabeled - traverse(expr) - for cs <- consumedBuf do consumed ++= cs - openLabeled = openLabeled.tail - case Return(expr, from) => - val retConsumed = consumed.segment(traverse(expr)) - from match - case Ident(name) => - for (lbl, consumedBuf) <- openLabeled do - if lbl == name then - consumedBuf += retConsumed - case _ => - case Match(sel, cases) => - // Matches without returns might still be kept after pattern matching to - // encode table switches. - traverse(sel) - val caseConsumed = for cas <- cases yield consumed.segment(traverse(cas)) - caseConsumed.foreach(consumed ++= _) - case tree: TypeDef if tree.symbol.isClass => - withFreshConsumed: - traverseChildren(tree) - case tree: WhileDo => - val loopConsumed = consumed.segment(traverseChildren(tree)) - if loopConsumed.size != 0 then - val (ref, pos) = loopConsumed.toMap.head - consumeInLoopError(ref, pos) - case _ => - traverseChildren(tree) -end SepCheck \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 655cdf979859..e28aeb8e0313 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -85,7 +85,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: /** Drops `private` from the flags of `symd` provided it is * a parameter accessor that's not `constructorOnly` or `uncheckedCaptured` * and that contains at least one @retains in co- or in-variant position. - * The @retains might be implicit for a type deriving from `Capability`. + * The @retains mught be implicit for a type deriving from `Capability`. */ private def newFlagsFor(symd: SymDenotation)(using Context): FlagSet = @@ -132,7 +132,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: def mappedInfo = if toBeUpdated.contains(sym) then symd.info // don't transform symbols that will anyway be updated - else Fresh.fromCap(transformExplicitType(symd.info), sym) + else transformExplicitType(symd.info) if Synthetics.needsTransform(symd) then Synthetics.transform(symd, mappedInfo) else if isPreCC(sym) then @@ -303,10 +303,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: * 6. Perform normalizeCaptures */ private def transformExplicitType(tp: Type, tptToCheck: Tree = EmptyTree)(using Context): Type = - - def fail(msg: Message) = - if !tptToCheck.isEmpty then report.error(msg, tptToCheck.srcPos) - val toCapturing = new DeepTypeMap with FollowAliasesMap: override def toString = "expand aliases" @@ -336,7 +332,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: else fntpe /** If C derives from Capability and we have a C^cs in source, we leave it as is - * instead of expanding it to C^{cap.rd}^cs. We do this by stripping capability-generated + * instead of expanding it to C^{cap}^cs. We do this by stripping capability-generated * universal capture sets from the parent of a CapturingType. */ def stripImpliedCaptureSet(tp: Type): Type = tp match @@ -345,27 +341,10 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: parent case _ => tp - /** Check that types extending SharedCapability don't have a `cap` in their capture set. - * TODO This is not enough. - * We need to also track that we cannot get exclusive capabilities in paths - * where some prefix derives from SharedCapability. Also, can we just - * exclude `cap`, or do we have to extend this to all exclusive capabilties? - * The problem is that we know what is exclusive in general only after capture - * checking, not before. - */ - def checkSharedOK(tp: Type): tp.type = - tp match - case CapturingType(parent, refs) - if refs.isUniversal && parent.derivesFromSharedCapability => - fail(em"$tp extends SharedCapability, so it cannot capture `cap`") - case _ => - tp - def apply(t: Type) = t match case t @ CapturingType(parent, refs) => - checkSharedOK: - t.derivedCapturingType(stripImpliedCaptureSet(this(parent)), refs) + t.derivedCapturingType(stripImpliedCaptureSet(this(parent)), refs) case t @ AnnotatedType(parent, ann) => val parent1 = this(parent) if ann.symbol.isRetains then @@ -373,13 +352,10 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: if !tptToCheck.isEmpty then checkWellformedLater(parent2, ann.tree, tptToCheck) try - checkSharedOK: - CapturingType(parent2, ann.tree.toCaptureSet) + CapturingType(parent2, ann.tree.toCaptureSet) catch case ex: IllegalCaptureRef => report.error(em"Illegal capture reference: ${ex.getMessage.nn}", tptToCheck.srcPos) parent2 - else if ann.symbol == defn.UncheckedCapturesAnnot then - makeUnchecked(apply(parent)) else t.derivedAnnotatedType(parent1, ann) case throwsAlias(res, exc) => @@ -391,6 +367,9 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: else normalizeCaptures(mapFollowingAliases(t)) end toCapturing + def fail(msg: Message) = + if !tptToCheck.isEmpty then report.error(msg, tptToCheck.srcPos) + val tp1 = toCapturing(tp) val tp2 = Existential.mapCapInResults(fail)(tp1) if tp2 ne tp then capt.println(i"expanded explicit in ${ctx.owner}: $tp --> $tp1 --> $tp2") @@ -449,32 +428,22 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: def setupTraverser(checker: CheckerAPI) = new TreeTraverserWithPreciseImportContexts: import checker.* - private val paramSigChange = util.EqHashSet[Tree]() - - /** Transform type of tree, and remember the transformed type as the type of the tree - * @pre !(boxed && sym.exists) - */ - private def transformTT(tree: TypeTree, sym: Symbol, boxed: Boolean)(using Context): Unit = + /** Transform type of tree, and remember the transformed type as the type the tree */ + private def transformTT(tree: TypeTree, boxed: Boolean)(using Context): Unit = if !tree.hasNuType then - var transformed = + val transformed = if tree.isInferred then transformInferredType(tree.tpe) else transformExplicitType(tree.tpe, tptToCheck = tree) - if boxed then transformed = box(transformed) - if sym.is(Param) && (transformed ne tree.tpe) then - paramSigChange += tree - tree.setNuType( - if boxed then transformed - else if sym.hasAnnotation(defn.UncheckedCapturesAnnot) then makeUnchecked(transformed) - else Fresh.fromCap(transformed, sym)) + tree.setNuType(if boxed then box(transformed) else transformed) /** Transform the type of a val or var or the result type of a def */ def transformResultType(tpt: TypeTree, sym: Symbol)(using Context): Unit = // First step: Transform the type and record it as knownType of tpt. try - transformTT(tpt, sym, + transformTT(tpt, boxed = - sym.isMutableVar + sym.is(Mutable, butNot = Method) && !ccConfig.useSealed && !sym.hasAnnotation(defn.UncheckedCapturesAnnot), // Under the sealed policy, we disallow root capabilities in the type of mutable @@ -521,11 +490,9 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: case tree @ TypeApply(fn, args) => traverse(fn) - for case arg: TypeTree <- args do - if defn.isTypeTestOrCast(fn.symbol) then - arg.setNuType(Fresh.fromCap(arg.tpe)) - else - transformTT(arg, NoSymbol, boxed = true) // type arguments in type applications are boxed + if !defn.isTypeTestOrCast(fn.symbol) then + for case arg: TypeTree <- args do + transformTT(arg, boxed = true) // type arguments in type applications are boxed case tree: TypeDef if tree.symbol.isClass => val sym = tree.symbol @@ -534,9 +501,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: inContext(ctx.withOwner(sym)) traverseChildren(tree) - case tree @ TypeDef(_, rhs: TypeTree) => - transformTT(rhs, tree.symbol, boxed = false) - case tree @ SeqLiteral(elems, tpt: TypeTree) => traverse(elems) tpt.setNuType(box(transformInferredType(tpt.tpe))) @@ -547,13 +511,13 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: case _ => traverseChildren(tree) postProcess(tree) - checkProperUseOrConsume(tree) + checkProperUse(tree) end traverse /** Processing done on node `tree` after its children are traversed */ def postProcess(tree: Tree)(using Context): Unit = tree match case tree: TypeTree => - transformTT(tree, NoSymbol, boxed = false) + transformTT(tree, boxed = false) case tree: ValOrDefDef => // Make sure denotation of tree's symbol is correct val sym = tree.symbol @@ -580,8 +544,8 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: def paramSignatureChanges = tree.match case tree: DefDef => tree.paramss.nestedExists: - case param: ValDef => paramSigChange.contains(param.tpt) - case param: TypeDef => paramSigChange.contains(param.rhs) + case param: ValDef => param.tpt.hasNuType + case param: TypeDef => param.rhs.hasNuType case _ => false // A symbol's signature changes if some of its parameter types or its result type @@ -616,7 +580,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: mt.paramInfos else val subst = SubstParams(psyms :: prevPsymss, mt1 :: prevLambdas) - psyms.map(psym => adaptedInfo(psym, subst(Fresh.toCap(psym.nextInfo)).asInstanceOf[mt.PInfo])), + psyms.map(psym => adaptedInfo(psym, subst(psym.nextInfo).asInstanceOf[mt.PInfo])), mt1 => integrateRT(mt.resType, psymss.tail, resType, psyms :: prevPsymss, mt1 :: prevLambdas) ) @@ -701,31 +665,16 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: case _ => end postProcess - /** Check that @use and @consume annotations only appear on parameters and not on - * anonymous function parameters - */ - def checkProperUseOrConsume(tree: Tree)(using Context): Unit = tree match + /** Check that @use annotations only appear on parameters and not on anonymous function parameters */ + def checkProperUse(tree: Tree)(using Context): Unit = tree match case tree: MemberDef => - val sym = tree.symbol - def isMethodParam = (sym.is(Param) || sym.is(ParamAccessor)) - && !sym.owner.isAnonymousFunction + def useAllowed(sym: Symbol) = + (sym.is(Param) || sym.is(ParamAccessor)) && !sym.owner.isAnonymousFunction for ann <- tree.symbol.annotations do - val annotCls = ann.symbol - if annotCls == defn.ConsumeAnnot then - if !(isMethodParam && sym.isTerm) - && !(sym.is(Method) && sym.owner.isClass) - then - report.error( - em"""@consume cannot be used here. Only memeber methods and their term parameters - |can have @consume annotations.""", - tree.srcPos) - else if annotCls == defn.UseAnnot then - if !isMethodParam then - report.error( - em"@use cannot be used here. Only method parameters can have @use annotations.", - tree.srcPos) + if ann.symbol == defn.UseAnnot && !useAllowed(tree.symbol) then + report.error(i"Only parameters of methods can have @use annotations", tree.srcPos) case _ => - end checkProperUseOrConsume + end checkProperUse end setupTraverser // --------------- Adding capture set variables ---------------------------------- @@ -786,7 +735,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: case RetainingType(parent, refs) => needsVariable(parent) && !refs.tpes.exists: - case ref: TermRef => ref.isCap + case ref: TermRef => ref.isRootCapability case _ => false case AnnotatedType(parent, _) => needsVariable(parent) @@ -849,16 +798,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: if variance > 0 then t1 else decorate(t1, Function.const(CaptureSet.Fluid)) - /** Replace all universal capture sets in this type by */ - private def makeUnchecked(using Context): TypeMap = new TypeMap with FollowAliasesMap: - def apply(t: Type) = t match - case t @ CapturingType(parent, refs) => - val parent1 = this(parent) - if refs.isUniversal then t.derivedCapturingType(parent1, CaptureSet.Fluid) - else t - case Existential(_) => t - case _ => mapFollowingAliases(t) - /** Pull out an embedded capture set from a part of `tp` */ def normalizeCaptures(tp: Type)(using Context): Type = tp match case tp @ RefinedType(parent @ CapturingType(parent1, refs), rname, rinfo) => @@ -938,7 +877,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: for j <- 0 until retained.length if j != i r <- retained(j).toCaptureRefs - if !r.isMaxCapability yield r val remaining = CaptureSet(others*) check(remaining, remaining) diff --git a/compiler/src/dotty/tools/dotc/cc/Synthetics.scala b/compiler/src/dotty/tools/dotc/cc/Synthetics.scala index cfdcbbc401bf..1372ebafe82f 100644 --- a/compiler/src/dotty/tools/dotc/cc/Synthetics.scala +++ b/compiler/src/dotty/tools/dotc/cc/Synthetics.scala @@ -116,7 +116,7 @@ object Synthetics: def transformUnapplyCaptures(info: Type)(using Context): Type = info match case info: MethodType => val paramInfo :: Nil = info.paramInfos: @unchecked - val newParamInfo = CapturingType(paramInfo, CaptureSet.fresh()) + val newParamInfo = CapturingType(paramInfo, CaptureSet.universal) val trackedParam = info.paramRefs.head def newResult(tp: Type): Type = tp match case tp: MethodOrPoly => @@ -132,9 +132,8 @@ object Synthetics: val (pt: PolyType) = info: @unchecked val (mt: MethodType) = pt.resType: @unchecked val (enclThis: ThisType) = owner.thisType: @unchecked - val paramCaptures = CaptureSet(enclThis, defn.captureRoot.termRef) pt.derivedLambdaType(resType = MethodType(mt.paramNames)( - mt1 => mt.paramInfos.map(_.capturing(paramCaptures)), + mt1 => mt.paramInfos.map(_.capturing(CaptureSet.universal)), mt1 => CapturingType(mt.resType, CaptureSet(enclThis, mt1.paramRefs.head)))) def transformCurriedTupledCaptures(info: Type, owner: Symbol) = @@ -149,10 +148,7 @@ object Synthetics: ExprType(mapFinalResult(et.resType, CapturingType(_, CaptureSet(enclThis)))) def transformCompareCaptures = - val (enclThis: ThisType) = symd.owner.thisType: @unchecked - MethodType( - defn.ObjectType.capturing(CaptureSet(defn.captureRoot.termRef, enclThis)) :: Nil, - defn.BooleanType) + MethodType(defn.ObjectType.capturing(CaptureSet.universal) :: Nil, defn.BooleanType) symd.copySymDenotation(info = symd.name match case DefaultGetterName(nme.copy, n) => diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index a0634fa89b6a..6e2e924edf65 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -15,7 +15,7 @@ import Comments.{Comment, docCtx} import util.Spans.NoSpan import config.Feature import Symbols.requiredModuleRef -import cc.{CaptureSet, RetainingType, Existential, readOnly} +import cc.{CaptureSet, RetainingType, Existential} import ast.tpd.ref import scala.annotation.tailrec @@ -996,22 +996,18 @@ class Definitions { @tu lazy val CapsModule: Symbol = requiredModule("scala.caps") @tu lazy val captureRoot: TermSymbol = CapsModule.requiredValue("cap") - @tu lazy val Caps_Capability: ClassSymbol = requiredClass("scala.caps.Capability") + @tu lazy val Caps_Capability: TypeSymbol = CapsModule.requiredType("Capability") @tu lazy val Caps_CapSet: ClassSymbol = requiredClass("scala.caps.CapSet") @tu lazy val Caps_reachCapability: TermSymbol = CapsModule.requiredMethod("reachCapability") - @tu lazy val Caps_readOnlyCapability: TermSymbol = CapsModule.requiredMethod("readOnlyCapability") @tu lazy val Caps_capsOf: TermSymbol = CapsModule.requiredMethod("capsOf") @tu lazy val Caps_Exists: ClassSymbol = requiredClass("scala.caps.Exists") @tu lazy val CapsUnsafeModule: Symbol = requiredModule("scala.caps.unsafe") @tu lazy val Caps_unsafeAssumePure: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumePure") - @tu lazy val Caps_unsafeAssumeSeparate: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumeSeparate") @tu lazy val Caps_ContainsTrait: TypeSymbol = CapsModule.requiredType("Contains") @tu lazy val Caps_containsImpl: TermSymbol = CapsModule.requiredMethod("containsImpl") - @tu lazy val Caps_Mutable: ClassSymbol = requiredClass("scala.caps.Mutable") - @tu lazy val Caps_SharedCapability: ClassSymbol = requiredClass("scala.caps.SharedCapability") /** The same as CaptureSet.universal but generated implicitly for references of Capability subtypes */ - @tu lazy val universalCSImpliedByCapability = CaptureSet(captureRoot.termRef.readOnly) + @tu lazy val universalCSImpliedByCapability = CaptureSet(captureRoot.termRef) @tu lazy val PureClass: Symbol = requiredClass("scala.Pure") @@ -1069,8 +1065,6 @@ class Definitions { @tu lazy val UncheckedCapturesAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedCaptures") @tu lazy val UntrackedCapturesAnnot: ClassSymbol = requiredClass("scala.caps.untrackedCaptures") @tu lazy val UseAnnot: ClassSymbol = requiredClass("scala.caps.use") - @tu lazy val ConsumeAnnot: ClassSymbol = requiredClass("scala.caps.consume") - @tu lazy val RefineOverrideAnnot: ClassSymbol = requiredClass("scala.caps.refineOverride") @tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile") @tu lazy val LanguageFeatureMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.languageFeature") @tu lazy val BeanGetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanGetter") @@ -1086,8 +1080,6 @@ class Definitions { @tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName") @tu lazy val VarargsAnnot: ClassSymbol = requiredClass("scala.annotation.varargs") @tu lazy val ReachCapabilityAnnot = requiredClass("scala.annotation.internal.reachCapability") - @tu lazy val FreshCapabilityAnnot = requiredClass("scala.annotation.internal.freshCapability") - @tu lazy val ReadOnlyCapabilityAnnot = requiredClass("scala.annotation.internal.readOnlyCapability") @tu lazy val RequiresCapabilityAnnot: ClassSymbol = requiredClass("scala.annotation.internal.requiresCapability") @tu lazy val RetainsAnnot: ClassSymbol = requiredClass("scala.annotation.retains") @tu lazy val RetainsCapAnnot: ClassSymbol = requiredClass("scala.annotation.retainsCap") @@ -1111,10 +1103,6 @@ class Definitions { @tu lazy val MetaAnnots: Set[Symbol] = NonBeanMetaAnnots + BeanGetterMetaAnnot + BeanSetterMetaAnnot - // Set of annotations that are not printed in types except under -Yprint-debug - @tu lazy val SilentAnnots: Set[Symbol] = - Set(InlineParamAnnot, ErasedParamAnnot, RefineOverrideAnnot) - // A list of annotations that are commonly used to indicate that a field/method argument or return // type is not null. These annotations are used by the nullification logic in JavaNullInterop to // improve the precision of type nullification. @@ -1562,9 +1550,6 @@ class Definitions { @tu lazy val pureSimpleClasses = Set(StringClass, NothingClass, NullClass) ++ ScalaValueClasses() - @tu lazy val capabilityWrapperAnnots: Set[Symbol] = - Set(ReachCapabilityAnnot, ReadOnlyCapabilityAnnot, MaybeCapabilityAnnot, FreshCapabilityAnnot) - @tu lazy val AbstractFunctionType: Array[TypeRef] = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0).asInstanceOf[Array[TypeRef]] val AbstractFunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun(AbstractFunctionType.map(_.symbol.asClass)) def AbstractFunctionClass(n: Int)(using Context): Symbol = AbstractFunctionClassPerRun()(using ctx)(n) diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 57bf870c6b64..0775b3caaf0c 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -597,6 +597,7 @@ object Flags { val JavaInterface: FlagSet = JavaDefined | NoInits | Trait val JavaProtected: FlagSet = JavaDefined | Protected val MethodOrLazy: FlagSet = Lazy | Method + val MutableOrLazy: FlagSet = Lazy | Mutable val MethodOrLazyOrMutable: FlagSet = Lazy | Method | Mutable val LiftedMethod: FlagSet = Lifted | Method val LocalParam: FlagSet = Local | Param diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index dc30ae2be7fb..56d71c7fb57e 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -121,7 +121,6 @@ object StdNames { val BITMAP_CHECKINIT: N = s"${BITMAP_PREFIX}init$$" // initialization bitmap for checkinit values val BITMAP_CHECKINIT_TRANSIENT: N = s"${BITMAP_PREFIX}inittrans$$" // initialization bitmap for transient checkinit values val CC_REACH: N = "$reach" - val CC_READONLY: N = "$readOnly" val DEFAULT_GETTER: N = str.DEFAULT_GETTER val DEFAULT_GETTER_INIT: N = "$lessinit$greater" val DO_WHILE_PREFIX: N = "doWhile$" @@ -554,7 +553,6 @@ object StdNames { val materializeTypeTag: N = "materializeTypeTag" val mirror : N = "mirror" val moduleClass : N = "moduleClass" - val mut: N = "mut" val name: N = "name" val nameDollar: N = "$name" val ne: N = "ne" @@ -589,7 +587,6 @@ object StdNames { val productPrefix: N = "productPrefix" val quotes : N = "quotes" val raw_ : N = "raw" - val rd: N = "rd" val refl: N = "refl" val reflect: N = "reflect" val reflectiveSelectable: N = "reflectiveSelectable" diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 70893a55fca9..670663ff2161 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -806,13 +806,6 @@ object SymDenotations { final def isRealMethod(using Context): Boolean = this.is(Method, butNot = Accessor) && !isAnonymousFunction - /** A mutable variable (not a getter or setter for it) */ - final def isMutableVar(using Context): Boolean = is(Mutable, butNot = Method) - - /** A mutable variable or its getter or setter */ - final def isMutableVarOrAccessor(using Context): Boolean = - is(Mutable) && (!is(Method) || is(Accessor)) - /** Is this a getter? */ final def isGetter(using Context): Boolean = this.is(Accessor) && !originalName.isSetterName && !(originalName.isScala2LocalSuffix && symbol.owner.is(Scala2x)) diff --git a/compiler/src/dotty/tools/dotc/core/SymUtils.scala b/compiler/src/dotty/tools/dotc/core/SymUtils.scala index baaeb025c6d5..1a762737d52f 100644 --- a/compiler/src/dotty/tools/dotc/core/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/SymUtils.scala @@ -287,7 +287,7 @@ class SymUtils: */ def isConstExprFinalVal(using Context): Boolean = atPhaseNoLater(erasurePhase) { - self.is(Final) && !self.isMutableVarOrAccessor && self.info.resultType.isInstanceOf[ConstantType] + self.is(Final, butNot = Mutable) && self.info.resultType.isInstanceOf[ConstantType] } && !self.sjsNeedsField /** The `ConstantType` of a val known to be `isConstrExprFinalVal`. diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index e4e2c6359eab..cc0471d40213 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -440,7 +440,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling if (tp1.prefix.isStable) return tryLiftedToThis1 case _ => if isCaptureVarComparison then - return subCaptures(tp1.captureSet, tp2.captureSet).isOK + return subCaptures(tp1.captureSet, tp2.captureSet, frozenConstraint).isOK if (tp1 eq NothingType) || isBottom(tp1) then return true } @@ -548,7 +548,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling case tp1 @ CapturingType(parent1, refs1) => def compareCapturing = if tp2.isAny then true - else if subCaptures(refs1, tp2.captureSet).isOK && sameBoxed(tp1, tp2, refs1) + else if subCaptures(refs1, tp2.captureSet, frozenConstraint).isOK && sameBoxed(tp1, tp2, refs1) || !ctx.mode.is(Mode.CheckBoundsOrSelfType) && tp1.isAlwaysPure then val tp2a = @@ -591,7 +591,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling && (isBottom(tp1) || GADTusage(tp2.symbol)) if isCaptureVarComparison then - return subCaptures(tp1.captureSet, tp2.captureSet).isOK + return subCaptures(tp1.captureSet, tp2.captureSet, frozenConstraint).isOK isSubApproxHi(tp1, info2.lo) && (trustBounds || isSubApproxHi(tp1, info2.hi)) || compareGADT @@ -678,12 +678,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling && isSubInfo(info1.resultType, info2.resultType.subst(info2, info1)) case (info1 @ CapturingType(parent1, refs1), info2: Type) if info2.stripCapturing.isInstanceOf[MethodOrPoly] => - subCaptures(refs1, info2.captureSet).isOK && sameBoxed(info1, info2, refs1) + subCaptures(refs1, info2.captureSet, frozenConstraint).isOK && sameBoxed(info1, info2, refs1) && isSubInfo(parent1, info2) case (info1: Type, CapturingType(parent2, refs2)) if info1.stripCapturing.isInstanceOf[MethodOrPoly] => val refs1 = info1.captureSet - (refs1.isAlwaysEmpty || subCaptures(refs1, refs2).isOK) && sameBoxed(info1, info2, refs1) + (refs1.isAlwaysEmpty || subCaptures(refs1, refs2, frozenConstraint).isOK) && sameBoxed(info1, info2, refs1) && isSubInfo(info1, parent2) case _ => isSubType(info1, info2) @@ -877,12 +877,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling // capt-capibility.scala and function-combinators.scala val singletonOK = tp1 match case tp1: SingletonType - if subCaptures(tp1.underlying.captureSet, refs2, CaptureSet.VarState.Separate).isOK => + if subCaptures(tp1.underlying.captureSet, refs2, frozen = true).isOK => recur(tp1.widen, tp2) case _ => false singletonOK - || subCaptures(refs1, refs2).isOK + || subCaptures(refs1, refs2, frozenConstraint).isOK && sameBoxed(tp1, tp2, refs1) && (recur(tp1.widen.stripCapturing, parent2) || tp1.isInstanceOf[SingletonType] && recur(tp1, parent2) @@ -2174,7 +2174,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling val info2 = tp2.refinedInfo val isExpr2 = info2.isInstanceOf[ExprType] var info1 = m.info match - case info1: ValueType if isExpr2 || m.symbol.isMutableVarOrAccessor => + case info1: ValueType if isExpr2 || m.symbol.is(Mutable) => // OK: { val x: T } <: { def x: T } // OK: { var x: T } <: { def x: T } // NO: { var x: T } <: { val x: T } @@ -2896,30 +2896,29 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling end inverse end MapExistentials - protected def makeVarState() = - if frozenConstraint then CaptureSet.VarState.Closed() else CaptureSet.VarState() - - protected def subCaptures(refs1: CaptureSet, refs2: CaptureSet, - vs: CaptureSet.VarState = makeVarState())(using Context): CaptureSet.CompareResult = + protected def subCaptures(refs1: CaptureSet, refs2: CaptureSet, frozen: Boolean)(using Context): CaptureSet.CompareResult = try if assocExistentials.isEmpty then - refs1.subCaptures(refs2, vs) + refs1.subCaptures(refs2, frozen) else val mapped = refs1.map(MapExistentials(assocExistentials)) if mapped.elems.exists(Existential.isBadExistential) then CaptureSet.CompareResult.Fail(refs2 :: Nil) - else mapped.subCaptures(refs2, vs) + else subCapturesMapped(mapped, refs2, frozen) catch case ex: AssertionError => println(i"fail while subCaptures $refs1 <:< $refs2") throw ex + protected def subCapturesMapped(refs1: CaptureSet, refs2: CaptureSet, frozen: Boolean)(using Context): CaptureSet.CompareResult = + refs1.subCaptures(refs2, frozen) + /** Is the boxing status of tp1 and tp2 the same, or alternatively, is * the capture sets `refs1` of `tp1` a subcapture of the empty set? * In the latter case, boxing status does not matter. */ protected def sameBoxed(tp1: Type, tp2: Type, refs1: CaptureSet)(using Context): Boolean = (tp1.isBoxedCapturing == tp2.isBoxedCapturing) - || refs1.subCaptures(CaptureSet.empty, makeVarState()).isOK + || refs1.subCaptures(CaptureSet.empty, frozenConstraint).isOK // ----------- Diagnostics -------------------------------------------------- @@ -3497,8 +3496,8 @@ object TypeComparer { def reduceMatchWith[T](op: MatchReducer => T)(using Context): T = comparing(_.reduceMatchWith(op)) - def subCaptures(refs1: CaptureSet, refs2: CaptureSet, vs: CaptureSet.VarState)(using Context): CaptureSet.CompareResult = - comparing(_.subCaptures(refs1, refs2, vs)) + def subCaptures(refs1: CaptureSet, refs2: CaptureSet, frozen: Boolean)(using Context): CaptureSet.CompareResult = + comparing(_.subCaptures(refs1, refs2, frozen)) def subsumesExistentially(tp1: TermParamRef, tp2: CaptureRef)(using Context) = comparing(_.subsumesExistentially(tp1, tp2)) @@ -3979,9 +3978,14 @@ class ExplainingTypeComparer(initctx: Context, short: Boolean) extends TypeCompa super.gadtAddBound(sym, b, isUpper) } - override def subCaptures(refs1: CaptureSet, refs2: CaptureSet, vs: CaptureSet.VarState)(using Context): CaptureSet.CompareResult = - traceIndented(i"subcaptures $refs1 <:< $refs2, varState = ${vs.toString}") { - super.subCaptures(refs1, refs2, vs) + override def subCaptures(refs1: CaptureSet, refs2: CaptureSet, frozen: Boolean)(using Context): CaptureSet.CompareResult = + traceIndented(i"subcaptures $refs1 <:< $refs2 ${if frozen then "frozen" else ""}") { + super.subCaptures(refs1, refs2, frozen) + } + + override def subCapturesMapped(refs1: CaptureSet, refs2: CaptureSet, frozen: Boolean)(using Context): CaptureSet.CompareResult = + traceIndented(i"subcaptures mapped $refs1 <:< $refs2 ${if frozen then "frozen" else ""}") { + super.subCapturesMapped(refs1, refs2, frozen) } def lastTrace(header: String): String = header + { try b.toString finally b.clear() } diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 0b758061febd..a7f41a71d7ce 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -19,7 +19,7 @@ import typer.Inferencing.* import typer.IfBottom import reporting.TestingReporter import cc.{CapturingType, derivedCapturingType, CaptureSet, captureSet, isBoxed, isBoxedCapturing} -import CaptureSet.{CompareResult, IdempotentCaptRefMap, IdentityCaptRefMap, VarState} +import CaptureSet.{CompareResult, IdempotentCaptRefMap, IdentityCaptRefMap} import scala.annotation.internal.sharable import scala.annotation.threadUnsafe @@ -161,7 +161,7 @@ object TypeOps: TypeComparer.lub(simplify(l, theMap), simplify(r, theMap), isSoft = tp.isSoft) case tp @ CapturingType(parent, refs) => if !ctx.mode.is(Mode.Type) - && refs.subCaptures(parent.captureSet, VarState.Separate).isOK + && refs.subCaptures(parent.captureSet, frozen = true).isOK && (tp.isBoxed || !parent.isBoxedCapturing) // fuse types with same boxed status and outer boxed with any type then diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index bb1ade8f9ed0..5a75b817a472 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -860,24 +860,21 @@ object Types extends TypeUtils { pinfo recoverable_& rinfo pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo) } - else rinfo match - case AnnotatedType(rinfo1, ann) if ann.symbol == defn.RefineOverrideAnnot => - pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, rinfo1) - case _ => - val isRefinedMethod = rinfo.isInstanceOf[MethodOrPoly] - val joint = pdenot.meet( - new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId), pre, isRefinedMethod), - pre, - safeIntersection = ctx.base.pendingMemberSearches.contains(name)) - joint match - case joint: SingleDenotation - if isRefinedMethod - && (rinfo <:< joint.info - || name == nme.apply && defn.isFunctionType(tp.parent)) => - // use `rinfo` to keep the right parameter names for named args. See i8516.scala. - joint.derivedSingleDenotation(joint.symbol, rinfo, pre, isRefinedMethod) - case _ => - joint + else + val isRefinedMethod = rinfo.isInstanceOf[MethodOrPoly] + val joint = pdenot.meet( + new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId), pre, isRefinedMethod), + pre, + safeIntersection = ctx.base.pendingMemberSearches.contains(name)) + joint match + case joint: SingleDenotation + if isRefinedMethod + && (rinfo <:< joint.info + || name == nme.apply && defn.isFunctionType(tp.parent)) => + // use `rinfo` to keep the right parameter names for named args. See i8516.scala. + joint.derivedSingleDenotation(joint.symbol, rinfo, pre, isRefinedMethod) + case _ => + joint } def goApplied(tp: AppliedType, tycon: HKTypeLambda) = @@ -4178,7 +4175,7 @@ object Types extends TypeUtils { tl => params.map(p => tl.integrate(params, adaptParamInfo(p))), tl => tl.integrate(params, resultType)) - /** Adapt info of parameter symbol to be integrated into corresponding MethodType + /** Adapt info of parameter symbol to be integhrated into corresponding MethodType * using the scheme described in `fromSymbols`. */ def adaptParamInfo(param: Symbol, pinfo: Type)(using Context): Type = diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index f50df85adf22..ef07d477c303 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1590,36 +1590,22 @@ object Parsers { case _ => None } - /** CaptureRef ::= { SimpleRef `.` } SimpleRef [`*`] [`.` rd] + /** CaptureRef ::= { SimpleRef `.` } SimpleRef [`*`] * | [ { SimpleRef `.` } SimpleRef `.` ] id `^` */ def captureRef(): Tree = - - def derived(ref: Tree, name: TermName) = + val ref = dotSelectors(simpleRef()) + if isIdent(nme.raw.STAR) then in.nextToken() - atSpan(startOffset(ref)) { PostfixOp(ref, Ident(name)) } - - def recur(ref: Tree): Tree = - if in.token == DOT then - in.nextToken() - if in.isIdent(nme.rd) then derived(ref, nme.CC_READONLY) - else recur(selector(ref)) - else if in.isIdent(nme.raw.STAR) then - val reachRef = derived(ref, nme.CC_REACH) - if in.token == DOT && in.lookahead.isIdent(nme.rd) then - in.nextToken() - derived(reachRef, nme.CC_READONLY) - else reachRef - else if isIdent(nme.UPARROW) then - in.nextToken() - atSpan(startOffset(ref)): - convertToTypeId(ref) match - case ref: RefTree => makeCapsOf(ref) - case ref => ref - else ref - - recur(simpleRef()) - end captureRef + atSpan(startOffset(ref)): + PostfixOp(ref, Ident(nme.CC_REACH)) + else if isIdent(nme.UPARROW) then + in.nextToken() + atSpan(startOffset(ref)): + convertToTypeId(ref) match + case ref: RefTree => makeCapsOf(ref) + case ref => ref + else ref /** CaptureSet ::= `{` CaptureRef {`,` CaptureRef} `}` -- under captureChecking */ @@ -3308,14 +3294,13 @@ object Parsers { case SEALED => Mod.Sealed() case IDENTIFIER => name match { + case nme.erased if in.erasedEnabled => Mod.Erased() case nme.inline => Mod.Inline() case nme.opaque => Mod.Opaque() case nme.open => Mod.Open() case nme.transparent => Mod.Transparent() case nme.infix => Mod.Infix() case nme.tracked => Mod.Tracked() - case nme.erased if in.erasedEnabled => Mod.Erased() - case nme.mut if Feature.ccEnabled => Mod.Mut() } } @@ -4696,8 +4681,7 @@ object Parsers { syntaxError(msg, tree.span) Nil tree match - case tree: MemberDef - if !(tree.mods.flags & ModifierFlags).isEmpty && !tree.mods.isMutableVar => // vars are OK, mut defs are not + case tree: MemberDef if !(tree.mods.flags & (ModifierFlags &~ Mutable)).isEmpty => fail(em"refinement cannot be ${(tree.mods.flags & ModifierFlags).flagStrings().mkString("`", "`, `", "`")}") case tree: DefDef if tree.termParamss.nestedExists(!_.rhs.isEmpty) => fail(em"refinement cannot have default arguments") diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index cbb587325011..e5bba6c3b73b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -1209,10 +1209,7 @@ object Scanners { def isSoftModifier: Boolean = token == IDENTIFIER - && (softModifierNames.contains(name) - || name == nme.erased && erasedEnabled - || name == nme.tracked && trackedEnabled - || name == nme.mut && Feature.ccEnabled) + && (softModifierNames.contains(name) || name == nme.erased && erasedEnabled || name == nme.tracked && trackedEnabled) def isSoftModifierInModifierPosition: Boolean = isSoftModifier && inModifierPosition() diff --git a/compiler/src/dotty/tools/dotc/printing/Formatting.scala b/compiler/src/dotty/tools/dotc/printing/Formatting.scala index 741b997d9926..ccd7b4e4e282 100644 --- a/compiler/src/dotty/tools/dotc/printing/Formatting.scala +++ b/compiler/src/dotty/tools/dotc/printing/Formatting.scala @@ -8,7 +8,7 @@ import core.* import Texts.*, Types.*, Flags.*, Symbols.*, Contexts.* import Decorators.* import reporting.Message -import util.{DiffUtil, SimpleIdentitySet} +import util.DiffUtil import Highlighting.* object Formatting { @@ -87,9 +87,6 @@ object Formatting { def show(x: H *: T) = CtxShow(toStr(x.head) *: toShown(x.tail).asInstanceOf[Tuple]) - given [X <: AnyRef: Show]: Show[SimpleIdentitySet[X]] with - def show(x: SimpleIdentitySet[X]) = summon[Show[List[X]]].show(x.toList) - given Show[FlagSet] with def show(x: FlagSet) = x.flagsString diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 3f086b28656c..e90aeb217362 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -27,12 +27,6 @@ class PlainPrinter(_ctx: Context) extends Printer { protected def printDebug = ctx.settings.YprintDebug.value - /** Print Fresh.Cap instances as */ - protected def printFreshDetailed = printDebug - - /** Print Fresh.Cap instances as "fresh" */ - protected def printFresh = printFreshDetailed || ctx.property(PrintFresh).isDefined - private var openRecs: List[RecType] = Nil protected def maxToTextRecursions: Int = 100 @@ -159,14 +153,12 @@ class PlainPrinter(_ctx: Context) extends Printer { + defn.FromJavaObjectSymbol def toTextCaptureSet(cs: CaptureSet): Text = - if printDebug && ctx.settings.YccDebug.value - && !cs.isConst && !cs.isInstanceOf[CaptureSet.HiddenSet] //HiddenSets can be cyclic - then cs.toString + if printDebug && ctx.settings.YccDebug.value && !cs.isConst then cs.toString else if cs == CaptureSet.Fluid then "" else val core: Text = if !cs.isConst && cs.elems.isEmpty then "?" - else "{" ~ Text(cs.processElems(_.toList.map(toTextCaptureRef)), ", ") ~ "}" + else "{" ~ Text(cs.elems.toList.map(toTextCaptureRef), ", ") ~ "}" // ~ Str("?").provided(!cs.isConst) core ~ cs.optionalInfo @@ -175,9 +167,8 @@ class PlainPrinter(_ctx: Context) extends Printer { toTextCaptureRef(ref.typeOpt) case TypeApply(fn, arg :: Nil) if fn.symbol == defn.Caps_capsOf => toTextRetainedElem(arg) - case ReachCapabilityApply(ref1) => toTextRetainedElem(ref1) ~ "*" - case ReadOnlyCapabilityApply(ref1) => toTextRetainedElem(ref1) ~ ".rd" - case _ => toText(ref) + case _ => + toText(ref) private def toTextRetainedElems[T <: Untyped](refs: List[Tree[T]]): Text = "{" ~ Text(refs.map(ref => toTextRetainedElem(ref)), ", ") ~ "}" @@ -187,7 +178,8 @@ class PlainPrinter(_ctx: Context) extends Printer { */ protected def toTextCapturing(parent: Type, refsText: Text, boxText: Text): Text = changePrec(InfixPrec): - boxText ~ toTextLocal(parent) ~ "^" ~ (refsText provided refsText != rootSetText) + boxText ~ toTextLocal(parent) ~ "^" + ~ (refsText provided refsText != rootSetText) final protected def rootSetText = Str("{cap}") // TODO Use disambiguation @@ -198,7 +190,7 @@ class PlainPrinter(_ctx: Context) extends Printer { case tp: TermRef if !tp.denotationIsCurrent && !homogenizedView // always print underlying when testing picklers - && !tp.isCap + && !tp.isRootCapability || tp.symbol.is(Module) || tp.symbol.name == nme.IMPORT => toTextRef(tp) ~ ".type" @@ -210,14 +202,14 @@ class PlainPrinter(_ctx: Context) extends Printer { else toTextPrefixOf(tp) ~ selectionString(tp) case tp: TermParamRef => - ParamRefNameString(tp) ~ hashStr(tp.binder) ~ ".type" + ParamRefNameString(tp) ~ lambdaHash(tp.binder) ~ ".type" case tp: TypeParamRef => val suffix = if showNestingLevel then val tvar = ctx.typerState.constraint.typeVarOfParam(tp) if tvar.exists then s"#${tvar.asInstanceOf[TypeVar].nestingLevel.toString}" else "" else "" - ParamRefNameString(tp) ~ hashStr(tp.binder) ~ suffix + ParamRefNameString(tp) ~ lambdaHash(tp.binder) ~ suffix case tp: SingletonType => toTextSingleton(tp) case AppliedType(tycon, args) => @@ -250,19 +242,9 @@ class PlainPrinter(_ctx: Context) extends Printer { }.close case tp @ CapturingType(parent, refs) => val boxText: Text = Str("box ") provided tp.isBoxed //&& ctx.settings.YccDebug.value - if parent.derivesFrom(defn.Caps_Capability) - && refs.containsRootCapability && refs.isReadOnly && !printDebug - then - toText(parent) - else - val refsText = - if refs.isUniversal then - if refs.elems.size == 1 then rootSetText else toTextCaptureSet(refs) - else if !refs.elems.isEmpty && refs.elems.forall(_.isCapOrFresh) && !printFresh then - rootSetText - else - toTextCaptureSet(refs) - toTextCapturing(parent, refsText, boxText) + val showAsCap = refs.isUniversal && (refs.elems.size == 1 || !printDebug) + val refsText = if showAsCap then rootSetText else toTextCaptureSet(refs) + toTextCapturing(parent, refsText, boxText) case tp @ RetainingType(parent, refs) => if Feature.ccEnabledSomewhere then val refsText = refs match @@ -293,25 +275,25 @@ class PlainPrinter(_ctx: Context) extends Printer { case ExprType(restp) => def arrowText: Text = restp match case AnnotatedType(parent, ann) if ann.symbol == defn.RetainsByNameAnnot => - ann.tree.retainedElems match - case ref :: Nil if ref.symbol == defn.captureRoot => Str("=>") - case refs => Str("->") ~ toTextRetainedElems(refs) + val refs = ann.tree.retainedElems + if refs.exists(_.symbol == defn.captureRoot) then Str("=>") + else Str("->") ~ toTextRetainedElems(refs) case _ => if Feature.pureFunsEnabled then "->" else "=>" changePrec(GlobalPrec)(arrowText ~ " " ~ toText(restp)) case tp: HKTypeLambda => changePrec(GlobalPrec) { - "[" ~ paramsText(tp) ~ "]" ~ hashStr(tp) ~ Str(" =>> ") ~ toTextGlobal(tp.resultType) + "[" ~ paramsText(tp) ~ "]" ~ lambdaHash(tp) ~ Str(" =>> ") ~ toTextGlobal(tp.resultType) } case tp: PolyType => changePrec(GlobalPrec) { - "[" ~ paramsText(tp) ~ "]" ~ hashStr(tp) ~ + "[" ~ paramsText(tp) ~ "]" ~ lambdaHash(tp) ~ (Str(": ") provided !tp.resultType.isInstanceOf[MethodOrPoly]) ~ toTextGlobal(tp.resultType) } case AnnotatedType(tpe, annot) => - if defn.SilentAnnots.contains(annot.symbol) && !printDebug then - toText(tpe) + if annot.symbol == defn.InlineParamAnnot || annot.symbol == defn.ErasedParamAnnot + then toText(tpe) else if (annot.symbol == defn.IntoAnnot || annot.symbol == defn.IntoParamAnnot) && !printDebug then atPrec(GlobalPrec)( Str("into ") ~ toText(tpe) ) @@ -356,7 +338,7 @@ class PlainPrinter(_ctx: Context) extends Printer { protected def paramsText(lam: LambdaType): Text = { def paramText(ref: ParamRef) = val erased = ref.underlying.hasAnnotation(defn.ErasedParamAnnot) - keywordText("erased ").provided(erased) ~ ParamRefNameString(ref) ~ hashStr(lam) ~ toTextRHS(ref.underlying, isParameter = true) + keywordText("erased ").provided(erased) ~ ParamRefNameString(ref) ~ lambdaHash(lam) ~ toTextRHS(ref.underlying, isParameter = true) Text(lam.paramRefs.map(paramText), ", ") } @@ -368,11 +350,11 @@ class PlainPrinter(_ctx: Context) extends Printer { /** The name of the symbol without a unique id. */ protected def simpleNameString(sym: Symbol): String = nameString(sym.name) - /** If -uniqid is set, the hashcode of the type, after a # */ - protected def hashStr(tp: Type): String = - if showUniqueIds then - try "#" + tp.hashCode - catch case ex: NullPointerException => "" + /** If -uniqid is set, the hashcode of the lambda type, after a # */ + protected def lambdaHash(pt: LambdaType): Text = + if (showUniqueIds) + try "#" + pt.hashCode + catch { case ex: NullPointerException => "" } else "" /** A string to append to a symbol composed of: @@ -421,7 +403,7 @@ class PlainPrinter(_ctx: Context) extends Printer { case tp @ ConstantType(value) => toText(value) case pref: TermParamRef => - ParamRefNameString(pref) ~ hashStr(pref.binder) + ParamRefNameString(pref) ~ lambdaHash(pref.binder) case tp: RecThis => val idx = openRecs.reverse.indexOf(tp.binder) if (idx >= 0) selfRecName(idx + 1) @@ -435,16 +417,11 @@ class PlainPrinter(_ctx: Context) extends Printer { def toTextCaptureRef(tp: Type): Text = homogenize(tp) match - case tp: TermRef if tp.symbol == defn.captureRoot => "cap" + case tp: TermRef if tp.symbol == defn.captureRoot => Str("cap") case tp: SingletonType => toTextRef(tp) case tp: (TypeRef | TypeParamRef) => toText(tp) ~ "^" - case ReadOnlyCapability(tp1) => toTextCaptureRef(tp1) ~ ".rd" case ReachCapability(tp1) => toTextCaptureRef(tp1) ~ "*" case MaybeCapability(tp1) => toTextCaptureRef(tp1) ~ "?" - case Fresh.Cap(hidden) => - if printFreshDetailed then s"" - else if printFresh then "fresh" - else "cap" case tp => toText(tp) protected def isOmittablePrefix(sym: Symbol): Boolean = @@ -559,7 +536,7 @@ class PlainPrinter(_ctx: Context) extends Printer { else if sym.is(Param) then "parameter" else if sym.is(Given) then "given instance" else if (flags.is(Lazy)) "lazy value" - else if (sym.isMutableVar) "variable" + else if (flags.is(Mutable)) "variable" else if (sym.isClassConstructor && sym.isPrimaryConstructor) "primary constructor" else if (sym.isClassConstructor) "constructor" else if (sym.is(Method)) "method" @@ -575,7 +552,7 @@ class PlainPrinter(_ctx: Context) extends Printer { else if (flags.is(Module)) "object" else if (sym.isClass) "class" else if (sym.isType) "type" - else if (sym.isMutableVarOrAccessor) "var" + else if (flags.is(Mutable)) "var" else if (flags.is(Package)) "package" else if (sym.is(Method)) "def" else if (sym.isTerm && !flags.is(Param)) "val" diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 652ec027b643..27ab73f0fe4d 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -337,7 +337,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "?" ~ (("(ignored: " ~ toText(ignored) ~ ")") provided printDebug) case tp @ PolyProto(targs, resType) => "[applied to [" ~ toTextGlobal(targs, ", ") ~ "] returning " ~ toText(resType) - case tp: AnnotatedType if tp.isTrackableRef => + case ReachCapability(_) | MaybeCapability(_) => toTextCaptureRef(tp) case _ => super.toText(tp) @@ -744,8 +744,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case PostfixOp(l, op) => if op.name == nme.CC_REACH then changePrec(DotPrec) { toText(l) ~ "*" } - else if op.name == nme.CC_READONLY then - changePrec(DotPrec) { toText(l) ~ ".rd" } else changePrec(InfixPrec) { toText(l) ~ " " ~ toText(op) } case PrefixOp(op, r) => diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 56420a0e0bc2..fd85a65822eb 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1694,7 +1694,7 @@ class OnlyClassesCanHaveDeclaredButUndefinedMembers(sym: Symbol)( def msg(using Context) = i"""Declaration of $sym not allowed here: only classes can have declared but undefined members""" def explain(using Context) = - if sym.isMutableVarOrAccessor then "Note that variables need to be initialized to be defined." + if sym.is(Mutable) then "Note that variables need to be initialized to be defined." else "" } diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index 4d915b57df1b..c303c40485ce 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -418,7 +418,7 @@ private class ExtractAPICollector(nonLocalClassSymbols: mutable.HashSet[Symbol]) apiClass(sym.asClass) } else if (sym.isType) { apiTypeMember(sym.asType) - } else if (sym.isMutableVar) { + } else if (sym.is(Mutable, butNot = Accessor)) { api.Var.of(sym.name.toString, apiAccess(sym), apiModifiers(sym), apiAnnotations(sym, inlineOrigin).toArray, apiType(sym.info)) } else if (sym.isStableMember && !sym.isRealMethod) { diff --git a/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala b/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala index 7263bce0478c..c1725cbd0255 100644 --- a/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala +++ b/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala @@ -120,7 +120,7 @@ object CapturedVars: def traverse(tree: Tree)(using Context) = tree match case id: Ident => val sym = id.symbol - if sym.isMutableVar && sym.owner.isTerm then + if sym.is(Mutable, butNot = Method) && sym.owner.isTerm then val enclMeth = ctx.owner.enclosingMethod if sym.enclosingMethod != enclMeth then report.log(i"capturing $sym in ${sym.enclosingMethod}, referenced from $enclMeth") diff --git a/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala b/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala index 5f52ac82879a..e8a402068bfc 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala @@ -65,7 +65,7 @@ class CheckReentrant extends MiniPhase { scanning(cls) { for (sym <- cls.classInfo.decls) if (sym.isTerm && !sym.isSetter && !isIgnored(sym)) - if (sym.isMutableVarOrAccessor) { + if (sym.is(Mutable)) { report.error( em"""possible data race involving globally reachable ${sym.showLocated}: ${sym.info} | use -Ylog:checkReentrant+ to find out more about why the variable is reachable.""") diff --git a/compiler/src/dotty/tools/dotc/transform/CheckStatic.scala b/compiler/src/dotty/tools/dotc/transform/CheckStatic.scala index 957fd78e9c2c..6c74f302b65d 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckStatic.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckStatic.scala @@ -52,7 +52,7 @@ class CheckStatic extends MiniPhase { report.error(MissingCompanionForStatic(defn.symbol), defn.srcPos) else if (clashes.exists) report.error(MemberWithSameNameAsStatic(), defn.srcPos) - else if (defn.symbol.isMutableVarOrAccessor && companion.is(Flags.Trait)) + else if (defn.symbol.is(Flags.Mutable) && companion.is(Flags.Trait)) report.error(TraitCompanionWithMutableStatic(), defn.srcPos) else if (defn.symbol.is(Flags.Lazy)) report.error(LazyStaticField(), defn.srcPos) diff --git a/compiler/src/dotty/tools/dotc/transform/Constructors.scala b/compiler/src/dotty/tools/dotc/transform/Constructors.scala index b373565489f0..9a0df830c6d7 100644 --- a/compiler/src/dotty/tools/dotc/transform/Constructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/Constructors.scala @@ -155,7 +155,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase = case Ident(_) | Select(This(_), _) => var sym = tree.symbol def isOverridableSelect = tree.isInstanceOf[Select] && !sym.isEffectivelyFinal - def switchOutsideSupercall = !sym.isMutableVarOrAccessor && !isOverridableSelect + def switchOutsideSupercall = !sym.is(Mutable) && !isOverridableSelect // If true, switch to constructor parameters also in the constructor body // that follows the super call. // Variables need to go through the getter since they might have been updated. diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index 2fd777f715d9..e2712a7d6302 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -255,7 +255,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { def transformMemberDefThreadUnsafe(x: ValOrDefDef)(using Context): Thicket = { val claz = x.symbol.owner.asClass val tpe = x.tpe.widen.resultType.widen - assert(!x.symbol.isMutableVarOrAccessor) + assert(!(x.symbol is Mutable)) val containerName = LazyLocalName.fresh(x.name.asTermName) val containerSymbol = newSymbol(claz, containerName, x.symbol.flags &~ containerFlagsMask | containerFlags | Private, @@ -447,7 +447,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { } def transformMemberDefThreadSafe(x: ValOrDefDef)(using Context): Thicket = { - assert(!x.symbol.isMutableVarOrAccessor) + assert(!(x.symbol is Mutable)) if ctx.settings.YlegacyLazyVals.value then transformMemberDefThreadSafeLegacy(x) else diff --git a/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala b/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala index b3ec05501b5b..95975ad9e6b8 100644 --- a/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala +++ b/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -28,7 +28,7 @@ class MoveStatics extends MiniPhase with SymTransformer { def transformSym(sym: SymDenotation)(using Context): SymDenotation = if (sym.hasAnnotation(defn.ScalaStaticAnnot) && sym.owner.is(Flags.Module) && sym.owner.companionClass.exists && - (sym.is(Flags.Method) || !(sym.isMutableVarOrAccessor && sym.owner.companionClass.is(Flags.Trait)))) { + (sym.is(Flags.Method) || !(sym.is(Flags.Mutable) && sym.owner.companionClass.is(Flags.Trait)))) { sym.owner.asClass.delete(sym.symbol) sym.owner.companionClass.asClass.enter(sym.symbol) sym.copySymDenotation(owner = sym.owner.companionClass) diff --git a/compiler/src/dotty/tools/dotc/transform/Recheck.scala b/compiler/src/dotty/tools/dotc/transform/Recheck.scala index e8227f759ad4..8936c460de81 100644 --- a/compiler/src/dotty/tools/dotc/transform/Recheck.scala +++ b/compiler/src/dotty/tools/dotc/transform/Recheck.scala @@ -167,11 +167,7 @@ abstract class Recheck extends Phase, SymTransformer: * from the current type. */ def setNuType(tpe: Type): Unit = - if nuTypes.lookup(tree) == null then updNuType(tpe) - - /** Set new type of the tree unconditionally. */ - def updNuType(tpe: Type): Unit = - if tpe ne tree.tpe then nuTypes(tree) = tpe + if nuTypes.lookup(tree) == null && (tpe ne tree.tpe) then nuTypes(tree) = tpe /** The new type of the tree, or if none was installed, the original type */ def nuType(using Context): Type = diff --git a/compiler/src/dotty/tools/dotc/transform/UninitializedDefs.scala b/compiler/src/dotty/tools/dotc/transform/UninitializedDefs.scala index 7531b6e41c19..f22fc53e9b6e 100644 --- a/compiler/src/dotty/tools/dotc/transform/UninitializedDefs.scala +++ b/compiler/src/dotty/tools/dotc/transform/UninitializedDefs.scala @@ -33,7 +33,7 @@ class UninitializedDefs extends MiniPhase: def recur(rhs: Tree): Boolean = rhs match case rhs: RefTree => rhs.symbol == defn.Compiletime_uninitialized - && tree.symbol.isMutableVarOrAccessor && tree.symbol.owner.isClass + && tree.symbol.is(Mutable) && tree.symbol.owner.isClass case closureDef(ddef) if defn.isContextFunctionType(tree.tpt.tpe.dealias) => recur(ddef.rhs) case _ => diff --git a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala index 5d246d7b07e0..61d96770ea55 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala @@ -836,7 +836,7 @@ class Objects(using Context @constructorOnly): Bottom else if target.exists then def isNextFieldOfColonColon: Boolean = ref.klass == defn.ConsClass && target.name.toString == "next" - if target.isMutableVarOrAccessor && !isNextFieldOfColonColon then + if target.isOneOf(Flags.Mutable) && !isNextFieldOfColonColon then if ref.hasVar(target) then val addr = ref.varAddr(target) if addr.owner == State.currentObject then diff --git a/compiler/src/dotty/tools/dotc/transform/init/Util.scala b/compiler/src/dotty/tools/dotc/transform/init/Util.scala index ca30e2d32a4d..e11d0e1e21a5 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Util.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Util.scala @@ -112,5 +112,5 @@ object Util: /** Whether the class or its super class/trait contains any mutable fields? */ def isMutable(cls: ClassSymbol)(using Context): Boolean = - cls.classInfo.decls.exists(_.isMutableVarOrAccessor) || + cls.classInfo.decls.exists(_.is(Flags.Mutable)) || cls.parentSyms.exists(parentCls => isMutable(parentCls.asClass)) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index eefa919d401d..e8d3a75b4dec 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -37,7 +37,7 @@ import config.Feature, Feature.{sourceVersion, modularity} import config.SourceVersion.* import config.MigrationVersion import printing.Formatting.hlAsKeyword -import cc.{isCaptureChecking, isRetainsLike, isUpdateMethod} +import cc.{isCaptureChecking, isRetainsLike} import collection.mutable import reporting.* @@ -596,7 +596,7 @@ object Checking { if (sym.isConstructor && !sym.isPrimaryConstructor && sym.owner.is(Trait, butNot = JavaDefined)) val addendum = if ctx.settings.Ydebug.value then s" ${sym.owner.flagsString}" else "" fail(em"Traits cannot have secondary constructors$addendum") - checkApplicable(Inline, sym.isTerm && !sym.is(Module) && !sym.isMutableVarOrAccessor) + checkApplicable(Inline, sym.isTerm && !sym.isOneOf(Mutable | Module)) checkApplicable(Lazy, !sym.isOneOf(Method | Mutable)) if (sym.isType && !sym.isOneOf(Deferred | JavaDefined)) for (cls <- sym.allOverriddenSymbols.filter(_.isClass)) { @@ -605,12 +605,8 @@ object Checking { } if sym.isWrappedToplevelDef && !sym.isType && sym.flags.is(Infix, butNot = Extension) then fail(ModifierNotAllowedForDefinition(Flags.Infix, s"A top-level ${sym.showKind} cannot be infix.")) - if sym.isUpdateMethod && !sym.owner.derivesFrom(defn.Caps_Mutable) then - fail(em"Update methods can only be used as members of classes extending the `Mutable` trait") checkApplicable(Erased, - !sym.is(Lazy, butNot = Given) - && !sym.isMutableVarOrAccessor - && (!sym.isType || sym.isClass)) + !sym.isOneOf(MutableOrLazy, butNot = Given) && !sym.isType || sym.isClass) checkCombination(Final, Open) checkCombination(Sealed, Open) checkCombination(Final, Sealed) diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 58119981dfc4..13e75be75838 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -85,7 +85,7 @@ object ErrorReporting { /** An explanatory note to be added to error messages * when there's a problem with abstract var defs */ def abstractVarMessage(sym: Symbol): String = - if sym.underlyingSymbol.isMutableVarOrAccessor then + if (sym.underlyingSymbol.is(Mutable)) "\n(Note that variables need to be initialized to be defined)" else "" diff --git a/compiler/src/dotty/tools/dotc/typer/Nullables.scala b/compiler/src/dotty/tools/dotc/typer/Nullables.scala index 86b9a337e69a..310ca999f4c5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Nullables.scala +++ b/compiler/src/dotty/tools/dotc/typer/Nullables.scala @@ -253,7 +253,7 @@ object Nullables: val mutables = infos.foldLeft(Set[TermRef]()): (ms, info) => ms.union( if info.asserted == null then Set.empty - else info.asserted.filter(_.symbol.isMutableVarOrAccessor)) + else info.asserted.filter(_.symbol.is(Mutable))) infos.extendWith(NotNullInfo(Set(), mutables)) end extension @@ -307,7 +307,7 @@ object Nullables: || s.isClass // not in a class || recur(s.owner)) - refSym.isMutableVarOrAccessor // if it is immutable, we don't need to check the rest conditions + refSym.is(Mutable) // if it is immutable, we don't need to check the rest conditions && refOwner.isTerm && recur(ctx.owner) end extension @@ -574,7 +574,7 @@ object Nullables: object dropNotNull extends TreeMap: var dropped: Boolean = false override def transform(t: Tree)(using Context) = t match - case AssertNotNull(t0) if t0.symbol.isMutableVarOrAccessor => + case AssertNotNull(t0) if t0.symbol.is(Mutable) => nullables.println(i"dropping $t") dropped = true transform(t0) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 4e7c4336b852..59993a69797d 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -130,7 +130,7 @@ trait QuotesAndSplices { report.error("Open pattern expected an identifier", arg.srcPos) EmptyTree } - for arg <- typedArgs if arg.symbol.isMutableVarOrAccessor do // TODO support these patterns. Possibly using scala.quoted.util.Var + for arg <- typedArgs if arg.symbol.is(Mutable) do // TODO support these patterns. Possibly using scala.quoted.util.Var report.error("References to `var`s cannot be used in higher-order pattern", arg.srcPos) val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) val patType = (tree.typeargs.isEmpty, tree.args.isEmpty) match diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 47d0b51ca28b..4dbeac7219c1 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -21,7 +21,7 @@ import config.MigrationVersion import config.Printers.refcheck import reporting.* import Constants.Constant -import cc.{stripCapturing, isUpdateMethod} +import cc.stripCapturing object RefChecks { import tpd.* @@ -594,7 +594,7 @@ object RefChecks { overrideError("needs `override` modifier") else if (other.is(AbsOverride) && other.isIncompleteIn(clazz) && !member.is(AbsOverride)) overrideError("needs `abstract override` modifiers") - else if member.is(Override) && other.isMutableVarOrAccessor then + else if member.is(Override) && other.is(Mutable) then overrideError("cannot override a mutable variable") else if (member.isAnyOverride && !(member.owner.thisType.baseClasses exists (_ isSubClass other.owner)) && @@ -615,8 +615,6 @@ object RefChecks { overrideError("is erased, cannot override non-erased member") else if (other.is(Erased) && !member.isOneOf(Erased | Inline)) // (1.9) overrideError("is not erased, cannot override erased member") - else if member.isUpdateMethod && !other.is(Mutable) then - overrideError(i"is an update method, cannot override a read-only method") else if other.is(Inline) && !member.is(Inline) then // (1.10) overrideError("is not inline, cannot implement an inline method") else if (other.isScala2Macro && !member.isScala2Macro) // (1.11) @@ -774,7 +772,7 @@ object RefChecks { // Give a specific error message for abstract vars based on why it fails: // It could be unimplemented, have only one accessor, or be uninitialized. - if underlying.isMutableVarOrAccessor then + if (underlying.is(Mutable)) { val isMultiple = grouped.getOrElse(underlying.name, Nil).size > 1 // If both getter and setter are missing, squelch the setter error. @@ -783,6 +781,7 @@ object RefChecks { if (member.isSetter) "\n(Note that an abstract var requires a setter in addition to the getter)" else if (member.isGetter && !isMultiple) "\n(Note that an abstract var requires a getter in addition to the setter)" else err.abstractVarMessage(member)) + } else if (underlying.is(Method)) { // If there is a concrete method whose name matches the unimplemented // abstract method, and a cursory examination of the difference reveals diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 10965e71100d..8ba63dfc1e67 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1351,7 +1351,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer cpy.Assign(tree)(lhsCore, typed(tree.rhs, lhs1.tpe.widen)).withType(defn.UnitType) def canAssign(sym: Symbol) = - sym.isMutableVar || + sym.is(Mutable, butNot = Accessor) || ctx.owner.isPrimaryConstructor && !sym.is(Method) && sym.maybeOwner == ctx.owner.owner || // allow assignments from the primary constructor to class fields ctx.owner.name.is(TraitSetterName) || ctx.owner.isStaticConstructor diff --git a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala index 0c2929283ee3..3699ca80d011 100644 --- a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -157,7 +157,7 @@ class VarianceChecker(using Context) { def isLocal = base.isAllOf(PrivateLocal) || base.is(Private) && !base.hasAnnotation(defn.AssignedNonLocallyAnnot) - if base.isMutableVar && !isLocal then + if base.is(Mutable, butNot = Method) && !isLocal then base.removeAnnotation(defn.AssignedNonLocallyAnnot) variance = 0 try checkInfo(base.info) diff --git a/compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala b/compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala index 03392fe8cb23..b243145c9e5f 100644 --- a/compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala +++ b/compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala @@ -42,11 +42,6 @@ abstract class SimpleIdentitySet[+Elem <: AnyRef] { if (that.contains(x)) s else s + x } - def ** [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] = - if this.size == 0 then this - else if that.size == 0 then that - else this.filter(that.contains) - def == [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): Boolean = this.size == that.size && forall(that.contains) diff --git a/docs/_docs/internals/exclusive-capabilities.md b/docs/_docs/internals/exclusive-capabilities.md deleted file mode 100644 index 97c6592ac693..000000000000 --- a/docs/_docs/internals/exclusive-capabilities.md +++ /dev/null @@ -1,551 +0,0 @@ -# Exclusive Capabilities - -Language design draft - - -## Capability Kinds - -A capability is called - - _exclusive_ if it is `cap` or it has an exclusive capability in its capture set. - - _shared_ otherwise. - -There is a new top capability `shared` which can be used as a capability for deriving shared capture sets. Other shared capabilities are created as read-only versions of exclusive capabilities. - -## Update Methods - -We introduce a new trait -```scala -trait Mutable -``` -It is used as a base trait for types that define _update methods_ using -a new modifier `mut`. - -`mut` can only be used in classes or objects extending `Mutable`. An update method is allowed to access exclusive capabilities in the method's environment. By contrast, a normal method in a type extending `Mutable` may access exclusive capabilities only if they are defined locally or passed to it in parameters. - -**Example:** -```scala -class Ref(init: Int) extends Mutable: - private var current = init - def get: Int = current - mut def put(x: Int): Unit = current = x -``` -Here, `put` needs to be declared as an update method since it accesses the exclusive write capability of the variable `current` in its environment. -`mut` can also be used on an inner class of a class or object extending `Mutable`. It gives all code in the class the right -to access exclusive capabilities in the class environment. Normal classes -can only access exclusive capabilities defined in the class or passed to it in parameters. - -```scala -object Registry extends Mutable: - var count = 0 - mut class Counter: - mut def next: Int = - count += 1 - count -``` -Normal method members of `Mutable` classes cannot call update methods. This is indicated since accesses in the callee are recorded in the caller. So if the callee captures exclusive capabilities so does the caller. - -An update method cannot implement or override a normal method, whereas normal methods may implement or override update methods. Since methods such as `toString` or `==` inherited from Object are normal methods, it follows that none of these methods may be implemented as an update method. - -The `apply` method of a function type is also a normal method, hence `Mutable` classes may not implement a function type with an update method as the `apply` method. - -## Mutable Types - -A type is called a _mutable_ if it extends `Mutable` and it has an update method or an update class as non-private member or constructor. - -When we create an instance of a mutable type we always add `cap` to its capture set. For instance, if class `Ref` is declared as shown previously then `new Ref(1)` has type `Ref[Int]^{cap}`. - -**Restriction:** A non-mutable type cannot be downcast by a pattern match to a mutable type. - -**Definition:** A class is _read_only_ if the following conditions are met: - - 1. It does not extend any exclusive capabilities from its environment. - 2. It does not take parameters with exclusive capabilities. - 3. It does not contain mutable fields, or fields that take exclusive capabilities. - -**Restriction:** If a class or trait extends `Mutable` all its parent classes or traits must either extend `Mutable` or be read-only. - -The idea is that when we upcast a reference to a type extending `Mutable` to a type that does not extend `Mutable`, we cannot possibly call a method on this reference that uses an exclusive capability. Indeed, by the previous restriction this class must be a read-only class, which means that none of the code implemented -in the class can access exclusive capabilities on its own. And we -also cannot override any of the methods of this class with a method -accessing exclusive capabilities, since such a method would have -to be an update method and update methods are not allowed to override regular methods. - - - -**Example:** - -Consider trait `IterableOnce` from the standard library. - -```scala -trait IterableOnce[+T] extends Mutable: - def iterator: Iterator[T]^{this} - mut def foreach(op: T => Unit): Unit - mut def exists(op: T => Boolean): Boolean - ... -``` -The trait is a mutable type with many update methods, among them `foreach` and `exists`. These need to be classified as `mut` because their implementation in the subtrait `Iterator` uses the update method `next`. -```scala -trait Iterator[T] extends IterableOnce[T]: - def iterator = this - def hasNext: Boolean - mut def next(): T - mut def foreach(op: T => Unit): Unit = ... - mut def exists(op; T => Boolean): Boolean = ... - ... -``` -But there are other implementations of `IterableOnce` that are not mutable types (even though they do indirectly extend the `Mutable` trait). Notably, collection classes implement `IterableOnce` by creating a fresh -`iterator` each time one is required. The mutation via `next()` is then restricted to the state of that iterator, whereas the underlying collection is unaffected. These implementations would implement each `mut` method in `IterableOnce` by a normal method without the `mut` modifier. - -```scala -trait Iterable[T] extends IterableOnce[T]: - def iterator = new Iterator[T] { ... } - def foreach(op: T => Unit) = iterator.foreach(op) - def exists(op: T => Boolean) = iterator.exists(op) -``` -Here, `Iterable` is not a mutable type since it has no update method as member. -All inherited update methods are (re-)implemented by normal methods. - -**Note:** One might think that we don't need a base trait `Mutable` since in any case -a mutable type is defined by the presence of update methods, not by what it extends. In fact the importance of `Mutable` is that it defines _the other methods_ as read-only methods that _cannot_ access exclusive capabilities. For types not extending `Mutable`, this is not the case. For instance, the `apply` method of a function type is not an update method and the type itself does not extend `Mutable`. But `apply` may well be implemented by -a method that accesses exclusive capabilities. - - - -## Read-only Capabilities - -If `x` is an exclusive capability of a type extending `Mutable`, `x.rd` is its associated, shared _read-only_ capability. - -`shared` can be understood as the read-only capability corresponding to `cap`. -```scala - shared = cap.rd -``` - -A _top capability_ is either `cap` or `shared`. - - -## Shorthands - -**Meaning of `^`:** - -The meaning of `^` and `=>` is the same as before: - - - `C^` means `C^{cap}`. - - `A => B` means `(A -> B)^{cap}`. - -**Implicitly added capture sets** - -A reference to a type extending any of the traits `Capability` or `Mutable` gets an implicit capture set `{shared}` in case no explicit capture set is given. - -For instance, a matrix multiplication method can be expressed as follows: - -```scala -class Matrix(nrows: Int, ncols: Int) extends Mutable: - mut def update(i: Int, j: Int, x: Double): Unit = ... - def apply(i: Int, j: Int): Double = ... - -def mul(a: Matrix, b: Matrix, c: Matrix^): Unit = - // multiply a and b, storing the result in c -``` -Here, `a` and `b` are implicitly read-only, and `c`'s type has capture set `cap`. I.e. with explicit capture sets this would read: -```scala -def mul(a: Matrix^{shared}, b: Matrix^{shared}, c: Matrix^{cap}): Unit -``` -Separation checking will then make sure that `a` and `b` must be different from `c`. - - -## Capture Sets - -As the previous example showed, we would like to use a `Mutable` type such as `Array` or `Matrix` in two permission levels: read-only and unrestricted. A standard technique is to invent a type qualifier such as "read-only" or "mutable" to indicate access permissions. What we would like to do instead is to combine the qualifier with the capture set of a type. So we -distinguish two kinds of capture sets: regular and read-only. Read-only sets can contain only shared capabilities. - -Internally, in the discussion that follows we use a label after the set to indicate its mode. `{...}_` is regular and `{...}rd` is read-only. We could envisage source language to specify read-only sets, e.g. something like - -```scala -{io, async}.rd -``` - -But in almost all cases we don't need an explicit mode in source code to indicate the kind of capture set, since the contents of the set itself tell us what kind it is. A capture set is assumed to be read-only if it is on a -type extending `Mutable` and it contains only shared capabilities, otherwise it is assumed to be regular. - -The read-only function `ro` maps capture sets to read-only capture sets. It is defined pointwise on capabilities as follows: - - - `ro ({ x1, ..., xn } _) = { ro(x1), ..., ro(xn) }` - - `ro(x) = x` if `x` is shared - - `ro(x) = x.rd` if `x` is exclusive - - - -## Subcapturing - -Subcapturing has to take the mode of capture sets into account. We let `m` stand for arbitrary modes. - -1. Rule (sc-var) comes in two variants. If `x` is defined as `S^C` then - - - `{x, xs} m <: (C u {xs}) m` - - `{x.rd, xs} m <: (ro(C) u {xs}) m` - -3. The subset rule works only between sets of the same kind: - - - `C _ <: C _ u {x}` - - `C rd <: C rd u {x}` if `x` is a shared capability. - -4. We can map regular capture sets to read-only sets: - - - `C _ <: ro(C) rd` - -5. Read-only capabilities in regular capture sets can be widened to exclusive capabilities: - - - `{x.rd, xs} _ <: {x, xs} _` - -One case where an explicit capture set mode would be useful concerns -refinements of type variable bounds, as in the following example. -```scala -class A: - type T <: Object^{x.rd, y} -class B extends A: - type T <: Object^{x.rd} -class C extends B: - type T = Matrix^{x.rd} -``` -We assume that `x` and `y` are exclusive capabilities. -The capture set of type `T` in class `C` is a read-only set since `Matrix` extends `Mutable`. But the capture sets of the occurrences of -`T` in `A` and `B` are regular. This leads to an error in bounds checking -the definition of `T` in `C` against the one in `B` -since read-only sets do not subcapture regular sets. We can fix the -problem by declaring the capture set in class `B` as read-only: -```scala -class B extends A: - type T <: Object^{x.rd}.rd -``` -But now a different problem arises since the capture set of `T` in `B` is -read-only but the capture set of `T` and `A` is regular. The capture set of -`T` in `A` cannot be made read-only since it contains an exclusive capability `y`. So we'd have to drop `y` and declare class `A` like this: -```scala -class A: - type T <: Object^{x.rd}.rd -``` - - - -## Accesses to Mutable Types - -A _read-only access_ is a reference `x` to a type extending `Mutable` with a regular capture set if the expected type is one of the following: - - - a value type that is not a mutable type, or - - a select prototype with a member that is a normal method or class (not an update method or class). - -A read-only access contributes the read-only capability `x.rd` to its environment (as formalized by _cv_). Other accesses contribute the full capability `x`. - -A reference `p.m` to an update method or class `m` of a mutable type is allowed only if `p`'s capture set is regular. - -If `e` is an expression of a type `T^cs` extending `Mutable` and the expected type is a value type that is not a mutable type, then the type of `e` is mapped to `T^ro(cs)`. - - -## Expression Typing - -An expression's type should never contain a top capability in its deep capture set. This is achieved by the following rules: - - - On var access `x`: - - - replace all direct capture sets with `x` - - replace all boxed caps with `x*` - - _Variant_: If the type of the typevar corresponding to a boxed cap can be uniquely reached by a path `this.p`, replace the `cap` with `x.p*`. - - - On select `t.foo` where `C` is the capture set of `t`: apply the SELECT rule, which amounts to: - - - replace all direct caps with `C` - - replace all boxed caps with `C*` - - - On applications: `t(args)`, `new C(args)` if the result type `T` contains `cap` (deeply): - - - create a fresh skolem `val sk: T` - - set result type to `sk.type` - - Skolem symbols are eliminated before they reach the type of the enclosing val or def. - - - When avoiding a variable in a local block, as in: - ```scala - { val x: T^ = ...; ... r: List[T^{x}] } - ``` - where the capture set of `x` contains a top capability, - replace `x` by a fresh skolem `val sk: T`. Alternatively: keep it as is, but don't widen it. - - -## Post Processing Right Hand Sides - -The type of the right hand sides of `val`s or `def`s is post-processed before it becomes the inferred type or is compared with the declared type. Post processing -means that all local skolems in the type are avoided, which might mean `cap` can now occur in the the type. - -However, if a local skolem `sk` has `cap` as underlying type, but is only used -in its read-only form `sk.rd` in the result type, we can drop the skolem instead of widening to `shared`. - -**Example:** - -```scala - def f(x: Int): Double = ... - - def precomputed(n: Int)(f: Int -> Double): Int -> Double = - val a: Array[Double]^ = Array.tabulate(n)(f) - a(_) -``` -Here, `Array.tabulate(n)(f)` returns a value of type `Array[Double]^{cap}`. -The last expression `a(_)` expands to the closure `idx => a(idx)`, which -has type `Int ->{a.rd} Double`, since `a` appears only in the context of a -selection with the `apply` method of `Array`, which is not an update method. The type of the enclosing block then has type `Int ->{sk.rd} Double` for a fresh skolem `sk`, -since `a` is no longer visible. After post processing, this type becomes -`Int -> Double`. - -This pattern allows to use mutation in the construction of a local data structure, returning a pure result when the construction is done. Such -data structures are said to have _transient mutability_. - -## Separation checking - -Separation checking checks that we don't have hidden aliases. A hidden alias arises when we have two definitions `x` and `y` with overlapping transitive capture sets that are not manifest in the types of `x` and `y` because one of these types has widened the alias to a top capability. - -Since expression types can't mention cap, widening happens only - - when passing an argument to a parameter - - when widening to a declared (result) type of a val or def - -**Definitions:** - - - The _transitive capture set_ `tcs(c)` of a capability `c` with underlying capture set `C` is `c` itself, plus the transitive capture set of `C`, but excluding `cap` or `shared`. - - - The _transitive capture set_ `tcs(C)` of a capture set C is the union - of `tcs(c)` for all elements `c` of `C`. - - - Two capture sets _interfere_ if one contains an exclusive capability `x` and the other - also contains `x` or contains the read-only capability `x.rd`. - - - If `C1 <: C2` and `C2` contains a top capability, then let `C2a` be `C2` without top capabilities. The hidden set `hidden(C1, C2)` of `C1` relative to `C2` is the smallest subset `C1h` of `C1` such that `C1 \ C1h <: C2a`. - - - If `T1 <: T2` then let the hidden set `hidden(T1, T2)` of `T1` relative to `T2` be the - union of all hidden sets of corresponding capture sets in `T1` and `T2`. - - -**Algorithm outline:** - - - Associate _shadowed sets_ with blocks, template statement sequences, applications, and val symbols. The idea is that a shadowed set gets populated when a capture reference is widened to cap. In that case the original references that were widened get added to the set. - - - After processing a `val x: T2 = t` with `t: T1` after post-processing: - - - If `T2` is declared, add `tcs(hidden(T1, T2))` to the shadowed set - of the enclosing statement sequence and remember it as `shadowed(x)`. - - If`T2` is inferred, add `tcs(T1)` to the shadowed set - of the enclosing statement sequence and remember it as `shadowed(x)`. - - - When processing the right hand side of a `def f(params): T2 = t` with `t: T1` after post-processing - - - If `T2` is declared, check that `shadowed*(hidden(T1, T2))` contains only local values (including skolems). - - If `T2` is inferred, check that `shadowed*(tcs(T1))` contains only local values (including skolems). - - Here, `shadowed*` is the transitive closure of `shadowed`. - - - When processing an application `p.f(arg1, ..., arg_n)`, after processing `p`, add its transitive capture set to the shadowed set of the call. Then, in sequence, process each argument by adding `tcs(hidden(T1, T2))` to the shadowed set of the call, where `T1` is the argument type and `T2` is the type of the formal parameter. - - - When adding a reference `r` or capture set `C` in `markFree` to enclosing environments, check that `tcs(r)` (respectively, `tcs(C)`) does not interfere with an enclosing shadowed set. - - -This requires, first, a linear processing of the program in evaluation order, and, second, that all capture sets are known. Normal rechecking violates both of these requirements. First, definitions -without declared result types are lazily rechecked using completers. Second, capture sets are constructed -incrementally. So we probably need a second scan after rechecking proper. In order not to duplicate work, we need to record during rechecking all additions to environments via `markFree`. - -**Notes:** - - - Mutable variables are not allowed to have top capabilities in their deep capture sets, so separation checking is not needed for checking var definitions or assignments. - - - A lazy val can be thought of conceptually as a value with possibly a capturing type and as a method computing that value. A reference to a lazy val is interpreted as a call to that method. It's use set is the reference to the lazy val itself as well as the use set of the called method. - - - - -## Escape Checking - -The rules for separation checking also check that capabilities do not escape. Separate -rules for explicitly preventing cap to be boxed or unboxed are not needed anymore. Consider the canonical `withFile` example: -```scala -def withFile[T](body: File^ => T): T = - ... - -withFile: f => - () => f.write("too late") -``` -Here, the argument to `withFile` has the dependent function type -```scala -(f: File^) -> () ->{f} Unit -``` -A non-dependent type is required so the expected result type of the closure is -``` -() ->{cap} Unit -``` -When typing a closure, we type an anonymous function. The result type of that function is determined by type inference. That means the generated closure looks like this -```scala -{ def $anon(f: File^): () ->{cap} Unit = - () => f.write("too late") - $anon -} -``` -By the rules of separation checking the hidden set of the body of $anon is `f`, which refers -to a value outside the rhs of `$anon`. This is illegal according to separation checking. - -In the last example, `f: File^` was an exclusive capability. But it could equally have been a shared capability, i.e. `withFile` could be formulated as follows: -```scala -def withFile[T](body: File^{shared} => T): T = -``` -The same reasoning as before would enforce that there are no leaks. - - -## Mutable Variables - -Local mutable variables are tracked by default. It is essentially as if a mutable variable `x` was decomposed into a new private field of class `Ref` together with a getter and setter. I.e. instead of -```scala -var x: T = init -``` -we'd deal with -```scala -val x$ = Ref[T](init) -def x = x$.get -mut def x_=(y: T) = x$.put(y) -``` - -There should be a way to exclude a mutable variable or field from tracking. Maybe an annotation or modifier such as `transparent` or `untracked`? - -The expansion outlined above justifies the following rules for handling mutable variables directly: - - - A type with non-private tracked mutable fields is classified as mutable. - It has to extend the `Mutable` class. - - A read access to a local mutable variable `x` charges the capability `x.rd` to the environment. - - An assignment to a local mutable variable `x` charges the capability `x` to the environment. - - A read access to a mutable field `this.x` charges the capability `this.rd` to the environment. - - A write access to a mutable field `this.x` charges the capability `this` to the environment. - -Mutable Scopes -============== - -We sometimes want to make separation checking coarser. For instance when constructing a doubly linked list we want to create `Mutable` objects and -store them in mutable variables. Since a variable's type cannot contain `cap`, -we must know beforehand what mutable objects it can be refer to. This is impossible if the other objects are created later. - -Mutable scopes provide a solution to this they permit to derive a set of variables from a common exclusive reference. We define a new class -```scala -class MutableScope extends Mutable -``` -To make mutable scopes useful, we need a small tweak -of the rule governing `new` in the _Mutable Types_ section. The previous rule was: - -> When we create an instance of a mutable type we always add `cap` to its capture set. - -The new rule is: - -> When we create an instance of a mutable type we search for a given value of type `MutableScope`. If such a value is found (say it is `ms`) then we use -`ms` as the capture set of the created instance. Otherwise we use `cap`. - -We could envisage using mutable scopes like this: -``` -object enclave: - private given ms: MutableScope() - - ... -``` -Within `enclave` all mutable objects have `ms` as their capture set. So they can contain variables that also have `ms` as their capture set of their values. - -Mutable scopes should count as mutable types (this can be done either by decree or by adding an update method to `MutableScope`). Hence, mutable scopes can themselves be nested inside other mutable scopes. - -## Consumed Capabilities - -We allow `consume` as a modifier on parameters and methods. Example: - -```scala -class C extends Capability - -class Channel[T]: - def send(consume x: T) - - - -class Buffer[+T] extends Mutable: - consume def append(x: T): Buffer[T]^ - -b.append(x) -b1.append(y) - -def concat[T](consume buf1: Buffer[T]^, buf2: Buffer[T]): Buffer[T]^ - -A ->{x.consume} B - - -A - - C , Gamma, x: S |- t; T - --------------------------- - , Gamma |- (x -> t): S ->C T - - - C, Gamma |- let x = s in t: T - - -class Iterator[T]: - consume def filter(p: T => Boolean): Iterator[T]^ - consume def exists(p: T => Boolean): Boolean -``` - -As a parameter, `consume` implies `^` as capture set of the parameter type. The `^` can be given, but is redundant. - -When a method with a `consume` parameter of type `T2^` is called with an argument of type `T1`, we add the elements of `tcs(hidden(T1, T2^))` not just to the enclosing shadowed set but to all enclosing shadowed sets where elements are visible. This makes these elements permanently inaccessible. - - - -val f = Future { ... } -val g = Future { ... } - - -A parameter is implicitly @unbox if it contains a boxed cap. Example: - -def apply[T](f: Box[T => T], y: T): T = - xs.head(y) - -def compose[T](fs: @unbox List[T => T]) = - xs.foldRight(identity)((f: T => T, g: T => T) => x => g(f(x))) - - - -compose(List(f, g)) - -f :: g :: Nil - -def compose[T](fs: List[Unbox[T => T]], x: T) = - val combined = (xs.foldRight(identity)((f: T => T, g: T => T) => x => g(f(x)))): T->{fs*} T - combined(x) - - -With explicit diff --git a/library/src/scala/annotation/internal/freshCapability.scala b/library/src/scala/annotation/internal/freshCapability.scala deleted file mode 100644 index 210220ec0a89..000000000000 --- a/library/src/scala/annotation/internal/freshCapability.scala +++ /dev/null @@ -1,10 +0,0 @@ -package scala.annotation -package internal - -/** An annotation used internally for fresh capability wrappers of `cap`. - * A fresh capability is encoded as `caps.cap @freshCapability(...)` where - * `freshCapability(...)` is a special kind of annotation of type `Fresh.Annot` - * that contains a hidden set. - */ -class freshCapability extends StaticAnnotation - diff --git a/library/src/scala/annotation/internal/readOnlyCapability.scala b/library/src/scala/annotation/internal/readOnlyCapability.scala deleted file mode 100644 index 8e939aea6bb9..000000000000 --- a/library/src/scala/annotation/internal/readOnlyCapability.scala +++ /dev/null @@ -1,7 +0,0 @@ -package scala.annotation -package internal - -/** An annotation that marks a capture ref as a read-only capability. - * `x.rd` is encoded as `x.type @readOnlyCapability` - */ -class readOnlyCapability extends StaticAnnotation diff --git a/library/src/scala/caps.scala b/library/src/scala/caps.scala index 4444bdf7e5b3..c35b3b55e813 100644 --- a/library/src/scala/caps.scala +++ b/library/src/scala/caps.scala @@ -16,10 +16,6 @@ import annotation.{experimental, compileTimeOnly, retainsCap} @deprecated("Use `Capability` instead") type Cap = Capability - trait Mutable extends Capability - - trait SharedCapability extends Capability - /** Carrier trait for capture set type parameters */ trait CapSet extends Any @@ -45,12 +41,6 @@ import annotation.{experimental, compileTimeOnly, retainsCap} */ extension (x: Any) def reachCapability: Any = x - /** Read-only capabilities x.rd which appear as terms in @retains annotations are encoded - * as `caps.readOnlyCapability(x)`. When converted to CaptureRef types in capture sets - * they are represented as `x.type @annotation.internal.readOnlyCapability`. - */ - extension (x: Any) def readOnlyCapability: Any = x - /** A trait to allow expressing existential types such as * * (x: Exists) => A ->{x} B @@ -62,32 +52,11 @@ import annotation.{experimental, compileTimeOnly, retainsCap} */ final class untrackedCaptures extends annotation.StaticAnnotation - /** An annotation on parameters `x` stating that the method's body makes - * use of the reach capability `x*`. Consequently, when calling the method - * we need to charge the deep capture set of the actual argiment to the - * environment. - * - * Note: This should go into annotations. For now it is here, so that we + /** This should go into annotations. For now it is here, so that we * can experiment with it quickly between minor releases */ final class use extends annotation.StaticAnnotation - /** An annotations on parameters and update methods. - * On a parameter it states that any capabilties passed in the argument - * are no longer available afterwards, unless they are of class `SharableCapabilitty`. - * On an update method, it states that the `this` of the enclosing class is - * consumed, which means that any capabilities of the method prefix are - * no longer available afterwards. - */ - final class consume extends annotation.StaticAnnotation - - /** An annotation placed on a refinement created by capture checking. - * Refinements with this annotation unconditionally override any - * info from the parent type, so no intersection needs to be formed. - * This could be useful for tracked parameters as well. - */ - final class refineOverride extends annotation.StaticAnnotation - object unsafe: extension [T](x: T) @@ -97,9 +66,4 @@ import annotation.{experimental, compileTimeOnly, retainsCap} */ def unsafeAssumePure: T = x - /** A wrapper around code for which separation checks are suppressed. - */ - def unsafeAssumeSeparate[T](op: T): T = op - end unsafe -end caps \ No newline at end of file diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 4723fd745d6a..00e7153bcb83 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -13,8 +13,6 @@ object MiMaFilters { ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.quotedPatternsWithPolymorphicFunctions"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$quotedPatternsWithPolymorphicFunctions$"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.quoted.runtime.Patterns.higherOrderHoleWithTypes"), - ProblemFilters.exclude[MissingClassProblem]("scala.annotation.internal.freshCapability"), - ProblemFilters.exclude[MissingClassProblem]("scala.annotation.internal.readOnlyCapability"), ), // Additions since last LTS diff --git a/scala2-library-cc/src/scala/collection/IterableOnce.scala b/scala2-library-cc/src/scala/collection/IterableOnce.scala index 7ea62a9e1a65..7e8555421c53 100644 --- a/scala2-library-cc/src/scala/collection/IterableOnce.scala +++ b/scala2-library-cc/src/scala/collection/IterableOnce.scala @@ -805,7 +805,7 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A]^ => case _ => Some(reduceLeft(op)) } private final def reduceLeftOptionIterator[B >: A](op: (B, A) => B): Option[B] = reduceOptionIterator[A, B](iterator)(op) - private final def reduceOptionIterator[X >: A, B >: X](it: Iterator[X]^{this, caps.cap})(op: (B, X) => B): Option[B] = { + private final def reduceOptionIterator[X >: A, B >: X](it: Iterator[X]^)(op: (B, X) => B): Option[B] = { if (it.hasNext) { var acc: B = it.next() while (it.hasNext) diff --git a/scala2-library-cc/src/scala/collection/View.scala b/scala2-library-cc/src/scala/collection/View.scala index 482884835cb1..132934dbe3bd 100644 --- a/scala2-library-cc/src/scala/collection/View.scala +++ b/scala2-library-cc/src/scala/collection/View.scala @@ -16,7 +16,6 @@ import scala.annotation.{nowarn, tailrec} import scala.collection.mutable.{ArrayBuffer, Builder} import scala.collection.immutable.LazyList import language.experimental.captureChecking -import caps.unsafe.unsafeAssumeSeparate /** Views are collections whose transformation operations are non strict: the resulting elements * are evaluated only when the view is effectively traversed (e.g. using `foreach` or `foldLeft`), @@ -151,12 +150,10 @@ object View extends IterableFactory[View] { object Filter { def apply[A](underlying: Iterable[A]^, p: A => Boolean, isFlipped: Boolean): Filter[A]^{underlying, p} = underlying match { - case filter: Filter[A]^{underlying} if filter.isFlipped == isFlipped => - unsafeAssumeSeparate: - // See filter-iterable.scala for a test where a variant of Filter - // works without the unsafeAssumeSeparate. But it requires significant - // changes compared to the version here. See also Filter in colltest5.CollectionStrawManCC5_1. - new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped) + case filter: Filter[A] if filter.isFlipped == isFlipped => + new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped) + .asInstanceOf[Filter[A]^{underlying, p}] + // !!! asInstanceOf needed once paths were added, see path-patmat-should-be-pos.scala for minimization case _ => new Filter(underlying, p, isFlipped) } } diff --git a/scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala b/scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala index f12576033622..28ce8da104aa 100644 --- a/scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala +++ b/scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala @@ -24,8 +24,7 @@ import scala.language.implicitConversions import scala.runtime.Statics import language.experimental.captureChecking import annotation.unchecked.uncheckedCaptures -import caps.{cap, untrackedCaptures} -import caps.unsafe.unsafeAssumeSeparate +import caps.untrackedCaptures /** This class implements an immutable linked list. We call it "lazy" * because it computes its elements only when they are needed. @@ -683,8 +682,7 @@ final class LazyListIterable[+A] private(@untrackedCaptures lazyState: () => Laz remaining -= 1 scout = scout.tail } - unsafeAssumeSeparate: - dropRightState(scout) + dropRightState(scout) } } @@ -880,7 +878,7 @@ final class LazyListIterable[+A] private(@untrackedCaptures lazyState: () => Laz // if cursor (eq scout) has state defined, it is empty; else unknown state if (!cursor.stateDefined) b.append(sep).append("") } else { - @inline def same(a: LazyListIterable[A]^, b: LazyListIterable[A]^{cap, a}): Boolean = (a eq b) || (a.state eq b.state) + @inline def same(a: LazyListIterable[A]^, b: LazyListIterable[A]^): Boolean = (a eq b) || (a.state eq b.state) // Cycle. // If we have a prefix of length P followed by a cycle of length C, // the scout will be at position (P%C) in the cycle when the cursor @@ -1054,9 +1052,7 @@ object LazyListIterable extends IterableFactory[LazyListIterable] { val head = it.next() rest = rest.tail restRef = rest // restRef.elem = rest - sCons(head, newLL( - unsafeAssumeSeparate( - stateFromIteratorConcatSuffix(it)(flatMapImpl(rest, f).state)))) + sCons(head, newLL(stateFromIteratorConcatSuffix(it)(flatMapImpl(rest, f).state))) } else State.Empty } } @@ -1182,7 +1178,7 @@ object LazyListIterable extends IterableFactory[LazyListIterable] { * @param f the function that's repeatedly applied * @return the LazyListIterable returning the infinite sequence of values `start, f(start), f(f(start)), ...` */ - def iterate[A](start: => A)(f: A ->{cap, start} A): LazyListIterable[A]^{start, f} = + def iterate[A](start: => A)(f: A => A): LazyListIterable[A]^{start, f} = newLL { val head = start sCons(head, iterate(f(head))(f)) diff --git a/scala2-library-cc/src/scala/collection/mutable/CheckedIndexedSeqView.scala b/scala2-library-cc/src/scala/collection/mutable/CheckedIndexedSeqView.scala index 1c3f669f5358..9ce0399e0662 100644 --- a/scala2-library-cc/src/scala/collection/mutable/CheckedIndexedSeqView.scala +++ b/scala2-library-cc/src/scala/collection/mutable/CheckedIndexedSeqView.scala @@ -14,11 +14,10 @@ package scala package collection package mutable import language.experimental.captureChecking -import caps.cap private[mutable] trait CheckedIndexedSeqView[+A] extends IndexedSeqView[A] { - protected val mutationCount: () ->{cap.rd} Int + protected val mutationCount: () => Int override def iterator: Iterator[A]^{this} = new CheckedIndexedSeqView.CheckedIterator(this, mutationCount()) override def reverseIterator: Iterator[A]^{this} = new CheckedIndexedSeqView.CheckedReverseIterator(this, mutationCount()) @@ -43,7 +42,7 @@ private[mutable] object CheckedIndexedSeqView { import IndexedSeqView.SomeIndexedSeqOps @SerialVersionUID(3L) - private[mutable] class CheckedIterator[A](self: IndexedSeqView[A]^, mutationCount: ->{cap.rd} Int) + private[mutable] class CheckedIterator[A](self: IndexedSeqView[A]^, mutationCount: => Int) extends IndexedSeqView.IndexedSeqViewIterator[A](self) { private[this] val expectedCount = mutationCount override def hasNext: Boolean = { @@ -53,7 +52,7 @@ private[mutable] object CheckedIndexedSeqView { } @SerialVersionUID(3L) - private[mutable] class CheckedReverseIterator[A](self: IndexedSeqView[A]^, mutationCount: ->{cap.rd} Int) + private[mutable] class CheckedReverseIterator[A](self: IndexedSeqView[A]^, mutationCount: => Int) extends IndexedSeqView.IndexedSeqViewReverseIterator[A](self) { private[this] val expectedCount = mutationCount override def hasNext: Boolean = { @@ -63,43 +62,43 @@ private[mutable] object CheckedIndexedSeqView { } @SerialVersionUID(3L) - class Id[+A](underlying: SomeIndexedSeqOps[A]^)(protected val mutationCount: () ->{cap.rd} Int) + class Id[+A](underlying: SomeIndexedSeqOps[A]^)(protected val mutationCount: () => Int) extends IndexedSeqView.Id(underlying) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class Appended[+A](underlying: SomeIndexedSeqOps[A]^, elem: A)(protected val mutationCount: () ->{cap.rd} Int) + class Appended[+A](underlying: SomeIndexedSeqOps[A]^, elem: A)(protected val mutationCount: () => Int) extends IndexedSeqView.Appended(underlying, elem) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class Prepended[+A](elem: A, underlying: SomeIndexedSeqOps[A]^)(protected val mutationCount: () ->{cap.rd} Int) + class Prepended[+A](elem: A, underlying: SomeIndexedSeqOps[A]^)(protected val mutationCount: () => Int) extends IndexedSeqView.Prepended(elem, underlying) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class Concat[A](prefix: SomeIndexedSeqOps[A]^, suffix: SomeIndexedSeqOps[A]^)(protected val mutationCount: () ->{cap.rd} Int) + class Concat[A](prefix: SomeIndexedSeqOps[A]^, suffix: SomeIndexedSeqOps[A]^)(protected val mutationCount: () => Int) extends IndexedSeqView.Concat[A](prefix, suffix) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class Take[A](underlying: SomeIndexedSeqOps[A]^, n: Int)(protected val mutationCount: () ->{cap.rd} Int) + class Take[A](underlying: SomeIndexedSeqOps[A]^, n: Int)(protected val mutationCount: () => Int) extends IndexedSeqView.Take(underlying, n) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class TakeRight[A](underlying: SomeIndexedSeqOps[A]^, n: Int)(protected val mutationCount: () ->{cap.rd} Int) + class TakeRight[A](underlying: SomeIndexedSeqOps[A]^, n: Int)(protected val mutationCount: () => Int) extends IndexedSeqView.TakeRight(underlying, n) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class Drop[A](underlying: SomeIndexedSeqOps[A]^, n: Int)(protected val mutationCount: () ->{cap.rd} Int) + class Drop[A](underlying: SomeIndexedSeqOps[A]^, n: Int)(protected val mutationCount: () => Int) extends IndexedSeqView.Drop[A](underlying, n) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class DropRight[A](underlying: SomeIndexedSeqOps[A]^, n: Int)(protected val mutationCount: () ->{cap.rd} Int) + class DropRight[A](underlying: SomeIndexedSeqOps[A]^, n: Int)(protected val mutationCount: () => Int) extends IndexedSeqView.DropRight[A](underlying, n) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class Map[A, B](underlying: SomeIndexedSeqOps[A]^, f: A => B)(protected val mutationCount: () ->{cap.rd} Int) + class Map[A, B](underlying: SomeIndexedSeqOps[A]^, f: A => B)(protected val mutationCount: () => Int) extends IndexedSeqView.Map(underlying, f) with CheckedIndexedSeqView[B] @SerialVersionUID(3L) - class Reverse[A](underlying: SomeIndexedSeqOps[A]^)(protected val mutationCount: () ->{cap.rd} Int) + class Reverse[A](underlying: SomeIndexedSeqOps[A]^)(protected val mutationCount: () => Int) extends IndexedSeqView.Reverse[A](underlying) with CheckedIndexedSeqView[A] { override def reverse: IndexedSeqView[A] = underlying match { case x: IndexedSeqView[A] => x @@ -108,7 +107,7 @@ private[mutable] object CheckedIndexedSeqView { } @SerialVersionUID(3L) - class Slice[A](underlying: SomeIndexedSeqOps[A]^, from: Int, until: Int)(protected val mutationCount: () ->{cap.rd} Int) + class Slice[A](underlying: SomeIndexedSeqOps[A]^, from: Int, until: Int)(protected val mutationCount: () => Int) extends AbstractIndexedSeqView[A] with CheckedIndexedSeqView[A] { protected val lo = from max 0 protected val hi = (until max 0) min underlying.length diff --git a/tests/neg-custom-args/captures/bad-uses-2.scala b/tests/neg-custom-args/captures/bad-uses-2.scala index 2b4d6eebb2f0..8dd121b2b134 100644 --- a/tests/neg-custom-args/captures/bad-uses-2.scala +++ b/tests/neg-custom-args/captures/bad-uses-2.scala @@ -1,13 +1,7 @@ -import caps.{use, consume} -class TestUse: +import caps.use +class Test: @use def F = ??? // error @use val x = ??? // error @use type T // error def foo[@use T](@use c: T): Unit = ??? // OK -class TestConsume: - @consume def F = ??? // ok - @consume val x = ??? // error - @consume type T // error - def foo[@consume T](@use c: T): Unit = ??? // error - diff --git a/tests/neg-custom-args/captures/box-adapt-cases.check b/tests/neg-custom-args/captures/box-adapt-cases.check index e5cadb051ac1..7ff185c499a5 100644 --- a/tests/neg-custom-args/captures/box-adapt-cases.check +++ b/tests/neg-custom-args/captures/box-adapt-cases.check @@ -1,19 +1,12 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/box-adapt-cases.scala:8:10 ------------------------------- -8 | x.value(cap => cap.use()) // error, was OK - | ^^^^^^^^^^^^^^^^ - | Found: (cap: box Cap^?) => Int - | Required: (cap: box Cap^) ->{fresh} Int - | - | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/box-adapt-cases.scala:15:10 ------------------------------ -15 | x.value(cap => cap.use()) // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/box-adapt-cases.scala:14:10 ------------------------------ +14 | x.value(cap => cap.use()) // error | ^^^^^^^^^^^^^^^^ | Found: (cap: box Cap^?) ->{io} Int | Required: (cap: box Cap^{io}) -> Int | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/box-adapt-cases.scala:29:10 ------------------------------ -29 | x.value(cap => cap.use()) // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/box-adapt-cases.scala:28:10 ------------------------------ +28 | x.value(cap => cap.use()) // error | ^^^^^^^^^^^^^^^^ | Found: (cap: box Cap^?) ->{io, fs} Int | Required: (cap: box Cap^{io, fs}) ->{io} Int diff --git a/tests/neg-custom-args/captures/box-adapt-cases.scala b/tests/neg-custom-args/captures/box-adapt-cases.scala index 7db58318ed05..8f7d7a0a6667 100644 --- a/tests/neg-custom-args/captures/box-adapt-cases.scala +++ b/tests/neg-custom-args/captures/box-adapt-cases.scala @@ -1,11 +1,10 @@ - trait Cap { def use(): Int } def test1(): Unit = { class Id[X](val value: [T] -> (op: X => T) -> T) val x: Id[Cap^] = ??? - x.value(cap => cap.use()) // error, was OK + x.value(cap => cap.use()) } def test2(io: Cap^): Unit = { diff --git a/tests/neg-custom-args/captures/box-adapt-contra.scala b/tests/neg-custom-args/captures/box-adapt-contra.scala index 95affbe2aef9..2dc79a66d932 100644 --- a/tests/neg-custom-args/captures/box-adapt-contra.scala +++ b/tests/neg-custom-args/captures/box-adapt-contra.scala @@ -1,5 +1,4 @@ - -import caps.consume +import language.experimental.captureChecking trait Cap @@ -8,7 +7,7 @@ def useCap[X](x: X): (X -> Unit) -> Unit = ??? def test1(c: Cap^): Unit = val f: (Cap^{c} -> Unit) -> Unit = useCap[Cap^{c}](c) // error -def test2(@consume c: Cap^, d: Cap^): Unit = +def test2(c: Cap^, d: Cap^): Unit = def useCap1[X](x: X): (X => Unit) -> Unit = ??? val f1: (Cap^{c} => Unit) ->{c} Unit = useCap1[Cap^{c}](c) // ok diff --git a/tests/neg-custom-args/captures/byname.check b/tests/neg-custom-args/captures/byname.check index de2078ddf30a..1c113591922d 100644 --- a/tests/neg-custom-args/captures/byname.check +++ b/tests/neg-custom-args/captures/byname.check @@ -8,10 +8,10 @@ -- Error: tests/neg-custom-args/captures/byname.scala:19:5 ------------------------------------------------------------- 19 | h(g()) // error | ^^^ - | reference (cap2 : Cap) is not included in the allowed capture set {cap1} + | reference (cap2 : Cap^) is not included in the allowed capture set {cap1} | of an enclosing function literal with expected type () ?->{cap1} I -- Error: tests/neg-custom-args/captures/byname.scala:22:12 ------------------------------------------------------------ 22 | h2(() => g())() // error | ^^^ - | reference (cap2 : Cap) is not included in the allowed capture set {cap1} + | reference (cap2 : Cap^) is not included in the allowed capture set {cap1} | of an enclosing function literal with expected type () ->{cap1} I diff --git a/tests/neg-custom-args/captures/capt-depfun.check b/tests/neg-custom-args/captures/capt-depfun.check deleted file mode 100644 index 7cd838d72dc0..000000000000 --- a/tests/neg-custom-args/captures/capt-depfun.check +++ /dev/null @@ -1,12 +0,0 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt-depfun.scala:11:43 ---------------------------------- -11 | val dc: ((Str^{y, z}) => Str^{y, z}) = ac(g()) // error // error: separatioon - | ^^^^^^^ - | Found: Str^{} ->{ac, y, z} Str^{y, z} - | Required: Str^{y, z} ->{fresh} Str^{y, z} - | - | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/capt-depfun.scala:11:24 ------------------------------------------------------- -11 | val dc: ((Str^{y, z}) => Str^{y, z}) = ac(g()) // error // error: separatioon - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Separation failure: value dc's type Str^{y, z} => Str^{y, z} hides parameters y and z. - | The parameters need to be annotated with @consume to allow this. diff --git a/tests/neg-custom-args/captures/capt-depfun.scala b/tests/neg-custom-args/captures/capt-depfun.scala index 384c403bdd27..20226b239198 100644 --- a/tests/neg-custom-args/captures/capt-depfun.scala +++ b/tests/neg-custom-args/captures/capt-depfun.scala @@ -1,6 +1,4 @@ import annotation.retains - - class C type Cap = C @retains(caps.cap) class Str @@ -8,4 +6,4 @@ class Str def f(y: Cap, z: Cap) = def g(): C @retains(y, z) = ??? val ac: ((x: Cap) => Str @retains(x) => Str @retains(x)) = ??? - val dc: ((Str^{y, z}) => Str^{y, z}) = ac(g()) // error // error: separatioon + val dc: ((Str^{y, z}) => Str^{y, z}) = ac(g()) // error diff --git a/tests/neg-custom-args/captures/capt1.check b/tests/neg-custom-args/captures/capt1.check index d9b10129e3f9..acf8faa7a969 100644 --- a/tests/neg-custom-args/captures/capt1.check +++ b/tests/neg-custom-args/captures/capt1.check @@ -1,68 +1,50 @@ --- Error: tests/neg-custom-args/captures/capt1.scala:5:11 -------------------------------------------------------------- -5 | () => if x == null then y else y // error +-- Error: tests/neg-custom-args/captures/capt1.scala:6:11 -------------------------------------------------------------- +6 | () => if x == null then y else y // error | ^ | reference (x : C^) is not included in the allowed capture set {} | of an enclosing function literal with expected type () -> C --- Error: tests/neg-custom-args/captures/capt1.scala:8:11 -------------------------------------------------------------- -8 | () => if x == null then y else y // error +-- Error: tests/neg-custom-args/captures/capt1.scala:9:11 -------------------------------------------------------------- +9 | () => if x == null then y else y // error | ^ | reference (x : C^) is not included in the allowed capture set {} | of an enclosing function literal with expected type Matchable --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:15:2 ----------------------------------------- -15 | def f(y: Int) = if x == null then y else y // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:16:2 ----------------------------------------- +16 | def f(y: Int) = if x == null then y else y // error | ^ | Found: (y: Int) ->{x} Int | Required: Matchable -16 | f +17 | f | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:22:2 ----------------------------------------- -22 | class F(y: Int) extends A: // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:23:2 ----------------------------------------- +23 | class F(y: Int) extends A: // error | ^ | Found: A^{x} | Required: A -23 | def m() = if x == null then y else y -24 | F(22) +24 | def m() = if x == null then y else y +25 | F(22) | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:27:2 ----------------------------------------- -27 | new A: // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:28:2 ----------------------------------------- +28 | new A: // error | ^ | Found: A^{x} | Required: A -28 | def m() = if x == null then y else y +29 | def m() = if x == null then y else y | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/capt1.scala:36:16 ------------------------------------------------------------- -36 | val z2 = h[() -> Cap](() => x) // error // error +-- Error: tests/neg-custom-args/captures/capt1.scala:34:16 ------------------------------------------------------------- +34 | val z2 = h[() -> Cap](() => x) // error // error | ^^^^^^^^^ - | Type variable X of method h cannot be instantiated to () -> (ex$18: caps.Exists) -> C^{ex$18} since - | the part C^{ex$18} of that type captures the root capability `cap`. --- Error: tests/neg-custom-args/captures/capt1.scala:36:30 ------------------------------------------------------------- -36 | val z2 = h[() -> Cap](() => x) // error // error + | Type variable X of method h cannot be instantiated to () -> (ex$15: caps.Exists) -> C^{ex$15} since + | the part C^{ex$15} of that type captures the root capability `cap`. +-- Error: tests/neg-custom-args/captures/capt1.scala:34:30 ------------------------------------------------------------- +34 | val z2 = h[() -> Cap](() => x) // error // error | ^ | reference (x : C^) is not included in the allowed capture set {} - | of an enclosing function literal with expected type () -> (ex$18: caps.Exists) -> C^{ex$18} --- Error: tests/neg-custom-args/captures/capt1.scala:38:13 ------------------------------------------------------------- -38 | val z3 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // error + | of an enclosing function literal with expected type () -> (ex$15: caps.Exists) -> C^{ex$15} +-- Error: tests/neg-custom-args/captures/capt1.scala:36:13 ------------------------------------------------------------- +36 | val z3 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // error | ^^^^^^^^^^^^^^^^^^^^^^^ - | Type variable X of method h cannot be instantiated to box () ->{x} (ex$23: caps.Exists) -> C^{ex$23} since - | the part C^{ex$23} of that type captures the root capability `cap`. --- Error: tests/neg-custom-args/captures/capt1.scala:43:7 -------------------------------------------------------------- -43 | if x == null then // error: separation - | ^ - | Separation failure: Illegal access to {x} which is hidden by the previous definition - | of value z1 with type () => (ex$27: caps.Exists) -> C^{ex$27}. - | This type hides capabilities {x} --- Error: tests/neg-custom-args/captures/capt1.scala:44:12 ------------------------------------------------------------- -44 | () => x // error: separation - | ^ - | Separation failure: Illegal access to {x} which is hidden by the previous definition - | of value z1 with type () => (ex$27: caps.Exists) -> C^{ex$27}. - | This type hides capabilities {x} --- Error: tests/neg-custom-args/captures/capt1.scala:47:2 -------------------------------------------------------------- -47 | x // error: separation - | ^ - | Separation failure: Illegal access to {x} which is hidden by the previous definition - | of value z1 with type () => (ex$27: caps.Exists) -> C^{ex$27}. - | This type hides capabilities {x} + | Type variable X of method h cannot be instantiated to box () ->{x} (ex$20: caps.Exists) -> C^{ex$20} since + | the part C^{ex$20} of that type captures the root capability `cap`. diff --git a/tests/neg-custom-args/captures/capt1.scala b/tests/neg-custom-args/captures/capt1.scala index 48778dbd6716..8da7e633ca51 100644 --- a/tests/neg-custom-args/captures/capt1.scala +++ b/tests/neg-custom-args/captures/capt1.scala @@ -1,4 +1,5 @@ + import annotation.retains class C def f(x: C @retains(caps.cap), y: C): () -> C = @@ -27,21 +28,10 @@ def h4(x: Cap, y: Int): A = new A: // error def m() = if x == null then y else y -def f1(c: Cap): () ->{c} c.type = () => c // ok - def foo() = val x: C @retains(caps.cap) = ??? def h[X](a: X)(b: X) = a - val z2 = h[() -> Cap](() => x) // error // error (() => C()) val z3 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // error - val z1: () => Cap = f1(x) - - val z4 = - if x == null then // error: separation - () => x // error: separation - else - () => C() - x // error: separation diff --git a/tests/neg-custom-args/captures/caseclass/Test_2.scala b/tests/neg-custom-args/captures/caseclass/Test_2.scala index 8c13a0d831ef..e54ab1774202 100644 --- a/tests/neg-custom-args/captures/caseclass/Test_2.scala +++ b/tests/neg-custom-args/captures/caseclass/Test_2.scala @@ -5,7 +5,7 @@ def test(c: C) = val mixed: () ->{c} Unit = pure val x = Ref(impure) val _: Ref = x // error - val y = caps.unsafe.unsafeAssumeSeparate(x.copy()) // TODO remove + val y = x.copy() val yc: Ref = y // error val y0 = x.copy(pure) val yc0: Ref = y0 diff --git a/tests/neg-custom-args/captures/cc-ex-conformance.scala b/tests/neg-custom-args/captures/cc-ex-conformance.scala index 3b685c5f76b1..16e13376c5b3 100644 --- a/tests/neg-custom-args/captures/cc-ex-conformance.scala +++ b/tests/neg-custom-args/captures/cc-ex-conformance.scala @@ -1,7 +1,6 @@ import language.experimental.captureChecking import caps.{Exists, Capability} - class C type EX1 = () => (c: Exists) => (C^{c}, C^{c}) @@ -16,7 +15,7 @@ def Test = val ex1: EX1 = ??? val ex2: EX2 = ??? val _: EX1 = ex1 - val _: EX2 = ex1 // error separation + val _: EX2 = ex1 // ok val _: EX1 = ex2 // ok val ex3: EX3 = ??? diff --git a/tests/neg-custom-args/captures/cc-subst-param-exact.scala b/tests/neg-custom-args/captures/cc-subst-param-exact.scala index 08a3efaaffdf..35e4acb95fdc 100644 --- a/tests/neg-custom-args/captures/cc-subst-param-exact.scala +++ b/tests/neg-custom-args/captures/cc-subst-param-exact.scala @@ -5,13 +5,13 @@ trait Ref[T] { def set(x: T): T } def test() = { def swap[T](x: Ref[T]^)(y: Ref[T]^{x}): Unit = ??? - def foo[T](x: Ref[T]^{cap.rd}): Unit = + def foo[T](x: Ref[T]^): Unit = swap(x)(x) - def bar[T](x: () => Ref[T]^{cap.rd})(y: Ref[T]^{x}): Unit = + def bar[T](x: () => Ref[T]^)(y: Ref[T]^{x}): Unit = swap(x())(y) // error - def baz[T](x: Ref[T]^{cap.rd})(y: Ref[T]^{x}): Unit = + def baz[T](x: Ref[T]^)(y: Ref[T]^{x}): Unit = swap(x)(y) } diff --git a/tests/neg-custom-args/captures/cc-this5.check b/tests/neg-custom-args/captures/cc-this5.check index a69c482300f8..21b5b36e0574 100644 --- a/tests/neg-custom-args/captures/cc-this5.check +++ b/tests/neg-custom-args/captures/cc-this5.check @@ -1,7 +1,7 @@ -- Error: tests/neg-custom-args/captures/cc-this5.scala:16:20 ---------------------------------------------------------- 16 | def f = println(c) // error | ^ - | reference (c : Cap) is not included in the allowed capture set {} + | reference (c : Cap^) is not included in the allowed capture set {} | of the enclosing class A -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-this5.scala:21:15 ------------------------------------- 21 | val x: A = this // error diff --git a/tests/neg-custom-args/captures/consume-overrides.scala b/tests/neg-custom-args/captures/consume-overrides.scala deleted file mode 100644 index 78f013349a31..000000000000 --- a/tests/neg-custom-args/captures/consume-overrides.scala +++ /dev/null @@ -1,15 +0,0 @@ -import caps.consume - -trait A[X]: - def foo(@consume x: X): X - def bar(x: X): X - -trait B extends A[C]: - def foo(x: C): C // error - def bar(@consume x: C): C // error - -trait B2: - def foo(x: C): C - def bar(@consume x: C): C - -abstract class C extends A[C], B2 // error diff --git a/tests/neg-custom-args/captures/delayedRunops.check b/tests/neg-custom-args/captures/delayedRunops.check index 372e010d191a..68da4672acf5 100644 --- a/tests/neg-custom-args/captures/delayedRunops.check +++ b/tests/neg-custom-args/captures/delayedRunops.check @@ -1,18 +1,14 @@ --- Error: tests/neg-custom-args/captures/delayedRunops.scala:17:13 ----------------------------------------------------- -17 | runOps(ops1) // error +-- Error: tests/neg-custom-args/captures/delayedRunops.scala:16:13 ----------------------------------------------------- +16 | runOps(ops1) // error | ^^^^ | reference ops* is not included in the allowed capture set {} | of an enclosing function literal with expected type () -> Unit --- Error: tests/neg-custom-args/captures/delayedRunops.scala:23:13 ----------------------------------------------------- -23 | runOps(ops1) // error +-- Error: tests/neg-custom-args/captures/delayedRunops.scala:22:13 ----------------------------------------------------- +22 | runOps(ops1) // error | ^^^^ | Local reach capability ops1* leaks into capture scope of enclosing function --- Error: tests/neg-custom-args/captures/delayedRunops.scala:29:13 ----------------------------------------------------- -29 | runOps(ops1) // error +-- Error: tests/neg-custom-args/captures/delayedRunops.scala:28:13 ----------------------------------------------------- +28 | runOps(ops1) // error | ^^^^ | reference ops* is not included in the allowed capture set {} | of an enclosing function literal with expected type () -> Unit --- Error: tests/neg-custom-args/captures/delayedRunops.scala:22:16 ----------------------------------------------------- -22 | val ops1: List[() => Unit] = ops // error - | ^^^^^^^^^^^^^^^^ - | Separation failure: value ops1's type List[box () => Unit] hides non-local parameter ops diff --git a/tests/neg-custom-args/captures/delayedRunops.scala b/tests/neg-custom-args/captures/delayedRunops.scala index 1108be9c938a..191118fa19c9 100644 --- a/tests/neg-custom-args/captures/delayedRunops.scala +++ b/tests/neg-custom-args/captures/delayedRunops.scala @@ -1,6 +1,5 @@ import language.experimental.captureChecking - -import caps.{use, consume} +import caps.use // ok def runOps(@use ops: List[() => Unit]): Unit = @@ -17,9 +16,9 @@ import caps.{use, consume} runOps(ops1) // error // unsound: impure operation pretended pure - def delayedRunOps2(@consume ops: List[() => Unit]): () ->{} Unit = + def delayedRunOps2(ops: List[() => Unit]): () ->{} Unit = () => - val ops1: List[() => Unit] = ops // error + val ops1: List[() => Unit] = ops runOps(ops1) // error // unsound: impure operation pretended pure diff --git a/tests/neg-custom-args/captures/depfun-reach.check b/tests/neg-custom-args/captures/depfun-reach.check index fc0e6c237647..c1d7d05dc8d6 100644 --- a/tests/neg-custom-args/captures/depfun-reach.check +++ b/tests/neg-custom-args/captures/depfun-reach.check @@ -2,18 +2,13 @@ 13 | op // error | ^^ | Found: (xs: List[(X, box () ->{io} Unit)]) ->{op} List[box () ->{xs*} Unit] - | Required: (xs: List[(X, box () ->{io} Unit)]) ->{fresh} List[() -> Unit] + | Required: (xs: List[(X, box () ->{io} Unit)]) => List[() -> Unit] | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/depfun-reach.scala:20:60 --------------------------------- 20 | val b: (xs: List[() ->{io} Unit]) => List[() ->{} Unit] = a // error | ^ | Found: (xs: List[box () ->{io} Unit]) ->{a} List[box () ->{xs*} Unit] - | Required: (xs: List[box () ->{io} Unit]) ->{fresh} List[() -> Unit] + | Required: (xs: List[box () ->{io} Unit]) => List[() -> Unit] | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/depfun-reach.scala:12:17 ------------------------------------------------------ -12 | : (xs: List[(X, () ->{io} Unit)]) => List[() ->{} Unit] = // error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |Separation failure: method foo's result type (xs: List[(X, box () ->{io} Unit)]) => List[() -> Unit] hides parameter op. - |The parameter needs to be annotated with @consume to allow this. diff --git a/tests/neg-custom-args/captures/depfun-reach.scala b/tests/neg-custom-args/captures/depfun-reach.scala index 4b2d662901b7..94b10f7dbcdb 100644 --- a/tests/neg-custom-args/captures/depfun-reach.scala +++ b/tests/neg-custom-args/captures/depfun-reach.scala @@ -9,7 +9,7 @@ def test(io: Object^, async: Object^) = compose(op) def foo[X](op: (xs: List[(X, () ->{io} Unit)]) => List[() ->{xs*} Unit]) - : (xs: List[(X, () ->{io} Unit)]) => List[() ->{} Unit] = // error + : (xs: List[(X, () ->{io} Unit)]) => List[() ->{} Unit] = op // error def boom(op: List[(() ->{async} Unit, () ->{io} Unit)]): List[() ->{} Unit] = diff --git a/tests/neg-custom-args/captures/effect-swaps-explicit.scala b/tests/neg-custom-args/captures/effect-swaps-explicit.scala index b3756056abbd..e440271ccf88 100644 --- a/tests/neg-custom-args/captures/effect-swaps-explicit.scala +++ b/tests/neg-custom-args/captures/effect-swaps-explicit.scala @@ -14,7 +14,7 @@ end boundary import boundary.{Label, break} -trait Async extends caps.SharedCapability +trait Async extends caps.Capability object Async: def blocking[T](body: Async ?=> T): T = ??? diff --git a/tests/neg-custom-args/captures/effect-swaps.check b/tests/neg-custom-args/captures/effect-swaps.check index 28611959d905..b74c165fd6b6 100644 --- a/tests/neg-custom-args/captures/effect-swaps.check +++ b/tests/neg-custom-args/captures/effect-swaps.check @@ -1,29 +1,29 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:64:8 ---------------------------------- -63 | Result: -64 | Future: // error, type mismatch +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:62:8 ---------------------------------- +61 | Result: +62 | Future: // error, type mismatch | ^ | Found: Result.Ok[box Future[box T^?]^{fr, contextual$1}] | Required: Result[Future[T], Nothing] -65 | fr.await.ok +63 | fr.await.ok |-------------------------------------------------------------------------------------------------------------------- |Inline stack trace |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from effect-swaps.scala:41 -41 | boundary(Ok(body)) + |This location contains code that was inlined from effect-swaps.scala:39 +39 | boundary(Ok(body)) | ^^^^^^^^ -------------------------------------------------------------------------------------------------------------------- | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:74:10 --------------------------------- -74 | Future: fut ?=> // error: type mismatch +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:72:10 --------------------------------- +72 | Future: fut ?=> // error: type mismatch | ^ | Found: Future[box T^?]^{fr, lbl} | Required: Future[box T^?]^? -75 | fr.await.ok +73 | fr.await.ok | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/effect-swaps.scala:68:15 ------------------------------------------------------ -68 | Result.make: // error: local reference leaks +-- Error: tests/neg-custom-args/captures/effect-swaps.scala:66:15 ------------------------------------------------------ +66 | Result.make: // error: local reference leaks | ^^^^^^^^^^^ - |local reference contextual$9 from (using contextual$9: boundary.Label[Result[box Future[box T^?]^{fr, contextual$9}, box E^?]]): + |local reference contextual$9 from (using contextual$9: boundary.Label[Result[box Future[box T^?]^{fr, contextual$9}, box E^?]]^): | box Future[box T^?]^{fr, contextual$9} leaks into outer capture set of type parameter T of method make in object Result diff --git a/tests/neg-custom-args/captures/effect-swaps.scala b/tests/neg-custom-args/captures/effect-swaps.scala index 3f0cc25fbb25..99c781b963c5 100644 --- a/tests/neg-custom-args/captures/effect-swaps.scala +++ b/tests/neg-custom-args/captures/effect-swaps.scala @@ -1,5 +1,3 @@ - - object boundary: final class Label[-T] extends caps.Capability @@ -14,7 +12,7 @@ end boundary import boundary.{Label, break} -trait Async extends caps.SharedCapability +trait Async extends caps.Capability object Async: def blocking[T](body: Async ?=> T): T = ??? diff --git a/tests/neg-custom-args/captures/existential-mapping.check b/tests/neg-custom-args/captures/existential-mapping.check index b52fdb5750ed..30836bc427cf 100644 --- a/tests/neg-custom-args/captures/existential-mapping.check +++ b/tests/neg-custom-args/captures/existential-mapping.check @@ -47,42 +47,42 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:27:25 -------------------------- 27 | val _: (x: C^) => C = y1 // error | ^^ - | Found: (y1 : (x: C^) ->{fresh} (ex$41: caps.Exists) -> C^{ex$41}) - | Required: (x: C^) ->{fresh} C + | Found: (y1 : (x: C^) => (ex$41: caps.Exists) -> C^{ex$41}) + | Required: (x: C^) => C | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:30:20 -------------------------- 30 | val _: C^ => C = y2 // error | ^^ - | Found: (y2 : C^ ->{fresh} (ex$45: caps.Exists) -> C^{ex$45}) - | Required: C^ ->{fresh} C + | Found: (y2 : C^ => (ex$45: caps.Exists) -> C^{ex$45}) + | Required: C^ => C | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:33:30 -------------------------- 33 | val _: A^ => (x: C^) => C = y3 // error | ^^ - | Found: (y3 : A^ ->{fresh} (ex$50: caps.Exists) -> (x: C^) ->{ex$50} (ex$49: caps.Exists) -> C^{ex$49}) - | Required: A^ ->{fresh} (ex$53: caps.Exists) -> (x: C^) ->{ex$53} C + | Found: (y3 : A^ => (ex$50: caps.Exists) -> (x: C^) ->{ex$50} (ex$49: caps.Exists) -> C^{ex$49}) + | Required: A^ => (ex$53: caps.Exists) -> (x: C^) ->{ex$53} C | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:36:25 -------------------------- 36 | val _: A^ => C^ => C = y4 // error | ^^ - | Found: (y4 : A^ ->{fresh} (ex$56: caps.Exists) -> C^ ->{ex$56} (ex$55: caps.Exists) -> C^{ex$55}) - | Required: A^ ->{fresh} (ex$59: caps.Exists) -> C^ ->{ex$59} C + | Found: (y4 : A^ => (ex$56: caps.Exists) -> C^ ->{ex$56} (ex$55: caps.Exists) -> C^{ex$55}) + | Required: A^ => (ex$59: caps.Exists) -> C^ ->{ex$59} C | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:39:30 -------------------------- 39 | val _: A^ => (x: C^) -> C = y5 // error | ^^ - | Found: (y5 : A^ ->{fresh} (x: C^) -> (ex$61: caps.Exists) -> C^{ex$61}) - | Required: A^ ->{fresh} (x: C^) -> C + | Found: (y5 : A^ => (x: C^) -> (ex$61: caps.Exists) -> C^{ex$61}) + | Required: A^ => (x: C^) -> C | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:42:30 -------------------------- 42 | val _: A^ => (x: C^) => C = y6 // error | ^^ - | Found: (y6 : A^ ->{fresh} (ex$70: caps.Exists) -> (x: C^) ->{ex$70} (ex$69: caps.Exists) -> C^{ex$69}) - | Required: A^ ->{fresh} (ex$73: caps.Exists) -> (x: C^) ->{ex$73} C + | Found: (y6 : A^ => (ex$70: caps.Exists) -> (x: C^) ->{ex$70} (ex$69: caps.Exists) -> C^{ex$69}) + | Required: A^ => (ex$73: caps.Exists) -> (x: C^) ->{ex$73} C | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/explain-under-approx.check b/tests/neg-custom-args/captures/explain-under-approx.check new file mode 100644 index 000000000000..c186fc6adb11 --- /dev/null +++ b/tests/neg-custom-args/captures/explain-under-approx.check @@ -0,0 +1,14 @@ +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/explain-under-approx.scala:12:10 ------------------------- +12 | col.add(Future(() => 25)) // error + | ^^^^^^^^^^^^^^^^ + | Found: Future[Int]{val a: (async : Async^)}^{async} + | Required: Future[Int]^{col.futs*} + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/explain-under-approx.scala:15:11 ------------------------- +15 | col1.add(Future(() => 25)) // error + | ^^^^^^^^^^^^^^^^ + | Found: Future[Int]{val a: (async : Async^)}^{async} + | Required: Future[Int]^{col1.futs*} + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/extending-cap-classes.check b/tests/neg-custom-args/captures/extending-cap-classes.check index 4a77a638a4d8..0936f48576e5 100644 --- a/tests/neg-custom-args/captures/extending-cap-classes.check +++ b/tests/neg-custom-args/captures/extending-cap-classes.check @@ -1,21 +1,21 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/extending-cap-classes.scala:7:15 ------------------------- 7 | val x2: C1 = new C2 // error | ^^^^^^ - | Found: C2 + | Found: C2^ | Required: C1 | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/extending-cap-classes.scala:8:15 ------------------------- 8 | val x3: C1 = new C3 // error | ^^^^^^ - | Found: C3 + | Found: C3^ | Required: C1 | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/extending-cap-classes.scala:13:15 ------------------------ 13 | val z2: C1 = y2 // error | ^^ - | Found: (y2 : C2) + | Found: (y2 : C2^) | Required: C1 | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/filevar-expanded.check b/tests/neg-custom-args/captures/filevar-expanded.check deleted file mode 100644 index e1991890f6fa..000000000000 --- a/tests/neg-custom-args/captures/filevar-expanded.check +++ /dev/null @@ -1,19 +0,0 @@ --- Error: tests/neg-custom-args/captures/filevar-expanded.scala:34:19 -------------------------------------------------- -34 | withFile(io3): f => // error: separation failure - | ^ - | Separation failure: argument of type (f: test2.File^{io3}) ->{io3} Unit - | to method withFile: [T](io2: test2.IO^)(op: (f: test2.File^{io2}) => T): T - | corresponds to capture-polymorphic formal parameter op of type (f: test2.File^{io3}) => Unit - | and captures {io3}, but this capability is also passed separately - | in the first argument with type (io3 : test2.IO^). - | - | Capture set of first argument : {io3} - | Hidden set of current argument : {io3} - | Footprint of first argument : {io3} - | Hidden footprint of current argument : {io3} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {io3} -35 | val o = Service(io3) -36 | o.file = f // this is a bit dubious. It's legal since we treat class refinements -37 | // as capture set variables that can be made to include refs coming from outside. -38 | o.log diff --git a/tests/neg-custom-args/captures/filevar.check b/tests/neg-custom-args/captures/filevar.check deleted file mode 100644 index 22efd36053b4..000000000000 --- a/tests/neg-custom-args/captures/filevar.check +++ /dev/null @@ -1,9 +0,0 @@ --- Error: tests/neg-custom-args/captures/filevar.scala:8:6 ------------------------------------------------------------- -8 | var file: File^ = uninitialized // error, was OK under unsealed - | ^ - | Mutable variable file cannot have type File^ since - | that type captures the root capability `cap`. --- Warning: tests/neg-custom-args/captures/filevar.scala:11:55 --------------------------------------------------------- -11 |def withFile[T](op: (l: caps.Capability) ?-> (f: File^{l}) => T): T = - | ^ - | redundant capture: File already accounts for l.type diff --git a/tests/neg-custom-args/captures/i15772.check b/tests/neg-custom-args/captures/i15772.check index f4d5b4d60189..67685d5663b8 100644 --- a/tests/neg-custom-args/captures/i15772.check +++ b/tests/neg-custom-args/captures/i15772.check @@ -1,29 +1,29 @@ --- Error: tests/neg-custom-args/captures/i15772.scala:21:26 ------------------------------------------------------------ -21 | val c : C^{x} = new C(x) // error +-- Error: tests/neg-custom-args/captures/i15772.scala:19:26 ------------------------------------------------------------ +19 | val c : C^{x} = new C(x) // error | ^ | reference (x : C^) is not included in the allowed capture set {} | of an enclosing function literal with expected type () -> Int --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:22:46 --------------------------------------- -22 | val boxed1 : ((C^) => Unit) -> Unit = box1(c) // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:20:46 --------------------------------------- +20 | val boxed1 : ((C^) => Unit) -> Unit = box1(c) // error | ^^^^^^^ | Found: (C{val arg: C^}^{c} => Unit) ->{c} Unit | Required: (C^ => Unit) -> Unit | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/i15772.scala:28:26 ------------------------------------------------------------ -28 | val c : C^{x} = new C(x) // error +-- Error: tests/neg-custom-args/captures/i15772.scala:26:26 ------------------------------------------------------------ +26 | val c : C^{x} = new C(x) // error | ^ | reference (x : C^) is not included in the allowed capture set {} | of an enclosing function literal with expected type () -> Int --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:29:35 --------------------------------------- -29 | val boxed2 : Observe[C^] = box2(c) // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:27:35 --------------------------------------- +27 | val boxed2 : Observe[C^] = box2(c) // error | ^^^^^^^ | Found: (C{val arg: C^}^{c} => Unit) ->{c} Unit | Required: (C^ => Unit) -> Unit | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:35:34 --------------------------------------- -35 | val boxed2 : Observe[C]^ = box2(c) // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:33:34 --------------------------------------- +33 | val boxed2 : Observe[C]^ = box2(c) // error | ^ | Found: box C^ | Required: box C{val arg: C^?}^? @@ -32,14 +32,10 @@ | cannot be included in capture set ? | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:46:2 ---------------------------------------- -46 | x: (() -> Unit) // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:44:2 ---------------------------------------- +44 | x: (() -> Unit) // error | ^ | Found: (x : () ->{filesList, sayHello} Unit) | Required: () -> Unit | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/i15772.scala:34:10 ------------------------------------------------------------ -34 | def c : C^ = new C(x) // error separation - | ^^ - | Separation failure: method c's result type C^ hides non-local parameter x diff --git a/tests/neg-custom-args/captures/i15772.scala b/tests/neg-custom-args/captures/i15772.scala index face1e8a0ff5..a054eac835c1 100644 --- a/tests/neg-custom-args/captures/i15772.scala +++ b/tests/neg-custom-args/captures/i15772.scala @@ -1,5 +1,3 @@ - - type Observe[T] = (T => Unit) -> Unit def unsafe(cap: C^) = cap.bad() @@ -31,7 +29,7 @@ def main2(x: C^) : () -> Int = 0 def main3(x: C^) = - def c : C^ = new C(x) // error separation + def c : C^ = new C(x) val boxed2 : Observe[C]^ = box2(c) // error boxed2((cap: C^) => unsafe(c)) 0 diff --git a/tests/neg-custom-args/captures/i19330-alt2.scala b/tests/neg-custom-args/captures/i19330-alt2.scala index 8c74e05185a2..3e52e3c65634 100644 --- a/tests/neg-custom-args/captures/i19330-alt2.scala +++ b/tests/neg-custom-args/captures/i19330-alt2.scala @@ -10,6 +10,6 @@ trait Foo: def foo: this.T = val leaked = usingLogger[T]: l => // error - val t: () => Logger^ = () => l // error separation + val t: () => Logger^ = () => l t: T leaked diff --git a/tests/neg-custom-args/captures/i19330.check b/tests/neg-custom-args/captures/i19330.check index 894dd53bd303..a8925b117611 100644 --- a/tests/neg-custom-args/captures/i19330.check +++ b/tests/neg-custom-args/captures/i19330.check @@ -3,15 +3,3 @@ | ^^^ | Type variable T of method usingLogger cannot be instantiated to x.T since | the part () => Logger^ of that type captures the root capability `cap`. --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i19330.scala:22:22 --------------------------------------- -22 | val bad: bar.T = foo(bar) // error - | ^^^^^^^^ - | Found: () => Logger^ - | Required: () ->{fresh} (ex$9: caps.Exists) -> Logger^{ex$9} - | - | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/i19330.scala:16:14 ------------------------------------------------------------ -16 | val t: () => Logger^ = () => l // error - | ^^^^^^^^^^^^^ - | Separation failure: value t's type () => (ex$5: caps.Exists) -> Logger^{ex$5} hides parameter l. - | The parameter needs to be annotated with @consume to allow this. diff --git a/tests/neg-custom-args/captures/i19330.scala b/tests/neg-custom-args/captures/i19330.scala index 9c589792a9e3..715b670860cd 100644 --- a/tests/neg-custom-args/captures/i19330.scala +++ b/tests/neg-custom-args/captures/i19330.scala @@ -1,6 +1,6 @@ -import language.experimental.captureChecking +import language.experimental.captureChecking trait Logger def usingLogger[T](op: Logger^ => T): T = ??? @@ -13,11 +13,11 @@ class Bar extends Foo: def foo(x: Foo): x.T = val leaked = usingLogger[x.T]: l => // error - val t: () => Logger^ = () => l // error + val t: () => Logger^ = () => l t: x.T leaked def test(): Unit = val bar = new Bar - val bad: bar.T = foo(bar) // error + val bad: bar.T = foo(bar) val leaked: Logger^ = bad() // leaked scoped capability! diff --git a/tests/neg-custom-args/captures/i21442.check b/tests/neg-custom-args/captures/i21442.check index 1f8fbf4190d9..30becfea0215 100644 --- a/tests/neg-custom-args/captures/i21442.check +++ b/tests/neg-custom-args/captures/i21442.check @@ -1,14 +1,9 @@ --- Error: tests/neg-custom-args/captures/i21442.scala:10:13 ------------------------------------------------------------ -10 | val io = x.unbox // error: local reach capability {x*} leaks - | ^^^^^^^ - | Local reach capability x* leaks into capture scope of method foo. - | To allow this, the parameter x should be declared with a @use annotation --- Error: tests/neg-custom-args/captures/i21442.scala:18:14 ------------------------------------------------------------ -18 | val io = x1.unbox // error +-- Error: tests/neg-custom-args/captures/i21442.scala:9:13 ------------------------------------------------------------- +9 | val io = x.unbox // error: local reach capability {x*} leaks + | ^^^^^^^ + | Local reach capability x* leaks into capture scope of method foo. + | To allow this, the parameter x should be declared with a @use annotation +-- Error: tests/neg-custom-args/captures/i21442.scala:17:14 ------------------------------------------------------------ +17 | val io = x1.unbox // error | ^^^^^^^^ | Local reach capability x1* leaks into capture scope of method bar --- Error: tests/neg-custom-args/captures/i21442.scala:17:10 ------------------------------------------------------------ -17 | val x1: Boxed[IO^] = x // error - | ^^^^^^^^^^ - | Separation failure: value x1's type Boxed[box IO^] hides parameter x. - | The parameter needs to be annotated with @consume to allow this. diff --git a/tests/neg-custom-args/captures/i21442.scala b/tests/neg-custom-args/captures/i21442.scala index 3541bd89789a..c9fa7d152fae 100644 --- a/tests/neg-custom-args/captures/i21442.scala +++ b/tests/neg-custom-args/captures/i21442.scala @@ -1,5 +1,4 @@ import language.experimental.captureChecking - trait IO: def use(): Unit case class Boxed[+T](unbox: T) @@ -14,6 +13,6 @@ def foo(x: Boxed[IO^]): Unit = // slightly different way. // But, no type error reported. def bar(x: Boxed[IO^]): Unit = - val x1: Boxed[IO^] = x // error + val x1: Boxed[IO^] = x val io = x1.unbox // error io.use() diff --git a/tests/neg-custom-args/captures/i21614.check b/tests/neg-custom-args/captures/i21614.check index aa60c2eaa366..f4967253455f 100644 --- a/tests/neg-custom-args/captures/i21614.check +++ b/tests/neg-custom-args/captures/i21614.check @@ -1,17 +1,17 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21614.scala:12:33 --------------------------------------- 12 | files.map((f: F) => new Logger(f)) // error, Q: can we make this pass (see #19076)? | ^ - | Found: (f : F) + | Found: (f : F^) | Required: File^ | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21614.scala:15:12 --------------------------------------- 15 | files.map(new Logger(_)) // error, Q: can we improve the error message? | ^^^^^^^^^^^^^ - |Found: (_$1: box File^{files*}) ->{files*} (ex$16: caps.Exists) -> box Logger{val f: File^{_$1}}^{ex$16.rd, _$1} - |Required: (_$1: box File^{files*}) => box Logger{val f: File^?}^? + | Found: (_$1: box File^{files*}) ->{files*} (ex$16: caps.Exists) -> box Logger{val f: File^{_$1}}^{ex$16} + | Required: (_$1: box File^{files*}) => box Logger{val f: File^?}^? | - |Note that the universal capability `cap.rd` - |cannot be included in capture set ? + | Note that the universal capability `cap` + | cannot be included in capture set ? | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i22005.scala b/tests/neg-custom-args/captures/i22005.scala index 689246d6f835..a9dca999e42b 100644 --- a/tests/neg-custom-args/captures/i22005.scala +++ b/tests/neg-custom-args/captures/i22005.scala @@ -1,4 +1,3 @@ - import caps.* class IO @@ -6,4 +5,4 @@ class File(io: IO^) class Handler[C^]: def f(file: File^): File^{C^} = file // error - def g(@consume file: File^{C^}): File^ = file // ok + def g(file: File^{C^}): File^ = file // ok diff --git a/tests/neg-custom-args/captures/lazylists-exceptions.check b/tests/neg-custom-args/captures/lazylists-exceptions.check index bdd053910ac8..111719a81f07 100644 --- a/tests/neg-custom-args/captures/lazylists-exceptions.check +++ b/tests/neg-custom-args/captures/lazylists-exceptions.check @@ -1,7 +1,7 @@ -- Error: tests/neg-custom-args/captures/lazylists-exceptions.scala:36:2 ----------------------------------------------- 36 | try // error | ^ - | The result of `try` cannot have type LazyList[Int]^{cap.rd} since + | The result of `try` cannot have type LazyList[Int]^ since | that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. 37 | tabulate(10) { i => diff --git a/tests/neg-custom-args/captures/lazyref.check b/tests/neg-custom-args/captures/lazyref.check index be8f5e56fb87..8683615c07d8 100644 --- a/tests/neg-custom-args/captures/lazyref.check +++ b/tests/neg-custom-args/captures/lazyref.check @@ -1,43 +1,28 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:20:28 -------------------------------------- -20 | val ref1c: LazyRef[Int] = ref1 // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:19:28 -------------------------------------- +19 | val ref1c: LazyRef[Int] = ref1 // error | ^^^^ | Found: (ref1 : LazyRef[Int]{val elem: () ->{cap1} Int}^{cap1}) | Required: LazyRef[Int] | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:22:35 -------------------------------------- -22 | val ref2c: LazyRef[Int]^{cap2} = ref2 // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:21:35 -------------------------------------- +21 | val ref2c: LazyRef[Int]^{cap2} = ref2 // error | ^^^^ | Found: LazyRef[Int]{val elem: () ->{ref2*} Int}^{ref2} | Required: LazyRef[Int]^{cap2} | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:24:35 -------------------------------------- -24 | val ref3c: LazyRef[Int]^{ref1} = ref3 // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:23:35 -------------------------------------- +23 | val ref3c: LazyRef[Int]^{ref1} = ref3 // error | ^^^^ | Found: LazyRef[Int]{val elem: () ->{ref3*} Int}^{ref3} | Required: LazyRef[Int]^{ref1} | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:26:35 -------------------------------------- -26 | val ref4c: LazyRef[Int]^{cap1} = ref4 // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:25:35 -------------------------------------- +25 | val ref4c: LazyRef[Int]^{cap1} = ref4 // error | ^^^^ | Found: LazyRef[Int]{val elem: () ->{ref4*} Int}^{ref4} | Required: LazyRef[Int]^{cap1} | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/lazyref.scala:25:55 ----------------------------------------------------------- -25 | val ref4 = (if cap1 == cap2 then ref1 else ref2).map(g) // error: separation failure - | ^ - |Separation failure: argument of type (x: Int) ->{cap2} Int - |to method map: [U](f: T => U): LazyRef[U]^{f, LazyRef.this} - |corresponds to capture-polymorphic formal parameter f of type Int => Int - |and captures {cap2}, but this capability is also passed separately - |in the function prefix with type (LazyRef[Int]{val elem: () ->{ref2*} Int} | (ref1 : LazyRef[Int]{val elem: () ->{cap1} Int}^{cap1}))^{ref2}. - | - | Capture set of function prefix : {ref1, ref2, ref2*} - | Hidden set of current argument : {cap2} - | Footprint of function prefix : {ref1, ref2, ref2*, cap1, cap2} - | Hidden footprint of current argument : {cap2} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {cap2} diff --git a/tests/neg-custom-args/captures/lazyref.scala b/tests/neg-custom-args/captures/lazyref.scala index 549bcc2257a5..99aa10d5d2b2 100644 --- a/tests/neg-custom-args/captures/lazyref.scala +++ b/tests/neg-custom-args/captures/lazyref.scala @@ -1,4 +1,3 @@ - class CC type Cap = CC^ @@ -22,5 +21,5 @@ def test(cap1: Cap, cap2: Cap) = val ref2c: LazyRef[Int]^{cap2} = ref2 // error val ref3 = ref1.map(g) val ref3c: LazyRef[Int]^{ref1} = ref3 // error - val ref4 = (if cap1 == cap2 then ref1 else ref2).map(g) // error: separation failure + val ref4 = (if cap1 == cap2 then ref1 else ref2).map(g) val ref4c: LazyRef[Int]^{cap1} = ref4 // error diff --git a/tests/neg-custom-args/captures/linear-buffer-2.check b/tests/neg-custom-args/captures/linear-buffer-2.check deleted file mode 100644 index 3d64c432d116..000000000000 --- a/tests/neg-custom-args/captures/linear-buffer-2.check +++ /dev/null @@ -1,29 +0,0 @@ --- Error: tests/neg-custom-args/captures/linear-buffer-2.scala:13:13 --------------------------------------------------- -13 | val buf3 = buf.append(3) // error - | ^^^ - | Separation failure: Illegal access to {buf} which is hidden by the previous definition - | of value buf1 with type Buffer[Int]^. - | This type hides capabilities {buf} --- Error: tests/neg-custom-args/captures/linear-buffer-2.scala:20:13 --------------------------------------------------- -20 | val buf3 = buf1.append(4) // error - | ^^^^ - | Separation failure: Illegal access to (buf1 : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 18 - | and therefore is no longer available. --- Error: tests/neg-custom-args/captures/linear-buffer-2.scala:28:13 --------------------------------------------------- -28 | val buf3 = buf1.append(4) // error - | ^^^^ - | Separation failure: Illegal access to (buf1 : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 25 - | and therefore is no longer available. --- Error: tests/neg-custom-args/captures/linear-buffer-2.scala:38:13 --------------------------------------------------- -38 | val buf3 = buf1.append(4) // error - | ^^^^ - | Separation failure: Illegal access to (buf1 : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 33 - | and therefore is no longer available. --- Error: tests/neg-custom-args/captures/linear-buffer-2.scala:42:4 ---------------------------------------------------- -42 | buf.append(1) // error - | ^^^ - | Separation failure: (buf : Buffer[Int]^) appears in a loop, therefore it cannot - | be passed to a @consume parameter or be used as a prefix of a @consume method call. diff --git a/tests/neg-custom-args/captures/linear-buffer-2.scala b/tests/neg-custom-args/captures/linear-buffer-2.scala deleted file mode 100644 index 428171c3fab8..000000000000 --- a/tests/neg-custom-args/captures/linear-buffer-2.scala +++ /dev/null @@ -1,42 +0,0 @@ -import caps.{cap, consume, Mutable} -import language.experimental.captureChecking - -class Buffer[T] extends Mutable: - @consume mut def append(x: T): Buffer[T]^ = this // ok - -def app[T](@consume buf: Buffer[T]^, elem: T): Buffer[T]^ = - buf.append(elem) - -def Test(@consume buf: Buffer[Int]^) = - val buf1: Buffer[Int]^ = buf.append(1) - val buf2 = buf1.append(2) // OK - val buf3 = buf.append(3) // error - -def Test2(@consume buf: Buffer[Int]^) = - val buf1: Buffer[Int]^ = buf.append(1) - val buf2 = - if ??? then buf1.append(2) // OK - else buf1.append(3) // OK - val buf3 = buf1.append(4) // error - -def Test3(@consume buf: Buffer[Int]^) = - val buf1: Buffer[Int]^ = buf.append(1) - val buf2 = (??? : Int) match - case 1 => buf1.append(2) // OK - case 2 => buf1.append(2) - case _ => buf1.append(3) - val buf3 = buf1.append(4) // error - -def Test4(@consume buf: Buffer[Int]^) = - val buf1: Buffer[Int]^ = buf.append(1) - val buf2 = (??? : Int) match - case 1 => buf1.append(2) // OK - case 2 => buf1.append(2) - case 3 => buf1.append(3) - case 4 => buf1.append(4) - case 5 => buf1.append(5) - val buf3 = buf1.append(4) // error - -def Test5(@consume buf: Buffer[Int]^) = - while true do - buf.append(1) // error diff --git a/tests/neg-custom-args/captures/linear-buffer.check b/tests/neg-custom-args/captures/linear-buffer.check deleted file mode 100644 index 16ba3bd096a2..000000000000 --- a/tests/neg-custom-args/captures/linear-buffer.check +++ /dev/null @@ -1,44 +0,0 @@ --- Error: tests/neg-custom-args/captures/linear-buffer.scala:5:24 ------------------------------------------------------ -5 | mut def append(x: T): BadBuffer[T]^ = this // error - | ^^^^^^^^^^^^^ - | Separation failure: method append's result type BadBuffer[T]^ hides non-local this of class class BadBuffer. - | The access must be in a @consume method to allow this. --- Error: tests/neg-custom-args/captures/linear-buffer.scala:7:13 ------------------------------------------------------ -7 | def bar: BadBuffer[T]^ = this // error - | ^^^^^^^^^^^^^ - | Separation failure: method bar's result type BadBuffer[T]^ hides non-local this of class class BadBuffer. - | The access must be in a @consume method to allow this. --- Error: tests/neg-custom-args/captures/linear-buffer.scala:6:9 ------------------------------------------------------- -6 | def foo = // error - | ^ - |Separation failure: method foo's inferred result type BadBuffer[box T^?]^ hides non-local this of class class BadBuffer. - |The access must be in a @consume method to allow this. --- Error: tests/neg-custom-args/captures/linear-buffer.scala:19:17 ----------------------------------------------------- -19 | val buf3 = app(buf, 3) // error - | ^^^ - | Separation failure: Illegal access to (buf : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 17 - | and therefore is no longer available. --- Error: tests/neg-custom-args/captures/linear-buffer.scala:26:17 ----------------------------------------------------- -26 | val buf3 = app(buf1, 4) // error - | ^^^^ - | Separation failure: Illegal access to (buf1 : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 24 - | and therefore is no longer available. --- Error: tests/neg-custom-args/captures/linear-buffer.scala:34:17 ----------------------------------------------------- -34 | val buf3 = app(buf1, 4) // error - | ^^^^ - | Separation failure: Illegal access to (buf1 : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 31 - | and therefore is no longer available. --- Error: tests/neg-custom-args/captures/linear-buffer.scala:44:17 ----------------------------------------------------- -44 | val buf3 = app(buf1, 4) // error - | ^^^^ - | Separation failure: Illegal access to (buf1 : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 39 - | and therefore is no longer available. --- Error: tests/neg-custom-args/captures/linear-buffer.scala:48:8 ------------------------------------------------------ -48 | app(buf, 1) // error - | ^^^ - | Separation failure: (buf : Buffer[Int]^) appears in a loop, therefore it cannot - | be passed to a @consume parameter or be used as a prefix of a @consume method call. diff --git a/tests/neg-custom-args/captures/linear-buffer.scala b/tests/neg-custom-args/captures/linear-buffer.scala deleted file mode 100644 index 97315c1aa0fb..000000000000 --- a/tests/neg-custom-args/captures/linear-buffer.scala +++ /dev/null @@ -1,48 +0,0 @@ -import caps.{cap, consume, Mutable} -import language.experimental.captureChecking - -class BadBuffer[T] extends Mutable: - mut def append(x: T): BadBuffer[T]^ = this // error - def foo = // error - def bar: BadBuffer[T]^ = this // error - bar - -class Buffer[T] extends Mutable: - @consume mut def append(x: T): Buffer[T]^ = this // ok - -def app[T](@consume buf: Buffer[T]^, elem: T): Buffer[T]^ = - buf.append(elem) - -def Test(@consume buf: Buffer[Int]^) = - val buf1: Buffer[Int]^ = app(buf, 1) - val buf2 = app(buf1, 2) // OK - val buf3 = app(buf, 3) // error - -def Test2(@consume buf: Buffer[Int]^) = - val buf1: Buffer[Int]^ = app(buf, 1) - val buf2 = - if ??? then app(buf1, 2) // OK - else app(buf1, 3) // OK - val buf3 = app(buf1, 4) // error - -def Test3(@consume buf: Buffer[Int]^) = - val buf1: Buffer[Int]^ = app(buf, 1) - val buf2 = (??? : Int) match - case 1 => app(buf1, 2) // OK - case 2 => app(buf1, 2) - case _ => app(buf1, 3) - val buf3 = app(buf1, 4) // error - -def Test4(@consume buf: Buffer[Int]^) = - val buf1: Buffer[Int]^ = app(buf, 1) - val buf2 = (??? : Int) match - case 1 => app(buf1, 2) // OK - case 2 => app(buf1, 2) - case 3 => app(buf1, 3) - case 4 => app(buf1, 4) - case 5 => app(buf1, 5) - val buf3 = app(buf1, 4) // error - -def Test5(@consume buf: Buffer[Int]^) = - while true do - app(buf, 1) // error diff --git a/tests/neg-custom-args/captures/mut-outside-mutable.check b/tests/neg-custom-args/captures/mut-outside-mutable.check deleted file mode 100644 index bfc1b5161f0a..000000000000 --- a/tests/neg-custom-args/captures/mut-outside-mutable.check +++ /dev/null @@ -1,8 +0,0 @@ --- Error: tests/neg-custom-args/captures/mut-outside-mutable.scala:5:10 ------------------------------------------------ -5 | mut def foreach(op: T => Unit): Unit // error - | ^ - | Update methods can only be used as members of classes extending the `Mutable` trait --- Error: tests/neg-custom-args/captures/mut-outside-mutable.scala:9:12 ------------------------------------------------ -9 | mut def baz() = 1 // error - | ^ - | Update methods can only be used as members of classes extending the `Mutable` trait diff --git a/tests/neg-custom-args/captures/mut-outside-mutable.scala b/tests/neg-custom-args/captures/mut-outside-mutable.scala deleted file mode 100644 index 18c0e59c5bd8..000000000000 --- a/tests/neg-custom-args/captures/mut-outside-mutable.scala +++ /dev/null @@ -1,10 +0,0 @@ -import caps.Mutable - -trait IterableOnce[T]: - def iterator: Iterator[T]^{this} - mut def foreach(op: T => Unit): Unit // error - -trait Foo extends Mutable: - def bar = - mut def baz() = 1 // error - baz() diff --git a/tests/neg-custom-args/captures/mut-override.scala b/tests/neg-custom-args/captures/mut-override.scala deleted file mode 100644 index 848e4d880223..000000000000 --- a/tests/neg-custom-args/captures/mut-override.scala +++ /dev/null @@ -1,19 +0,0 @@ -import caps.Mutable - -trait IterableOnce[T] extends Mutable: - def iterator: Iterator[T]^{this} - mut def foreach(op: T => Unit): Unit - -trait Iterator[T] extends IterableOnce[T]: - def iterator = this - def hasNext: Boolean - mut def next(): T - mut def foreach(op: T => Unit): Unit = ??? - override mut def toString = ??? // error - -trait Iterable[T] extends IterableOnce[T]: - def iterator: Iterator[T] = ??? - def foreach(op: T => Unit) = iterator.foreach(op) - -trait BadIterator[T] extends Iterator[T]: - override mut def hasNext: Boolean // error diff --git a/tests/neg-custom-args/captures/non-local-consume.scala b/tests/neg-custom-args/captures/non-local-consume.scala deleted file mode 100644 index 0e46146eb7e8..000000000000 --- a/tests/neg-custom-args/captures/non-local-consume.scala +++ /dev/null @@ -1,29 +0,0 @@ -import caps.{cap, consume, Mutable} -import language.experimental.captureChecking - -class Buffer extends Mutable - -def f1(@consume buf: Buffer^): Buffer^ = - val buf1: Buffer^ = buf // OK - buf1 - -def f2(@consume buf: Buffer^): Buffer^ = - def g(): Buffer^ = buf // error - g() - -def f3(@consume buf: Buffer^): Buffer^ = - val buf1 = buf - def g(): Buffer^ = buf1 // error - g() - -def f4(@consume buf: Buffer^): Buffer^ = - val buf1: Buffer^ = buf - def g(): Buffer^ = buf1 // error - g() - -def f5(@consume buf: Buffer^): Unit = - val buf1: Buffer^ = buf - def g(): Unit = cc(buf1) // error - g() - -def cc(@consume buf: Buffer^): Unit = () diff --git a/tests/neg-custom-args/captures/outer-var.check b/tests/neg-custom-args/captures/outer-var.check index 0c86213ff118..b24579b7a69f 100644 --- a/tests/neg-custom-args/captures/outer-var.check +++ b/tests/neg-custom-args/captures/outer-var.check @@ -1,5 +1,5 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/outer-var.scala:12:8 ------------------------------------- -12 | x = q // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/outer-var.scala:11:8 ------------------------------------- +11 | x = q // error | ^ | Found: (q : () => Unit) | Required: () ->{p, q²} Unit @@ -8,15 +8,15 @@ | q² is a parameter in method test | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/outer-var.scala:13:9 ------------------------------------- -13 | x = (q: Proc) // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/outer-var.scala:12:9 ------------------------------------- +12 | x = (q: Proc) // error | ^^^^^^^ | Found: () => Unit | Required: () ->{p, q} Unit | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/outer-var.scala:14:9 ------------------------------------- -14 | y = (q: Proc) // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/outer-var.scala:13:9 ------------------------------------- +13 | y = (q: Proc) // error | ^^^^^^^ | Found: () => Unit | Required: () ->{p} Unit @@ -25,18 +25,18 @@ | cannot be included in capture set {p} of variable y | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/outer-var.scala:15:8 ------------------------------------- -15 | y = q // error, was OK under unsealed +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/outer-var.scala:14:8 ------------------------------------- +14 | y = q // error, was OK under unsealed | ^ | Found: (q : () => Unit) | Required: () ->{p} Unit | | Note that reference (q : () => Unit), defined in method inner - | cannot be included in outer capture set {p} + | cannot be included in outer capture set {p} of variable y | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/outer-var.scala:17:57 --------------------------------------------------------- -17 | var finalizeActions = collection.mutable.ListBuffer[() => Unit]() // error, was OK under unsealed +-- Error: tests/neg-custom-args/captures/outer-var.scala:16:57 --------------------------------------------------------- +16 | var finalizeActions = collection.mutable.ListBuffer[() => Unit]() // error, was OK under unsealed | ^^^^^^^^^^ | Type variable A of object ListBuffer cannot be instantiated to box () => Unit since | that type captures the root capability `cap`. diff --git a/tests/neg-custom-args/captures/outer-var.scala b/tests/neg-custom-args/captures/outer-var.scala index eb82312d4b37..f869bfbfc387 100644 --- a/tests/neg-custom-args/captures/outer-var.scala +++ b/tests/neg-custom-args/captures/outer-var.scala @@ -1,4 +1,3 @@ - class CC type Cap = CC^ diff --git a/tests/neg-custom-args/captures/path-patmat-should-be-pos.scala b/tests/neg-custom-args/captures/path-patmat-should-be-pos.scala index 5f434a21cc8b..aca6102204a3 100644 --- a/tests/neg-custom-args/captures/path-patmat-should-be-pos.scala +++ b/tests/neg-custom-args/captures/path-patmat-should-be-pos.scala @@ -1,8 +1,6 @@ -import caps.cap - class It[A] -class Filter[A](val underlying: It[A]^, val p: A ->{cap, underlying} Boolean) extends It[A] +class Filter[A](val underlying: It[A]^, val p: A => Boolean) extends It[A] object Filter: def apply[A](underlying: It[A]^, p: A => Boolean): Filter[A]^{underlying, p} = underlying match diff --git a/tests/neg-custom-args/captures/reaches.check b/tests/neg-custom-args/captures/reaches.check index 008b22d9cd8e..7c00fa7299fe 100644 --- a/tests/neg-custom-args/captures/reaches.check +++ b/tests/neg-custom-args/captures/reaches.check @@ -1,12 +1,12 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:23:11 -------------------------------------- -23 | cur = (() => f.write()) :: Nil // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:22:11 -------------------------------------- +22 | cur = (() => f.write()) :: Nil // error | ^^^^^^^^^^^^^^^^^^^^^^^ | Found: List[box () ->{f} Unit] | Required: List[box () ->{xs*} Unit] | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:34:7 --------------------------------------- -34 | (() => f.write()) :: Nil // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:33:7 --------------------------------------- +33 | (() => f.write()) :: Nil // error | ^^^^^^^^^^^^^^^^^^^^^^^ | Found: List[box () ->{f} Unit] | Required: box List[box () ->{xs*} Unit]^? @@ -15,91 +15,47 @@ | cannot be included in outer capture set {xs*} of value cur | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/reaches.scala:37:6 ------------------------------------------------------------ -37 | var cur: List[Proc] = xs // error +-- Error: tests/neg-custom-args/captures/reaches.scala:36:6 ------------------------------------------------------------ +36 | var cur: List[Proc] = xs // error | ^ | Mutable variable cur cannot have type List[box () => Unit] since | the part box () => Unit of that type captures the root capability `cap`. --- Error: tests/neg-custom-args/captures/reaches.scala:44:16 ----------------------------------------------------------- -44 | val cur = Ref[List[Proc]](xs) // error +-- Error: tests/neg-custom-args/captures/reaches.scala:43:16 ----------------------------------------------------------- +43 | val cur = Ref[List[Proc]](xs) // error | ^^^^^^^^^^ | Type variable T of constructor Ref cannot be instantiated to List[box () => Unit] since | the part box () => Unit of that type captures the root capability `cap`. --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:46:35 -------------------------------------- -46 | val next: () => Unit = cur.get.head // error - | ^^^^^^^^^^^^ - | Found: () => Unit - | Required: () ->{fresh} Unit - | - | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:48:20 -------------------------------------- -48 | cur.set(cur.get.tail: List[Proc]) // error - | ^^^^^^^^^^^^ - | Found: List[box () => Unit] - | Required: List[box () ->{fresh} Unit] - | - | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/reaches.scala:54:51 ----------------------------------------------------------- -54 | val id: Id[Proc, Proc] = new Id[Proc, () -> Unit] // error +-- Error: tests/neg-custom-args/captures/reaches.scala:53:51 ----------------------------------------------------------- +53 | val id: Id[Proc, Proc] = new Id[Proc, () -> Unit] // error | ^ | Type variable A of constructor Id cannot be instantiated to box () => Unit since | that type captures the root capability `cap`. --- Error: tests/neg-custom-args/captures/reaches.scala:56:6 ------------------------------------------------------------ -56 | id(() => f.write()) // error +-- Error: tests/neg-custom-args/captures/reaches.scala:55:6 ------------------------------------------------------------ +55 | id(() => f.write()) // error | ^^^^^^^^^^^^^^^^^^^ | Local reach capability id* leaks into capture scope of method test --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:63:27 -------------------------------------- -63 | val f1: File^{id*} = id(f) // error, since now id(f): File^ // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:62:27 -------------------------------------- +62 | val f1: File^{id*} = id(f) // error, since now id(f): File^ // error | ^^^^^ | Found: File^{f} | Required: File^{id*} | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/reaches.scala:80:10 ----------------------------------------------------------- -80 | ps.map((x, y) => compose1(x, y)) // error // error // error sepcheck +-- Error: tests/neg-custom-args/captures/reaches.scala:79:10 ----------------------------------------------------------- +79 | ps.map((x, y) => compose1(x, y)) // error // error | ^ | Local reach capability ps* leaks into capture scope of method mapCompose. | To allow this, the parameter ps should be declared with a @use annotation --- Error: tests/neg-custom-args/captures/reaches.scala:80:13 ----------------------------------------------------------- -80 | ps.map((x, y) => compose1(x, y)) // error // error // error sepcheck +-- Error: tests/neg-custom-args/captures/reaches.scala:79:13 ----------------------------------------------------------- +79 | ps.map((x, y) => compose1(x, y)) // error // error | ^ | Local reach capability ps* leaks into capture scope of method mapCompose. | To allow this, the parameter ps should be declared with a @use annotation --- Error: tests/neg-custom-args/captures/reaches.scala:80:31 ----------------------------------------------------------- -80 | ps.map((x, y) => compose1(x, y)) // error // error // error sepcheck - | ^ - | Separation failure: argument of type (x$0: A) ->{y} box A^? - | to method compose1: [A, B, C](f: A => B, g: B => C): A ->{f, g} C - | corresponds to capture-polymorphic formal parameter g of type box A^? => box A^? - | and captures {ps*}, but this capability is also passed separately - | in the first argument with type (x$0: A) ->{x} box A^?. - | - | Capture set of first argument : {x} - | Hidden set of current argument : {y} - | Footprint of first argument : {x, ps*} - | Hidden footprint of current argument : {y, ps*} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {ps*} --- Error: tests/neg-custom-args/captures/reaches.scala:83:31 ----------------------------------------------------------- -83 | ps.map((x, y) => compose1(x, y)) // error sepcheck - | ^ - | Separation failure: argument of type (x$0: A) ->{y} box A^? - | to method compose1: [A, B, C](f: A => B, g: B => C): A ->{f, g} C - | corresponds to capture-polymorphic formal parameter g of type box A^? => box A^? - | and captures {ps*}, but this capability is also passed separately - | in the first argument with type (x$0: A) ->{x} box A^?. - | - | Capture set of first argument : {x} - | Hidden set of current argument : {y} - | Footprint of first argument : {x, ps*} - | Hidden footprint of current argument : {y, ps*} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {ps*} --- Error: tests/neg-custom-args/captures/reaches.scala:62:31 ----------------------------------------------------------- -62 | val leaked = usingFile[File^{id*}]: f => // error +-- Error: tests/neg-custom-args/captures/reaches.scala:61:31 ----------------------------------------------------------- +61 | val leaked = usingFile[File^{id*}]: f => // error | ^^^ | id* cannot be tracked since its deep capture set is empty --- Error: tests/neg-custom-args/captures/reaches.scala:63:18 ----------------------------------------------------------- -63 | val f1: File^{id*} = id(f) // error, since now id(f): File^ // error +-- Error: tests/neg-custom-args/captures/reaches.scala:62:18 ----------------------------------------------------------- +62 | val f1: File^{id*} = id(f) // error, since now id(f): File^ // error | ^^^ | id* cannot be tracked since its deep capture set is empty diff --git a/tests/neg-custom-args/captures/reaches.scala b/tests/neg-custom-args/captures/reaches.scala index d4f9ceee3de2..a9773b76f445 100644 --- a/tests/neg-custom-args/captures/reaches.scala +++ b/tests/neg-custom-args/captures/reaches.scala @@ -1,5 +1,4 @@ -import caps.use; -import caps.consume +import caps.use class File: def write(): Unit = ??? @@ -33,7 +32,7 @@ def runAll1(@use xs: List[Proc]): Unit = cur.set: (() => f.write()) :: Nil // error -def runAll2(@consume xs: List[Proc]): Unit = +def runAll2(xs: List[Proc]): Unit = var cur: List[Proc] = xs // error while cur.nonEmpty do val next: () => Unit = cur.head @@ -43,9 +42,9 @@ def runAll2(@consume xs: List[Proc]): Unit = def runAll3(xs: List[Proc]): Unit = val cur = Ref[List[Proc]](xs) // error while cur.get.nonEmpty do - val next: () => Unit = cur.get.head // error + val next: () => Unit = cur.get.head next() - cur.set(cur.get.tail: List[Proc]) // error + cur.set(cur.get.tail: List[Proc]) class Id[-A, +B >: A](): def apply(a: A): B = a @@ -77,7 +76,7 @@ def compose1[A, B, C](f: A => B, g: B => C): A ->{f, g} C = z => g(f(z)) def mapCompose[A](ps: List[(A => A, A => A)]): List[A ->{ps*} A] = - ps.map((x, y) => compose1(x, y)) // error // error // error sepcheck + ps.map((x, y) => compose1(x, y)) // error // error def mapCompose2[A](@use ps: List[(A => A, A => A)]): List[A ->{ps*} A] = - ps.map((x, y) => compose1(x, y)) // error sepcheck + ps.map((x, y) => compose1(x, y)) diff --git a/tests/neg-custom-args/captures/reaches2.check b/tests/neg-custom-args/captures/reaches2.check index 74223963dde9..1e921ee92072 100644 --- a/tests/neg-custom-args/captures/reaches2.check +++ b/tests/neg-custom-args/captures/reaches2.check @@ -1,25 +1,10 @@ --- Error: tests/neg-custom-args/captures/reaches2.scala:10:10 ---------------------------------------------------------- -10 | ps.map((x, y) => compose1(x, y)) // error // error // error - | ^ - |reference ps* is not included in the allowed capture set {} - |of an enclosing function literal with expected type ((box A ->{ps*} A, box A ->{ps*} A)) -> box (x$0: A^?) ->? A^? --- Error: tests/neg-custom-args/captures/reaches2.scala:10:13 ---------------------------------------------------------- -10 | ps.map((x, y) => compose1(x, y)) // error // error // error - | ^ - |reference ps* is not included in the allowed capture set {} - |of an enclosing function literal with expected type ((box A ->{ps*} A, box A ->{ps*} A)) -> box (x$0: A^?) ->? A^? --- Error: tests/neg-custom-args/captures/reaches2.scala:10:31 ---------------------------------------------------------- -10 | ps.map((x, y) => compose1(x, y)) // error // error // error - | ^ - | Separation failure: argument of type (x$0: A) ->{y} box A^? - | to method compose1: [A, B, C](f: A => B, g: B => C): A ->{f, g} C - | corresponds to capture-polymorphic formal parameter g of type box A^? => box A^? - | and captures {ps*}, but this capability is also passed separately - | in the first argument with type (x$0: A) ->{x} box A^?. - | - | Capture set of first argument : {x} - | Hidden set of current argument : {y} - | Footprint of first argument : {x, ps*} - | Hidden footprint of current argument : {y, ps*} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {ps*} +-- Error: tests/neg-custom-args/captures/reaches2.scala:8:10 ----------------------------------------------------------- +8 | ps.map((x, y) => compose1(x, y)) // error // error + | ^ + |reference ps* is not included in the allowed capture set {} + |of an enclosing function literal with expected type ((box A ->{ps*} A, box A ->{ps*} A)) -> box (x$0: A^?) ->? A^? +-- Error: tests/neg-custom-args/captures/reaches2.scala:8:13 ----------------------------------------------------------- +8 | ps.map((x, y) => compose1(x, y)) // error // error + | ^ + |reference ps* is not included in the allowed capture set {} + |of an enclosing function literal with expected type ((box A ->{ps*} A, box A ->{ps*} A)) -> box (x$0: A^?) ->? A^? diff --git a/tests/neg-custom-args/captures/reaches2.scala b/tests/neg-custom-args/captures/reaches2.scala index 69ee3472cf86..f2447b8c8795 100644 --- a/tests/neg-custom-args/captures/reaches2.scala +++ b/tests/neg-custom-args/captures/reaches2.scala @@ -1,5 +1,3 @@ - - class List[+A]: def map[B](f: A -> B): List[B] = ??? @@ -7,5 +5,5 @@ def compose1[A, B, C](f: A => B, g: B => C): A ->{f, g} C = z => g(f(z)) def mapCompose[A](ps: List[(A => A, A => A)]): List[A ->{ps*} A] = - ps.map((x, y) => compose1(x, y)) // error // error // error + ps.map((x, y) => compose1(x, y)) // error // error diff --git a/tests/neg-custom-args/captures/readOnly.check b/tests/neg-custom-args/captures/readOnly.check deleted file mode 100644 index e1aed07657e5..000000000000 --- a/tests/neg-custom-args/captures/readOnly.check +++ /dev/null @@ -1,19 +0,0 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/readOnly.scala:14:21 ------------------------------------- -14 | val _: () -> Int = getA // error - | ^^^^ - | Found: (getA : () ->{a.rd} Int) - | Required: () -> Int - | - | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/readOnly.scala:17:23 ------------------------------------- -17 | val _: Int -> Unit = putA // error - | ^^^^ - | Found: (putA : (x$0: Int) ->{a} Unit) - | Required: Int -> Unit - | - | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/readOnly.scala:20:23 ---------------------------------------------------------- -20 | val doit = () => z.put(x.get max y.get) // error - | ^^^^^ - | cannot call update method put from (z : Ref), - | since its capture set {z} is read-only diff --git a/tests/neg-custom-args/captures/readOnly.scala b/tests/neg-custom-args/captures/readOnly.scala deleted file mode 100644 index 4edea6638980..000000000000 --- a/tests/neg-custom-args/captures/readOnly.scala +++ /dev/null @@ -1,22 +0,0 @@ -import caps.Mutable -import caps.cap - -class Ref(init: Int) extends Mutable: - private var current = init - def get: Int = current - mut def put(x: Int): Unit = current = x - -def Test(c: Object^) = - val a: Ref^ = Ref(1) - val b: Ref^ = Ref(2) - - val getA = () => a.get - val _: () -> Int = getA // error - - val putA = (x: Int) => a.put(x) - val _: Int -> Unit = putA // error - - def setMax(x: Ref^{cap.rd}, y: Ref^{cap.rd}, z: Ref^{cap.rd}) = - val doit = () => z.put(x.get max y.get) // error - val _: () ->{x.rd, y.rd, z} Unit = doit - doit() diff --git a/tests/neg-custom-args/captures/real-try.check b/tests/neg-custom-args/captures/real-try.check index 6b478b48515a..7a4b12ac08f6 100644 --- a/tests/neg-custom-args/captures/real-try.check +++ b/tests/neg-custom-args/captures/real-try.check @@ -7,7 +7,7 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:14:2 ----------------------------------------------------------- 14 | try // error | ^ - | The result of `try` cannot have type () ->{cap.rd} Unit since + | The result of `try` cannot have type () => Unit since | that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. 15 | () => foo(1) @@ -17,7 +17,7 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:20:10 ---------------------------------------------------------- 20 | val x = try // error | ^ - | The result of `try` cannot have type () ->{cap.rd} Unit since + | The result of `try` cannot have type () => Unit since | that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. 21 | () => foo(1) @@ -27,7 +27,7 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:26:10 ---------------------------------------------------------- 26 | val y = try // error | ^ - | The result of `try` cannot have type () ->{cap.rd} Cell[Unit]^? since + | The result of `try` cannot have type () => Cell[Unit]^? since | that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. 27 | () => Cell(foo(1)) @@ -37,8 +37,8 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:32:10 ---------------------------------------------------------- 32 | val b = try // error | ^ - | The result of `try` cannot have type Cell[box () ->{cap.rd} Unit]^? since - | the part box () ->{cap.rd} Unit of that type captures the root capability `cap`. + | The result of `try` cannot have type Cell[box () => Unit]^? since + | the part box () => Unit of that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. 33 | Cell(() => foo(1)) 34 | catch diff --git a/tests/neg-custom-args/captures/sep-compose.check b/tests/neg-custom-args/captures/sep-compose.check deleted file mode 100644 index d763a180b9ed..000000000000 --- a/tests/neg-custom-args/captures/sep-compose.check +++ /dev/null @@ -1,120 +0,0 @@ --- Error: tests/neg-custom-args/captures/sep-compose.scala:32:10 ------------------------------------------------------- -32 | seq3(f)(f) // error - | ^ - | Separation failure: argument of type (f : () ->{a} Unit) - | to method seq3: (x: () => Unit)(y: () ->{a, cap} Unit): Unit - | corresponds to capture-polymorphic formal parameter y of type () ->{a, cap} Unit - | and captures {f, a, io}, but these capabilities are also passed separately - | in the first argument with type (f : () ->{a} Unit). - | - | Capture set of first argument : {f} - | Hidden set of current argument : {f} - | Footprint of first argument : {f, a, io} - | Hidden footprint of current argument : {f, a, io} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {f, a, io} --- Error: tests/neg-custom-args/captures/sep-compose.scala:33:10 ------------------------------------------------------- -33 | seq4(f)(f) // error - | ^ - | Separation failure: argument of type (f : () ->{a} Unit) - | to method seq4: (x: () ->{a, cap} Unit)(y: () => Unit): Unit - | corresponds to capture-polymorphic formal parameter y of type () => Unit - | and captures {f, a, io}, but these capabilities are also passed separately - | in the first argument with type (f : () ->{a} Unit). - | - | Capture set of first argument : {f} - | Hidden set of current argument : {f} - | Footprint of first argument : {f, a, io} - | Hidden footprint of current argument : {f, a, io} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {f, a, io} --- Error: tests/neg-custom-args/captures/sep-compose.scala:34:10 ------------------------------------------------------- -34 | seq5(f)(f) // error - | ^ - | Separation failure: argument of type (f : () ->{a} Unit) - | to method seq5: (x: () => Unit)(y: () => Unit): Unit - | corresponds to capture-polymorphic formal parameter y of type () => Unit - | and captures {f, a, io}, but these capabilities are also passed separately - | in the first argument with type (f : () ->{a} Unit). - | - | Capture set of first argument : {f} - | Hidden set of current argument : {f} - | Footprint of first argument : {f, a, io} - | Hidden footprint of current argument : {f, a, io} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {f, a, io} --- Error: tests/neg-custom-args/captures/sep-compose.scala:35:10 ------------------------------------------------------- -35 | seq6(f, f) // error - | ^ - | Separation failure: argument of type (f : () ->{a} Unit) - | to method seq6: (x: () => Unit, y: () ->{a, cap} Unit): Unit - | corresponds to capture-polymorphic formal parameter y of type () ->{a, cap} Unit - | and captures {f, a, io}, but these capabilities are also passed separately - | in the first argument with type (f : () ->{a} Unit). - | - | Capture set of first argument : {f} - | Hidden set of current argument : {f} - | Footprint of first argument : {f, a, io} - | Hidden footprint of current argument : {f, a, io} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {f, a, io} --- Error: tests/neg-custom-args/captures/sep-compose.scala:36:10 ------------------------------------------------------- -36 | seq7(f, f) // error - | ^ - | Separation failure: argument of type (f : () ->{a} Unit) - | to method seq7: (x: () ->{a, cap} Unit, y: () => Unit): Unit - | corresponds to capture-polymorphic formal parameter y of type () => Unit - | and captures {f, a, io}, but these capabilities are also passed separately - | in the first argument with type (f : () ->{a} Unit). - | - | Capture set of first argument : {f} - | Hidden set of current argument : {f} - | Footprint of first argument : {f, a, io} - | Hidden footprint of current argument : {f, a, io} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {f, a, io} --- Error: tests/neg-custom-args/captures/sep-compose.scala:37:7 -------------------------------------------------------- -37 | seq8(f)(f) // error - | ^ - | Separation failure: argument of type (f : () ->{a} Unit) - | to method seq8: (x: () => Unit)(y: () ->{a} Unit): Unit - | corresponds to capture-polymorphic formal parameter x of type () => Unit - | and captures {f, a, io}, but these capabilities are also passed separately - | in the second argument with type (f : () ->{a} Unit). - | - | Capture set of second argument : {f} - | Hidden set of current argument : {f} - | Footprint of second argument : {f, a, io} - | Hidden footprint of current argument : {f, a, io} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {f, a, io} --- Error: tests/neg-custom-args/captures/sep-compose.scala:40:5 -------------------------------------------------------- -40 | p1(f) // error - | ^ - | Separation failure: argument of type (f : () ->{a} Unit) - | to a function of type (x$0: () => Unit) ->{f} Unit - | corresponds to capture-polymorphic formal parameter x$0 of type () => Unit - | and captures {f, a, io}, but these capabilities are also passed separately - | in the function prefix. - | - | Capture set of function prefix : {p1} - | Hidden set of current argument : {f} - | Footprint of function prefix : {p1, f, a, io} - | Hidden footprint of current argument : {f, a, io} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {f, a, io} --- Error: tests/neg-custom-args/captures/sep-compose.scala:41:38 ------------------------------------------------------- -41 | val p8 = (x: () ->{a} Unit) => seq8(f)(x) // error - | ^ - | Separation failure: argument of type (f : () ->{a} Unit) - | to method seq8: (x: () => Unit)(y: () ->{a} Unit): Unit - | corresponds to capture-polymorphic formal parameter x of type () => Unit - | and captures {a, io}, but these capabilities are also passed separately - | in the second argument with type (x : () ->{a} Unit). - | - | Capture set of second argument : {x} - | Hidden set of current argument : {f} - | Footprint of second argument : {x, a, io} - | Hidden footprint of current argument : {f, a, io} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {a, io} diff --git a/tests/neg-custom-args/captures/sep-compose.scala b/tests/neg-custom-args/captures/sep-compose.scala deleted file mode 100644 index cfa3318b315f..000000000000 --- a/tests/neg-custom-args/captures/sep-compose.scala +++ /dev/null @@ -1,45 +0,0 @@ -import caps.cap - -def seq1(x: () => Unit, y: () ->{x, cap} Unit): Unit = - x(); y() - -def seq2(x: () => Unit)(y: () ->{x, cap} Unit): Unit = - x(); y() - -def seq5(x: () ->{cap} Unit)(y: () => Unit): Unit = - x(); y() - -def test(io: Object^, a: Object^{io}): Unit = - - def seq3(x: () => Unit)(y: () ->{a, cap} Unit): Unit = - x(); y() - - def seq4(x: () ->{a, cap} Unit)(y: () => Unit): Unit = - x(); y() - - def seq6(x: () => Unit, y: () ->{a, cap} Unit): Unit = - x(); y() - - def seq7(x: () ->{a, cap} Unit, y: () => Unit): Unit = - x(); y() - - def seq8(x: () => Unit)(y: () ->{a} Unit): Unit = - x(); y() - - val f = () => println(a) - seq1(f, f) // ok - seq2(f)(f) // ok - seq3(f)(f) // error - seq4(f)(f) // error - seq5(f)(f) // error - seq6(f, f) // error - seq7(f, f) // error - seq8(f)(f) // error - - val p1 = (x: () => Unit) => seq1(f, x) - p1(f) // error - val p8 = (x: () ->{a} Unit) => seq8(f)(x) // error - p8(f) - - - diff --git a/tests/neg-custom-args/captures/sep-use.check b/tests/neg-custom-args/captures/sep-use.check deleted file mode 100644 index 64e2bd7800bc..000000000000 --- a/tests/neg-custom-args/captures/sep-use.check +++ /dev/null @@ -1,36 +0,0 @@ --- Error: tests/neg-custom-args/captures/sep-use.scala:7:10 ------------------------------------------------------------ -7 | println(io) // error - | ^^ - | Separation failure: Illegal access to {io} which is hidden by the previous definition - | of value x with type () => Unit. - | This type hides capabilities {io} --- Error: tests/neg-custom-args/captures/sep-use.scala:12:12 ----------------------------------------------------------- -12 | def x: () => Unit = () => println(io) // error - | ^^^^^^^^^^ - | Separation failure: method x's result type () => Unit hides non-local parameter io --- Error: tests/neg-custom-args/captures/sep-use.scala:13:10 ----------------------------------------------------------- -13 | println(io) // error - | ^^ - | Separation failure: Illegal access to {io} which is hidden by the previous definition - | of method x with result type () => Unit. - | This type hides capabilities {io} --- Error: tests/neg-custom-args/captures/sep-use.scala:18:10 ----------------------------------------------------------- -18 | def xx: (y: Int) => Unit = _ => println(io) // error - | ^^^^^^^^^^^^^^^^ - | Separation failure: method xx's result type (y: Int) => Unit hides non-local parameter io --- Error: tests/neg-custom-args/captures/sep-use.scala:19:10 ----------------------------------------------------------- -19 | println(io) // error - | ^^ - | Separation failure: Illegal access to {io} which is hidden by the previous definition - | of method xx with result type (y: Int) => Unit. - | This type hides capabilities {io} --- Error: tests/neg-custom-args/captures/sep-use.scala:24:19 ----------------------------------------------------------- -24 | def xxx(y: Int): Object^ = io // error - | ^^^^^^^ - | Separation failure: method xxx's result type Object^ hides non-local parameter io --- Error: tests/neg-custom-args/captures/sep-use.scala:25:10 ----------------------------------------------------------- -25 | println(io) // error - | ^^ - | Separation failure: Illegal access to {io} which is hidden by the previous definition - | of method xxx with result type Object^. - | This type hides capabilities {io} diff --git a/tests/neg-custom-args/captures/sep-use.scala b/tests/neg-custom-args/captures/sep-use.scala deleted file mode 100644 index e89adb0f060e..000000000000 --- a/tests/neg-custom-args/captures/sep-use.scala +++ /dev/null @@ -1,27 +0,0 @@ -import caps.{cap, consume} - - -def test1(@consume io: Object^): Unit = - - val x: () => Unit = () => println(io) - println(io) // error - println(x) // ok - -def test2(@consume io: Object^): Unit = - - def x: () => Unit = () => println(io) // error - println(io) // error - println(x) // ok - -def test3(@consume io: Object^): Unit = - - def xx: (y: Int) => Unit = _ => println(io) // error - println(io) // error - println(xx(2)) // ok - -def test4(@consume io: Object^): Unit = - - def xxx(y: Int): Object^ = io // error - println(io) // error - println(xxx(2)) // ok - diff --git a/tests/neg-custom-args/captures/sep-use2.scala b/tests/neg-custom-args/captures/sep-use2.scala deleted file mode 100644 index 48f2a84c6fe4..000000000000 --- a/tests/neg-custom-args/captures/sep-use2.scala +++ /dev/null @@ -1,29 +0,0 @@ - -import caps.consume - -def test1(@consume c: Object^, f: Object^ => Object^) = - def cc: Object^ = c // error - val x1 = - { f(cc) } // ok - val x2 = - f(cc) // ok - val x3: Object^ = - f(cc) // ok - val x4: Object^ = // error - { f(c) } // error - -def test2(@consume c: Object^, f: Object^ ->{c} Object^) = - def cc: Object^ = c // error - val x1 = - { f(cc) } // error // error - val x4: Object^ = - { f(c) } // error // error - - - - - - - - - diff --git a/tests/neg-custom-args/captures/sepchecks.scala b/tests/neg-custom-args/captures/sepchecks.scala deleted file mode 100644 index 4508b6839781..000000000000 --- a/tests/neg-custom-args/captures/sepchecks.scala +++ /dev/null @@ -1,62 +0,0 @@ -import caps.Mutable -import caps.cap - - -trait Rdr[T]: - def get: T - -class Ref[T](init: T) extends Rdr[T], Mutable: - private var current = init - def get: T = current - mut def put(x: T): Unit = current = x - -def Test(c: Object^): Unit = - val a: Ref[Int]^ = Ref(1) - val b: Ref[Int]^ = Ref(2) - def aa = a - - val getA = () => a.get - val _: () ->{a.rd} Int = getA - - val putA = (x: Int) => a.put(x) - val _: Int ->{a} Unit = putA - - def setMax(x: Ref[Int]^{cap.rd}, y: Ref[Int]^{cap.rd}, z: Ref[Int]^{cap}) = - val doit = () => z.put(x.get max y.get) - val _: () ->{x.rd, y.rd, z} Unit = doit - doit() - - def setMax2(x: Rdr[Int]^{cap.rd}, y: Rdr[Int]^{cap.rd}, z: Ref[Int]^{cap}) = ??? - - setMax2(aa, aa, b) - setMax2(a, aa, b) - setMax2(a, b, b) // error - setMax2(b, b, b) // error - - abstract class IMatrix: - def apply(i: Int, j: Int): Double - - class Matrix(nrows: Int, ncols: Int) extends IMatrix, Mutable: - val arr = Array.fill(nrows, ncols)(0.0) - def apply(i: Int, j: Int): Double = arr(i)(j) - mut def update(i: Int, j: Int, x: Double): Unit = arr(i)(j) = x - - def mul(x: IMatrix^{cap.rd}, y: IMatrix^{cap.rd}, z: Matrix^): Matrix^ = ??? - - val m1 = Matrix(10, 10) - val m2 = Matrix(10, 10) - mul(m1, m2, m2) // error: will fail separation checking - mul(m1, m1, m2) // ok - - def move(get: () => Int, set: Int => Unit) = - set(get()) - - val geta = () => a.get - - def get2(x: () => Int, y: () => Int): (Int, Int) = - (x(), y()) - - move(geta, b.put(_)) // ok - move(geta, a.put(_)) // error - get2(geta, geta) // ok - get2(geta, () => a.get) // ok diff --git a/tests/neg-custom-args/captures/sepchecks2.check b/tests/neg-custom-args/captures/sepchecks2.check deleted file mode 100644 index 45d3553a77d3..000000000000 --- a/tests/neg-custom-args/captures/sepchecks2.check +++ /dev/null @@ -1,52 +0,0 @@ --- Error: tests/neg-custom-args/captures/sepchecks2.scala:10:10 -------------------------------------------------------- -10 | println(c) // error - | ^ - | Separation failure: Illegal access to {c} which is hidden by the previous definition - | of value xs with type List[box () => Unit]. - | This type hides capabilities {xs*, c} --- Error: tests/neg-custom-args/captures/sepchecks2.scala:13:33 -------------------------------------------------------- -13 | foo((() => println(c)) :: Nil, c) // error - | ^ - | Separation failure: argument of type (c : Object^) - | to method foo: (xs: List[box () => Unit], y: Object^): Nothing - | corresponds to capture-polymorphic formal parameter y of type Object^ - | and captures {c}, but this capability is also passed separately - | in the first argument with type List[box () ->{c} Unit]. - | - | Capture set of first argument : {c} - | Hidden set of current argument : {c} - | Footprint of first argument : {c} - | Hidden footprint of current argument : {c} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {c} --- Error: tests/neg-custom-args/captures/sepchecks2.scala:14:10 -------------------------------------------------------- -14 | val x1: (Object^, Object^) = (c, c) // error - | ^^^^^^^^^^^^^^^^^^ - | Separation failure in value x1's type (box Object^, box Object^). - | One part, box Object^ , hides {c}. - | A previous part, box Object^ , also hides {c}. - | The two sets overlap at {c}. --- Error: tests/neg-custom-args/captures/sepchecks2.scala:15:10 -------------------------------------------------------- -15 | val x2: (Object^, Object^{d}) = (d, d) // error - | ^^^^^^^^^^^^^^^^^^^^^ - | Separation failure in value x2's type (box Object^, box Object^{d}). - | One part, box Object^{d} , references {d}. - | A previous part, box Object^ , hides {d}. - | The two sets overlap at {d}. --- Error: tests/neg-custom-args/captures/sepchecks2.scala:27:6 --------------------------------------------------------- -27 | bar((c, c)) // error - | ^^^^^^ - | Separation failure in the argument's adapted type (box Object^, box Object^). - | One part, box Object^ , hides {c}. - | A previous part, box Object^ , also hides {c}. - | The two sets overlap at {c}. --- Error: tests/neg-custom-args/captures/sepchecks2.scala:30:9 --------------------------------------------------------- -30 | val x: (Object^, Object^{c}) = (d, c) // error - | ^^^^^^^^^^^^^^^^^^^^^ - | Separation failure: value x's type (box Object^, box Object^{c}) hides parameter d. - | The parameter needs to be annotated with @consume to allow this. --- Error: tests/neg-custom-args/captures/sepchecks2.scala:33:9 --------------------------------------------------------- -33 | val x: (Object^, Object^) = (c, d) // error - | ^^^^^^^^^^^^^^^^^^ - | Separation failure: value x's type (box Object^, box Object^) hides parameters c and d. - | The parameters need to be annotated with @consume to allow this. diff --git a/tests/neg-custom-args/captures/sepchecks2.scala b/tests/neg-custom-args/captures/sepchecks2.scala deleted file mode 100644 index 34ca3a56a243..000000000000 --- a/tests/neg-custom-args/captures/sepchecks2.scala +++ /dev/null @@ -1,35 +0,0 @@ - -import caps.consume - -def foo(xs: List[() => Unit], y: Object^) = ??? - -def bar(x: (Object^, Object^)): Unit = ??? - -def Test(@consume c: Object^) = - val xs: List[() => Unit] = (() => println(c)) :: Nil - println(c) // error - -def Test2(c: Object^, d: Object^): Unit = - foo((() => println(c)) :: Nil, c) // error - val x1: (Object^, Object^) = (c, c) // error - val x2: (Object^, Object^{d}) = (d, d) // error - -def Test3(@consume c: Object^, @consume d: Object^) = - val x: (Object^, Object^) = (c, d) // ok - -def Test4(@consume c: Object^, @consume d: Object^) = - val x: (Object^, Object^{c}) = (d, c) // ok - -def Test5(c: Object^, d: Object^): Unit = - bar((c, d)) // ok - -def Test6(c: Object^, d: Object^): Unit = - bar((c, c)) // error - -def Test7(c: Object^, d: Object^) = - val x: (Object^, Object^{c}) = (d, c) // error - -def Test8(c: Object^, d: Object^) = - val x: (Object^, Object^) = (c, d) // error - - diff --git a/tests/neg-custom-args/captures/sepchecks3.scala b/tests/neg-custom-args/captures/sepchecks3.scala deleted file mode 100644 index 8cc7d705b42b..000000000000 --- a/tests/neg-custom-args/captures/sepchecks3.scala +++ /dev/null @@ -1,12 +0,0 @@ - -import caps.consume - -def foo(xs: List[() => Unit], y: Object^) = ??? - -def bar(x: (Object^, Object^)): Unit = ??? - -def Test(c: Object^): Object^ = c // error - -def Test2(@consume c: Object^): Object^ = c // ok - -def Test3(c: Object^): List[Object^] = c :: Nil // error diff --git a/tests/neg-custom-args/captures/sepchecks4.check b/tests/neg-custom-args/captures/sepchecks4.check deleted file mode 100644 index 28f7ec55b944..000000000000 --- a/tests/neg-custom-args/captures/sepchecks4.check +++ /dev/null @@ -1,25 +0,0 @@ --- Error: tests/neg-custom-args/captures/sepchecks4.scala:8:12 --------------------------------------------------------- -8 | val x: () => Unit = () => println(io) // error - | ^^^^^^^^^^ - | Separation failure: value x's type () => Unit hides parameter io. - | The parameter needs to be annotated with @consume to allow this. --- Error: tests/neg-custom-args/captures/sepchecks4.scala:7:25 --------------------------------------------------------- -7 |def bad(io: Object^): () => Unit = // error - | ^^^^^^^^^^ - | Separation failure: method bad's result type () => Unit hides parameter io. - | The parameter needs to be annotated with @consume to allow this. --- Error: tests/neg-custom-args/captures/sepchecks4.scala:12:25 -------------------------------------------------------- -12 | par(() => println(io))(() => println(io)) // error // (1) - | ^^^^^^^^^^^^^^^^^ - | Separation failure: argument of type () ->{io} Unit - | to method par: (op1: () => Unit)(op2: () => Unit): Unit - | corresponds to capture-polymorphic formal parameter op2 of type () => Unit - | and captures {io}, but this capability is also passed separately - | in the first argument with type () ->{io} Unit. - | - | Capture set of first argument : {io} - | Hidden set of current argument : {io} - | Footprint of first argument : {io} - | Hidden footprint of current argument : {io} - | Declared footprint of current argument: {} - | Undeclared overlap of footprints : {io} diff --git a/tests/neg-custom-args/captures/sepchecks4.scala b/tests/neg-custom-args/captures/sepchecks4.scala deleted file mode 100644 index d44b31ca02dc..000000000000 --- a/tests/neg-custom-args/captures/sepchecks4.scala +++ /dev/null @@ -1,16 +0,0 @@ -import caps.cap -import language.future -import language.experimental.captureChecking - -def par(op1: () => Unit)(op2: () => Unit): Unit = () - -def bad(io: Object^): () => Unit = // error - val x: () => Unit = () => println(io) // error - x - -def test(io: Object^): Unit = - par(() => println(io))(() => println(io)) // error // (1) - - val f = bad(io) - par(f)(() => println(io)) // no error, but it is equivalent to (1) and should failimport caps.consume - diff --git a/tests/neg-custom-args/captures/sepchecks5.check b/tests/neg-custom-args/captures/sepchecks5.check deleted file mode 100644 index b65b6a46e6e2..000000000000 --- a/tests/neg-custom-args/captures/sepchecks5.check +++ /dev/null @@ -1,10 +0,0 @@ --- Error: tests/neg-custom-args/captures/sepchecks5.scala:12:37 -------------------------------------------------------- -12 |def bad(io: Object^): () => Unit = f(io) // error - | ^^ - | Separation failure: argument to @consume parameter with type (io : Object^) refers to parameter io. - | The parameter needs to be annotated with @consume to allow this. --- Error: tests/neg-custom-args/captures/sepchecks5.scala:19:13 -------------------------------------------------------- -19 | val f2 = g(io) // error - | ^^ - | Separation failure: argument to @consume parameter with type (io : Object^) refers to parameter io. - | The parameter needs to be annotated with @consume to allow this. diff --git a/tests/neg-custom-args/captures/sepchecks5.scala b/tests/neg-custom-args/captures/sepchecks5.scala deleted file mode 100644 index 5e2d4796f9f7..000000000000 --- a/tests/neg-custom-args/captures/sepchecks5.scala +++ /dev/null @@ -1,21 +0,0 @@ -import caps.{cap, consume} -import language.future -import language.experimental.captureChecking - -def par(op1: () => Unit)(op2: () => Unit): Unit = () - -def f(@consume io: Object^): () => Unit = - () => println(io) - -def g(@consume io: Object^): () => Unit = f(io) // ok - -def bad(io: Object^): () => Unit = f(io) // error - -def test(io: Object^): Unit = - - val f1 = bad(io) - par(f1)(() => println(io)) // !!! separation failure - - val f2 = g(io) // error - par(f2)(() => println(io)) // !!! separation failure - diff --git a/tests/neg-custom-args/captures/shared-capability.check b/tests/neg-custom-args/captures/shared-capability.check deleted file mode 100644 index 64fb3eb39d44..000000000000 --- a/tests/neg-custom-args/captures/shared-capability.check +++ /dev/null @@ -1,4 +0,0 @@ --- Error: tests/neg-custom-args/captures/shared-capability.scala:9:13 -------------------------------------------------- -9 |def test2(a: Async^): Object^ = a // error - | ^^^^^^ - | Async^ extends SharedCapability, so it cannot capture `cap` diff --git a/tests/neg-custom-args/captures/shared-capability.scala b/tests/neg-custom-args/captures/shared-capability.scala deleted file mode 100644 index 262a6db386ba..000000000000 --- a/tests/neg-custom-args/captures/shared-capability.scala +++ /dev/null @@ -1,10 +0,0 @@ - - -import caps.SharedCapability - -class Async extends SharedCapability - -def test1(a: Async): Object^ = a // OK - -def test2(a: Async^): Object^ = a // error - diff --git a/tests/neg-custom-args/captures/unsound-reach-2.scala b/tests/neg-custom-args/captures/unsound-reach-2.scala index 944ef82da5bb..c7dfa117a2fe 100644 --- a/tests/neg-custom-args/captures/unsound-reach-2.scala +++ b/tests/neg-custom-args/captures/unsound-reach-2.scala @@ -1,4 +1,4 @@ -import language.experimental.captureChecking; +import language.experimental.captureChecking trait Consumer[-T]: def apply(x: T): Unit @@ -13,7 +13,7 @@ class Bar extends Foo[File^]: // error def use(x: File^)(op: Consumer[File^]): Unit = op.apply(x) def bad(): Unit = - val backdoor: Foo[File^] = new Bar // error (follow-on, since the parent Foo[File^] of bar is illegal). + val backdoor: Foo[File^] = new Bar val boom: Foo[File^{backdoor*}] = backdoor var escaped: File^{backdoor*} = null diff --git a/tests/neg-custom-args/captures/unsound-reach-3.scala b/tests/neg-custom-args/captures/unsound-reach-3.scala index 0aeb10e39916..c5cdfca9d87a 100644 --- a/tests/neg-custom-args/captures/unsound-reach-3.scala +++ b/tests/neg-custom-args/captures/unsound-reach-3.scala @@ -1,19 +1,18 @@ -import language.experimental.captureChecking -import caps.consume +import language.experimental.captureChecking trait File: def close(): Unit def withFile[R](path: String)(op: File^ => R): R = ??? trait Foo[+X]: - def use(@consume x: File^): X + def use(x: File^): X class Bar extends Foo[File^]: // error - def use(@consume x: File^): File^ = x + def use(x: File^): File^ = x def bad(): Unit = - val backdoor: Foo[File^] = new Bar // error (follow-on, since the parent Foo[File^] of bar is illegal). + val backdoor: Foo[File^] = new Bar val boom: Foo[File^{backdoor*}] = backdoor var escaped: File^{backdoor*} = null diff --git a/tests/neg-custom-args/captures/unsound-reach-4.check b/tests/neg-custom-args/captures/unsound-reach-4.check index c4905cee258c..ca95bf42ba59 100644 --- a/tests/neg-custom-args/captures/unsound-reach-4.check +++ b/tests/neg-custom-args/captures/unsound-reach-4.check @@ -1,23 +1,9 @@ --- Error: tests/neg-custom-args/captures/unsound-reach-4.scala:16:18 --------------------------------------------------- -16 |class Bar extends Foo[File^]: // error +-- Error: tests/neg-custom-args/captures/unsound-reach-4.scala:13:18 --------------------------------------------------- +13 |class Bar extends Foo[File^]: // error | ^^^^^^^^^^ | Type variable X of trait Foo cannot be instantiated to File^ since | that type captures the root capability `cap`. --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/unsound-reach-4.scala:20:29 ------------------------------ -20 | val backdoor: Foo[File^] = new Bar // error (follow-on, since the parent Foo[File^] of bar is illegal). - | ^^^^^^^ - | Found: Bar^? - | Required: Foo[box File^] - | - | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/unsound-reach-4.scala:25:22 --------------------------------------------------- -25 | escaped = boom.use(f) // error +-- Error: tests/neg-custom-args/captures/unsound-reach-4.scala:22:22 --------------------------------------------------- +22 | escaped = boom.use(f) // error | ^^^^^^^^^^^ | Local reach capability backdoor* leaks into capture scope of method bad --- [E164] Declaration Error: tests/neg-custom-args/captures/unsound-reach-4.scala:17:6 --------------------------------- -17 | def use(@consume x: F): File^ = x // error @consume override - | ^ - |error overriding method use in trait Foo of type (x: File^): box File^; - | method use of type (x: File^): (ex$2: caps.Exists) -> File^{ex$2} has a parameter x with different @consume status than the corresponding parameter in the overridden definition - | - | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/unsound-reach-4.scala b/tests/neg-custom-args/captures/unsound-reach-4.scala index d636afd027d4..88fbc2f5c1de 100644 --- a/tests/neg-custom-args/captures/unsound-reach-4.scala +++ b/tests/neg-custom-args/captures/unsound-reach-4.scala @@ -1,9 +1,6 @@ - -import language.experimental.captureChecking; -import caps.consume - +import language.experimental.captureChecking trait File: def close(): Unit @@ -14,10 +11,10 @@ type F = File^ trait Foo[+X]: def use(x: F): X class Bar extends Foo[File^]: // error - def use(@consume x: F): File^ = x // error @consume override + def use(x: F): File^ = x def bad(): Unit = - val backdoor: Foo[File^] = new Bar // error (follow-on, since the parent Foo[File^] of bar is illegal). + val backdoor: Foo[File^] = new Bar val boom: Foo[File^{backdoor*}] = backdoor var escaped: File^{backdoor*} = null diff --git a/tests/neg-custom-args/captures/unsound-reach-6.check b/tests/neg-custom-args/captures/unsound-reach-6.check deleted file mode 100644 index 90fd6c40fbfd..000000000000 --- a/tests/neg-custom-args/captures/unsound-reach-6.check +++ /dev/null @@ -1,13 +0,0 @@ --- Error: tests/neg-custom-args/captures/unsound-reach-6.scala:7:13 ---------------------------------------------------- -7 | println(xs.head) // error - | ^^^^^^^ - | Local reach capability xs* leaks into capture scope of method f. - | To allow this, the parameter xs should be declared with a @use annotation --- Error: tests/neg-custom-args/captures/unsound-reach-6.scala:11:14 --------------------------------------------------- -11 | val z = f(ys) // error @consume failure - | ^^ - |Separation failure: argument to @consume parameter with type (ys : List[box () ->{io} Unit]) refers to non-local parameter ys --- Error: tests/neg-custom-args/captures/unsound-reach-6.scala:19:14 --------------------------------------------------- -19 | val z = f(ys) // error @consume failure - | ^^ - |Separation failure: argument to @consume parameter with type (ys : -> List[box () ->{io} Unit]) refers to non-local parameter io diff --git a/tests/neg-custom-args/captures/unsound-reach-6.scala b/tests/neg-custom-args/captures/unsound-reach-6.scala index 4ce789025837..b7306dca4190 100644 --- a/tests/neg-custom-args/captures/unsound-reach-6.scala +++ b/tests/neg-custom-args/captures/unsound-reach-6.scala @@ -1,14 +1,11 @@ - -import caps.consume - class IO -def f(@consume xs: List[() => Unit]): () => Unit = () => +def f(xs: List[() => Unit]): () => Unit = () => println(xs.head) // error def test(io: IO^)(ys: List[() ->{io} Unit]) = val x = () => - val z = f(ys) // error @consume failure + val z = f(ys) z() val _: () -> Unit = x // !!! ys* gets lost () @@ -16,7 +13,7 @@ def test(io: IO^)(ys: List[() ->{io} Unit]) = def test(io: IO^) = def ys: List[() ->{io} Unit] = ??? val x = () => - val z = f(ys) // error @consume failure + val z = f(ys) z() val _: () -> Unit = x // !!! io gets lost () diff --git a/tests/neg-custom-args/captures/unsound-reach.check b/tests/neg-custom-args/captures/unsound-reach.check index 17d4a4420833..69794f569edb 100644 --- a/tests/neg-custom-args/captures/unsound-reach.check +++ b/tests/neg-custom-args/captures/unsound-reach.check @@ -8,13 +8,6 @@ | ^ | Type variable X of constructor Foo2 cannot be instantiated to box File^ since | that type captures the root capability `cap`. --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/unsound-reach.scala:18:31 -------------------------------- -18 | val backdoor: Foo[File^] = new Bar // error (follow-on, since the parent Foo[File^] of bar is illegal). - | ^^^^^^^ - | Found: Bar^? - | Required: Foo[box File^] - | - | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/unsound-reach.scala:23:21 ----------------------------------------------------- 23 | boom.use(f): (f1: File^{backdoor*}) => // error | ^ diff --git a/tests/neg-custom-args/captures/unsound-reach.scala b/tests/neg-custom-args/captures/unsound-reach.scala index 0aa7f1fc7ee9..3fb666c7c1fc 100644 --- a/tests/neg-custom-args/captures/unsound-reach.scala +++ b/tests/neg-custom-args/captures/unsound-reach.scala @@ -1,4 +1,4 @@ -import language.experimental.captureChecking; +import language.experimental.captureChecking trait File: def close(): Unit @@ -15,7 +15,7 @@ class Bar2 extends Foo2[File^]: // error def use(x: File^)(op: File^ => Unit): Unit = op(x) // OK using sealed checking def bad(): Unit = - val backdoor: Foo[File^] = new Bar // error (follow-on, since the parent Foo[File^] of bar is illegal). + val backdoor: Foo[File^] = new Bar val boom: Foo[File^{backdoor*}] = backdoor var escaped: File^{backdoor*} = null diff --git a/tests/neg-custom-args/captures/update-call.scala b/tests/neg-custom-args/captures/update-call.scala deleted file mode 100644 index 848e4d880223..000000000000 --- a/tests/neg-custom-args/captures/update-call.scala +++ /dev/null @@ -1,19 +0,0 @@ -import caps.Mutable - -trait IterableOnce[T] extends Mutable: - def iterator: Iterator[T]^{this} - mut def foreach(op: T => Unit): Unit - -trait Iterator[T] extends IterableOnce[T]: - def iterator = this - def hasNext: Boolean - mut def next(): T - mut def foreach(op: T => Unit): Unit = ??? - override mut def toString = ??? // error - -trait Iterable[T] extends IterableOnce[T]: - def iterator: Iterator[T] = ??? - def foreach(op: T => Unit) = iterator.foreach(op) - -trait BadIterator[T] extends Iterator[T]: - override mut def hasNext: Boolean // error diff --git a/tests/neg-custom-args/captures/vars.check b/tests/neg-custom-args/captures/vars.check index 4fe4163aa433..db5c8083e3b7 100644 --- a/tests/neg-custom-args/captures/vars.check +++ b/tests/neg-custom-args/captures/vars.check @@ -1,11 +1,10 @@ -- Error: tests/neg-custom-args/captures/vars.scala:24:14 -------------------------------------------------------------- 24 | a = x => g(x) // error | ^^^^ - | reference (cap3 : CC^) is not included in the allowed capture set {cap1} - | of an enclosing function literal with expected type (x$0: String) ->{cap1} String + | reference (cap3 : CC^) is not included in the allowed capture set {cap1} of variable a | | Note that reference (cap3 : CC^), defined in method scope - | cannot be included in outer capture set {cap1} + | cannot be included in outer capture set {cap1} of variable a -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/vars.scala:25:8 ------------------------------------------ 25 | a = g // error | ^ @@ -13,7 +12,7 @@ | Required: (x$0: String) ->{cap1} String | | Note that reference (cap3 : CC^), defined in method scope - | cannot be included in outer capture set {cap1} + | cannot be included in outer capture set {cap1} of variable a | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/vars.scala:27:12 ----------------------------------------- diff --git a/tests/pos-custom-args/captures/boxmap-paper.scala b/tests/pos-custom-args/captures/boxmap-paper.scala index 436132280d40..20282d5813f9 100644 --- a/tests/pos-custom-args/captures/boxmap-paper.scala +++ b/tests/pos-custom-args/captures/boxmap-paper.scala @@ -1,4 +1,3 @@ -import caps.cap type Cell_orig[+T] = [K] -> (T => K) -> K @@ -19,13 +18,13 @@ def map[A, B](c: Cell[A])(f: A => B): Cell[B] def pureMap[A, B](c: Cell[A])(f: A -> B): Cell[B] = c[Cell[B]]((x: A) => cell(f(x))) -def lazyMap[A, B](c: Cell[A])(f: A ->{cap.rd} B): () ->{f} Cell[B] +def lazyMap[A, B](c: Cell[A])(f: A => B): () ->{f} Cell[B] = () => c[Cell[B]]((x: A) => cell(f(x))) trait IO: def print(s: String): Unit -def test(io: IO^{cap.rd}) = +def test(io: IO^) = val loggedOne: () ->{io} Int = () => { io.print("1"); 1 } diff --git a/tests/pos-custom-args/captures/capt1.scala b/tests/pos-custom-args/captures/capt1.scala index f8cf39933bef..e3f5c20e724e 100644 --- a/tests/pos-custom-args/captures/capt1.scala +++ b/tests/pos-custom-args/captures/capt1.scala @@ -1,6 +1,3 @@ - -import caps.unsafe.unsafeAssumeSeparate - class C type Cap = C^ def f1(c: Cap): () ->{c} c.type = () => c // ok @@ -25,9 +22,6 @@ def foo(): C^ = val z1: () => Cap = f1(x) def h[X](a: X)(b: X) = a - val z2 = unsafeAssumeSeparate: - if x == null then - () => x - else - () => C() - unsafeAssumeSeparate(x) \ No newline at end of file + val z2 = + if x == null then () => x else () => C() + x \ No newline at end of file diff --git a/tests/pos-custom-args/captures/cc-dep-param.scala b/tests/pos-custom-args/captures/cc-dep-param.scala index 5fd634de9040..1440cd4d7d40 100644 --- a/tests/pos-custom-args/captures/cc-dep-param.scala +++ b/tests/pos-custom-args/captures/cc-dep-param.scala @@ -1,9 +1,8 @@ import language.experimental.captureChecking -import caps.cap trait Foo[T] def test(): Unit = - val a: Foo[Int]^{cap.rd} = ??? + val a: Foo[Int]^ = ??? val useA: () ->{a} Unit = ??? def foo[X](x: Foo[X]^, op: () ->{x} Unit): Unit = ??? foo(a, useA) diff --git a/tests/pos-custom-args/captures/cc-this.scala b/tests/pos-custom-args/captures/cc-this.scala index 638c20d94a91..d9705df76c55 100644 --- a/tests/pos-custom-args/captures/cc-this.scala +++ b/tests/pos-custom-args/captures/cc-this.scala @@ -1,11 +1,8 @@ -import caps.consume - - class Cap extends caps.Capability def eff(using Cap): Unit = () -def test(using @consume cc: Cap) = +def test(using Cap) = class C(val x: () => Int): val y: C^ = this diff --git a/tests/pos-custom-args/captures/eta-expansions.scala b/tests/pos-custom-args/captures/eta-expansions.scala index cbe72137bd65..b4e38cdf0856 100644 --- a/tests/pos-custom-args/captures/eta-expansions.scala +++ b/tests/pos-custom-args/captures/eta-expansions.scala @@ -3,7 +3,7 @@ class Cap extends caps.Capability def test(d: Cap) = def map2(xs: List[Int])(f: Int => Int): List[Int] = xs.map(f) val f1 = map2 // capture polymorphic implicit eta expansion - val f2c: List[Int] => (Int => Int) => List[Int] = f1 + def f2c: List[Int] => (Int => Int) => List[Int] = f1 val a0 = identity[Cap ->{d} Unit] // capture monomorphic implicit eta expansion val a0c: (Cap ->{d} Unit) ->{d} Cap ->{d} Unit = a0 val b0 = (x: Cap ->{d} Unit) => identity[Cap ->{d} Unit](x) // not an implicit eta expansion, hence capture polymorphic diff --git a/tests/neg-custom-args/captures/filevar-expanded.scala b/tests/pos-custom-args/captures/filevar-expanded.scala similarity index 94% rename from tests/neg-custom-args/captures/filevar-expanded.scala rename to tests/pos-custom-args/captures/filevar-expanded.scala index 461a617bde0d..58e7a0e67e0a 100644 --- a/tests/neg-custom-args/captures/filevar-expanded.scala +++ b/tests/pos-custom-args/captures/filevar-expanded.scala @@ -31,7 +31,7 @@ object test2: op(new File) def test(io3: IO^) = - withFile(io3): f => // error: separation failure + withFile(io3): f => val o = Service(io3) o.file = f // this is a bit dubious. It's legal since we treat class refinements // as capture set variables that can be made to include refs coming from outside. diff --git a/tests/pos-custom-args/captures/filter-iterable.scala b/tests/pos-custom-args/captures/filter-iterable.scala deleted file mode 100644 index c8e80af4cd73..000000000000 --- a/tests/pos-custom-args/captures/filter-iterable.scala +++ /dev/null @@ -1,11 +0,0 @@ -import caps.cap - -class It[A] - -class Filter[A](val underlying: It[A]^, val p: A ->{cap, underlying} Boolean) extends It[A] -object Filter: - def apply[A](underlying: It[A]^, p: A => Boolean): Filter[A]^{cap, p, underlying} = - underlying match - case filter: Filter[A]^ => - val x = new Filter(filter.underlying, a => filter.p(a) && p(a)) - x: Filter[A]^{filter, p} \ No newline at end of file diff --git a/tests/pos-custom-args/captures/foreach2.scala b/tests/pos-custom-args/captures/foreach2.scala deleted file mode 100644 index 318bcb9cddfc..000000000000 --- a/tests/pos-custom-args/captures/foreach2.scala +++ /dev/null @@ -1,7 +0,0 @@ -import annotation.unchecked.uncheckedCaptures - -class ArrayBuffer[T]: - def foreach(op: T => Unit): Unit = ??? -def test = - val tasks = new ArrayBuffer[(() => Unit) @uncheckedCaptures] - val _: Unit = tasks.foreach(((task: () => Unit) => task())) diff --git a/tests/pos-custom-args/captures/i15749a.scala b/tests/pos-custom-args/captures/i15749a.scala index c008d20a155c..184f980d6d70 100644 --- a/tests/pos-custom-args/captures/i15749a.scala +++ b/tests/pos-custom-args/captures/i15749a.scala @@ -1,7 +1,6 @@ import caps.cap import caps.use - class Unit object u extends Unit @@ -14,7 +13,7 @@ def test = def wrapper[T](x: T): Wrapper[T] = Wrapper: [X] => (op: T ->{cap} X) => op(x) - def strictMap[A <: Top, B <: Top](mx: Wrapper[A])(f: A ->{cap, mx*} B): Wrapper[B] = + def strictMap[A <: Top, B <: Top](mx: Wrapper[A])(f: A ->{cap} B): Wrapper[B] = mx.value((x: A) => wrapper(f(x))) def force[A](thunk: Unit ->{cap} A): A = thunk(u) diff --git a/tests/pos-custom-args/captures/lazyref.scala b/tests/pos-custom-args/captures/lazyref.scala index f4c85de9a51a..2e3a0030bcdc 100644 --- a/tests/pos-custom-args/captures/lazyref.scala +++ b/tests/pos-custom-args/captures/lazyref.scala @@ -1,6 +1,3 @@ - -import caps.consume - class Cap extends caps.Capability class LazyRef[T](val elem: () => T): @@ -14,7 +11,7 @@ def map[A, B](ref: LazyRef[A]^, f: A => B): LazyRef[B]^{f, ref} = def mapc[A, B]: (ref: LazyRef[A]^, f: A => B) => LazyRef[B]^{f, ref} = (ref1, f1) => map[A, B](ref1, f1) -def test(@consume cap1: Cap, @consume cap2: Cap) = +def test(cap1: Cap, cap2: Cap) = def f(x: Int) = if cap1 == cap1 then x else 0 def g(x: Int) = if cap2 == cap2 then x else 0 val ref1 = LazyRef(() => f(0)) diff --git a/tests/pos-custom-args/captures/mutRef.scala b/tests/pos-custom-args/captures/mutRef.scala deleted file mode 100644 index 5fe82c9b987a..000000000000 --- a/tests/pos-custom-args/captures/mutRef.scala +++ /dev/null @@ -1,5 +0,0 @@ -import caps.Mutable -class Ref(init: Int) extends Mutable: - private var current = init - def get: Int = current - mut def put(x: Int): Unit = current = x diff --git a/tests/pos-custom-args/captures/nested-classes-2.scala b/tests/pos-custom-args/captures/nested-classes-2.scala index 7290ed4a12ea..744635ee949b 100644 --- a/tests/pos-custom-args/captures/nested-classes-2.scala +++ b/tests/pos-custom-args/captures/nested-classes-2.scala @@ -1,7 +1,21 @@ + +def f(x: (() => Unit)): (() => Unit) => (() => Unit) = + def g(y: (() => Unit)): (() => Unit) = x + g + +def test1(x: (() => Unit)): Unit = + def test2(y: (() => Unit)) = + val a: (() => Unit) => (() => Unit) = f(y) + a(x) // OK, but should be error + test2(() => ()) + def test2(x1: (() => Unit), x2: (() => Unit) => Unit) = class C1(x1: (() => Unit), xx2: (() => Unit) => Unit): - def c2(y1: (() => Unit), y2: (() => Unit) => Unit): C2^ = ??? - class C2(y1: (() => Unit), y2: (() => Unit) => Unit) + def c2(y1: (() => Unit), y2: (() => Unit) => Unit): C2^ = C2(y1, y2) + class C2(y1: (() => Unit), y2: (() => Unit) => Unit): + val a: (() => Unit) => (() => Unit) = f(y1) + a(x1) //OK, but should be error + C2(() => (), x => ()) def test3(y1: (() => Unit), y2: (() => Unit) => Unit) = val cc1: C1^{y1, y2} = C1(y1, y2) diff --git a/tests/pos-custom-args/captures/reaches.scala b/tests/pos-custom-args/captures/reaches.scala index 131dce862b02..cbe88e60020b 100644 --- a/tests/pos-custom-args/captures/reaches.scala +++ b/tests/pos-custom-args/captures/reaches.scala @@ -1,5 +1,4 @@ - -import caps.{use, consume} +import caps.use class C def f(xs: List[C^]) = @@ -37,7 +36,7 @@ def cons(x: Proc, xs: List[Proc]): List[() ->{x, xs*} Unit] = val y = x :: xs y -def addOneProc(@consume xs: List[Proc]): List[Proc] = +def addOneProc(xs: List[Proc]): List[Proc] = val x: Proc = () => println("hello") val result: List[() ->{x, xs*} Unit] = x :: xs result // OK, we can widen () ->{x, xs*} Unit to cap here. @@ -45,7 +44,7 @@ def addOneProc(@consume xs: List[Proc]): List[Proc] = def compose1[A, B, C](f: A => B, g: B => C): A ->{f, g} C = z => g(f(z)) -def compose2[A, B, C](@consume f: A => B, @consume g: B => C): A => C = +def compose2[A, B, C](f: A => B, g: B => C): A => C = z => g(f(z)) //def mapCompose[A](ps: List[(A => A, A => A)]): List[A ->{ps*} A] = diff --git a/tests/pos-custom-args/captures/sep-compose.scala b/tests/pos-custom-args/captures/sep-compose.scala deleted file mode 100644 index 3a56bd789282..000000000000 --- a/tests/pos-custom-args/captures/sep-compose.scala +++ /dev/null @@ -1,21 +0,0 @@ -import caps.cap - - -def seq1(x: () => Unit, y: () ->{x, cap} Unit): Unit = - x(); y() - -def seq2(x: () => Unit)(y: () ->{x, cap} Unit): Unit = - x(); y() - -def test(io: Object^, a: Object^{io}): Unit = - val f = () => println(a) - val g = () => println(a) - seq1(f, f) - seq2(f)(f) - seq1(g, g) - seq2(g)(g) - - seq1(f, g) - seq2(f)(g) - seq1(g, f) - seq2(g)(f) \ No newline at end of file diff --git a/tests/pos-custom-args/captures/sep-eq.scala b/tests/pos-custom-args/captures/sep-eq.scala deleted file mode 100644 index 94a64cc0c8f4..000000000000 --- a/tests/pos-custom-args/captures/sep-eq.scala +++ /dev/null @@ -1,20 +0,0 @@ -import caps.Mutable -import caps.cap - - -extension (x: Object^) - infix def eql (y: Object^{x, cap}): Boolean = x eq y - -def eql1(x: Object^, y: Object^{x, cap}): Boolean = x eql y -def eql2(x: Object^)(y: Object^{x, cap}): Boolean = x eql y - -class LLI extends Object: - this: LLI^ => - - val f: Object^ = ??? - - def foo = - def these = f - val eq0 = these eql these - val eq1 = eql2(f)(f) - val eq2 = eql2(these)(these) diff --git a/tests/pos-custom-args/captures/simple-apply.scala b/tests/pos-custom-args/captures/simple-apply.scala deleted file mode 100644 index 1e2a6715dd79..000000000000 --- a/tests/pos-custom-args/captures/simple-apply.scala +++ /dev/null @@ -1,6 +0,0 @@ -object Test: - - def foo(x: Object^, ys: List[Object^]) = ??? - def test(io: Object^, async: Object^): Unit = - val v: Object^{io} = ??? - foo(v, List(async)) diff --git a/tests/pos-custom-args/captures/skolems2.scala b/tests/pos-custom-args/captures/skolems2.scala deleted file mode 100644 index 74438aa5793a..000000000000 --- a/tests/pos-custom-args/captures/skolems2.scala +++ /dev/null @@ -1,19 +0,0 @@ - -import caps.consume -import caps.unsafe.unsafeAssumeSeparate - -def Test(@consume c: Object^, f: Object^ => Object^) = - def cc: Object^ = unsafeAssumeSeparate(c) - val x1 = - { f(cc) } - val x2 = - f(cc) - val x3: Object^ = - f(cc) - val x4: Object^ = - { f(cc) } - - - - - diff --git a/tests/pos-special/stdlib/Test2.scala b/tests/pos-special/stdlib/Test2.scala index e0d9a1491516..cab9440c17db 100644 --- a/tests/pos-special/stdlib/Test2.scala +++ b/tests/pos-special/stdlib/Test2.scala @@ -2,7 +2,6 @@ import scala.reflect.ClassTag import language.experimental.captureChecking import collection.{View, Seq} import collection.mutable.{ArrayBuffer, ListBuffer} -import caps.unsafe.unsafeAssumeSeparate object Test { @@ -88,7 +87,7 @@ object Test { val ys9: Iterator[Boolean]^{xs9} = xs9 val xs10 = xs.flatMap(flips) val ys10: Iterator[Int]^{xs10} = xs10 - val xs11 = unsafeAssumeSeparate(xs ++ xs) + val xs11 = xs ++ xs val ys11: Iterator[Int]^{xs11} = xs11 val xs12 = xs ++ Nil val ys12: Iterator[Int]^{xs12} = xs12 @@ -96,7 +95,7 @@ object Test { val ys13: List[Int] = xs13 val xs14 = xs ++ ("a" :: Nil) val ys14: Iterator[Any]^{xs14} = xs14 - val xs15 = unsafeAssumeSeparate(xs.zip(xs9)) + val xs15 = xs.zip(xs9) val ys15: Iterator[(Int, Boolean)]^{xs15} = xs15 println("-------") println(x1) @@ -142,7 +141,7 @@ object Test { val ys9: View[Boolean]^{xs9} = xs9 val xs10 = xs.flatMap(flips) val ys10: View[Int]^{xs10} = xs10 - val xs11 = unsafeAssumeSeparate(xs ++ xs) + val xs11 = xs ++ xs val ys11: View[Int]^{xs11} = xs11 val xs12 = xs ++ Nil val ys12: View[Int]^{xs12} = xs12 @@ -150,7 +149,7 @@ object Test { val ys13: List[Int] = xs13 val xs14 = xs ++ ("a" :: Nil) val ys14: View[Any]^{xs14} = xs14 - val xs15 = unsafeAssumeSeparate(xs.zip(xs9)) + val xs15 = xs.zip(xs9) val ys15: View[(Int, Boolean)]^{xs15} = xs15 println("-------") println(x1) diff --git a/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala b/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala index 54e5f7e2c6fd..5443758afa72 100644 --- a/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala +++ b/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala @@ -5,8 +5,6 @@ import Predef.{augmentString as _, wrapString as _, *} import scala.reflect.ClassTag import annotation.unchecked.{uncheckedVariance, uncheckedCaptures} import annotation.tailrec -import caps.cap -import caps.unsafe.unsafeAssumeSeparate /** A strawman architecture for new collections. It contains some * example collection classes and methods with the intent to expose @@ -31,7 +29,7 @@ object CollectionStrawMan5 { /** Base trait for instances that can construct a collection from an iterable */ trait FromIterable { type C[X] <: Iterable[X]^ - def fromIterable[B](it: Iterable[B]^{this, cap}): C[B]^{it} + def fromIterable[B](it: Iterable[B]^): C[B]^{it} } type FromIterableOf[+CC[X] <: Iterable[X]^] = FromIterable { @@ -62,16 +60,17 @@ object CollectionStrawMan5 { trait SeqFactory extends IterableFactory { type C[X] <: Seq[X] - def fromIterable[B](it: Iterable[B]^{this, cap}): C[B] + def fromIterable[B](it: Iterable[B]^): C[B] } /** Base trait for strict collections */ trait Buildable[+A] extends Iterable[A] { - protected def newBuilder: Builder[A, Repr] @uncheckedVariance - override def partition(p: A => Boolean): (Repr, Repr) = + protected[this] def newBuilder: Builder[A, Repr] @uncheckedVariance + override def partition(p: A => Boolean): (Repr, Repr) = { val l, r = newBuilder iterator.foreach(x => (if (p(x)) l else r) += x) (l.result, r.result) + } // one might also override other transforms here to avoid generating // iterators if it helps efficiency. } @@ -106,7 +105,7 @@ object CollectionStrawMan5 { with IterablePolyTransforms[A] with IterableMonoTransforms[A] { // sound bcs of VarianceNote type Repr = C[A] @uncheckedVariance - protected def fromLikeIterable(coll: Iterable[A] @uncheckedVariance ^ {this, cap}): Repr @uncheckedVariance ^{coll} = + protected[this] def fromLikeIterable(coll: Iterable[A] @uncheckedVariance ^): Repr @uncheckedVariance ^{coll} = fromIterable(coll) } @@ -116,7 +115,7 @@ object CollectionStrawMan5 { this: SeqLike[A] => type C[X] <: Seq[X] def fromIterable[B](coll: Iterable[B]^): C[B] - override protected def fromLikeIterable(coll: Iterable[A] @uncheckedVariance ^): Repr = + override protected[this] def fromLikeIterable(coll: Iterable[A] @uncheckedVariance ^): Repr = fromIterable(coll) trait IterableOps[+A] extends Any { @@ -135,7 +134,7 @@ object CollectionStrawMan5 { this: IterableMonoTransforms[A]^ => type Repr protected def coll: Iterable[A]^{this} - protected def fromLikeIterable(coll: Iterable[A] @uncheckedVariance ^ {this, cap}): Repr^{coll} + protected[this] def fromLikeIterable(coll: Iterable[A] @uncheckedVariance ^): Repr^{coll} def filter(p: A => Boolean): Repr^{this, p} = fromLikeIterable(View.Filter(coll, p)) def partition(p: A => Boolean): (Repr^{this, p}, Repr^{this, p}) = { @@ -154,7 +153,7 @@ object CollectionStrawMan5 { this: IterablePolyTransforms[A]^ => type C[A] protected def coll: Iterable[A]^{this} - def fromIterable[B](coll: Iterable[B]^{this, cap}): C[B]^{coll} + def fromIterable[B](coll: Iterable[B]^): C[B]^{coll} def map[B](f: A => B): C[B]^{this, f} = fromIterable(View.Map(coll, f)) def flatMap[B](f: A => IterableOnce[B]^): C[B]^{this, f} = fromIterable(View.FlatMap(coll, f)) def ++[B >: A](xs: IterableOnce[B]^): C[B]^{this, xs} = fromIterable(View.Concat(coll, xs)) @@ -170,7 +169,7 @@ object CollectionStrawMan5 { while (it.hasNext) xs = new Cons(it.next(), xs) fromLikeIterable(xs) - override protected def fromLikeIterable(coll: Iterable[A] @uncheckedVariance ^): Repr + override protected[this] def fromLikeIterable(coll: Iterable[A] @uncheckedVariance ^): Repr override def filter(p: A => Boolean): Repr = fromLikeIterable(View.Filter(coll, p)) @@ -205,7 +204,7 @@ object CollectionStrawMan5 { def head: A def tail: List[A] def iterator = new Iterator[A] { - private var current = self + private[this] var current = self def hasNext = !current.isEmpty def next() = { val r = current.head; current = current.tail; r } } @@ -216,7 +215,7 @@ object CollectionStrawMan5 { } def length: Int = if (isEmpty) 0 else 1 + tail.length - protected def newBuilder = new ListBuffer[A @uncheckedVariance @uncheckedCaptures] + protected[this] def newBuilder = new ListBuffer[A @uncheckedVariance @uncheckedCaptures] def ++:[B >: A](prefix: List[B]): List[B] = if (prefix.isEmpty) this else Cons(prefix.head, prefix.tail ++: this) @@ -408,7 +407,7 @@ object CollectionStrawMan5 { this: View[A]^ => type C[X] = View[X]^{this} override def view: this.type = this - override def fromIterable[B](c: Iterable[B]^{this, cap}): View[B]^{this, c} = { + override def fromIterable[B](c: Iterable[B]^): View[B]^{this, c} = { c match { case c: View[B] => c case _ => View.fromIterator(c.iterator) @@ -451,18 +450,6 @@ object CollectionStrawMan5 { this: Filter[A]^{underlying, p} => def iterator: Iterator[A]^{this} = underlying.iterator.filter(p) } - - object Filter: - def apply[A](underlying: Iterable[A]^, pp: A => Boolean, isFlipped: Boolean): Filter[A]^{underlying, pp} = - underlying match - case filter: Filter[A]^{underlying} => - unsafeAssumeSeparate: - // See filter-iterable.scala for a test where a variant of Filter - // works without the unsafeAssumeSeparate. But it requires significant - // changes compared to the version here. - new Filter(filter.underlying, a => filter.p(a) && pp(a)) - case _ => new Filter(underlying, pp) - case class Partition[A](val underlying: Iterable[A]^, p: A => Boolean) { self: Partition[A]^{underlying, p} => diff --git a/tests/run-custom-args/captures/colltest5/Test_2.scala b/tests/run-custom-args/captures/colltest5/Test_2.scala index 2bde8cb5a885..f6f47b536541 100644 --- a/tests/run-custom-args/captures/colltest5/Test_2.scala +++ b/tests/run-custom-args/captures/colltest5/Test_2.scala @@ -1,6 +1,5 @@ import Predef.{augmentString as _, wrapString as _, *} import scala.reflect.ClassTag -import caps.unsafe.unsafeAssumeSeparate object Test { import colltest5.strawman.collections.* @@ -90,7 +89,7 @@ object Test { val ys9: View[Boolean]^{xs9} = xs9 val xs10 = xs.flatMap(flips) val ys10: View[Int]^{xs10} = xs10 - val xs11 = unsafeAssumeSeparate(xs ++ xs) + val xs11 = xs ++ xs val ys11: View[Int]^{xs11} = xs11 val xs12 = xs ++ Nil val ys12: View[Int]^{xs12} = xs12 @@ -98,7 +97,7 @@ object Test { val ys13: List[Int] = xs13 val xs14 = xs ++ Cons("a", Nil) val ys14: View[Any]^{xs14} = xs14 - val xs15 = unsafeAssumeSeparate(xs.zip(xs9)) + val xs15 = xs.zip(xs9) val ys15: View[(Int, Boolean)]^{xs15} = xs15 println("-------") println(x1)