diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index 5c9946f6134a..8276a0987003 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -203,6 +203,23 @@ extension (tp: Type) case _ => false + /** Tests whether the type derives from `caps.Capability`, which means + * references of this type are maximal capabilities. + */ + def derivesFromCapability(using Context): Boolean = tp.dealias match + case tp: (TypeRef | AppliedType) => + val sym = tp.typeSymbol + if sym.isClass then sym.derivesFrom(defn.Caps_Capability) + else tp.superType.derivesFromCapability + case tp: TypeProxy => + tp.superType.derivesFromCapability + case tp: AndType => + tp.tp1.derivesFromCapability || tp.tp2.derivesFromCapability + case tp: OrType => + tp.tp1.derivesFromCapability && tp.tp2.derivesFromCapability + case _ => + false + /** Drop @retains annotations everywhere */ def dropAllRetains(using Context): Type = // TODO we should drop retains from inferred types before unpickling val tm = new TypeMap: @@ -408,7 +425,7 @@ extension (sym: Symbol) /** The owner of the current level. Qualifying owners are * - methods other than constructors and anonymous functions * - anonymous functions, provided they either define a local - * root of type caps.Cap, or they are the rhs of a val definition. + * root of type caps.Capability, or they are the rhs of a val definition. * - classes, if they are not staticOwners * - _root_ */ diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index d1a5a07f6a0f..f78ed1a91bd6 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -115,7 +115,7 @@ sealed abstract class CaptureSet extends Showable: * capture set. */ protected final def addNewElem(elem: CaptureRef)(using Context, VarState): CompareResult = - if elem.isRootCapability || summon[VarState] == FrozenState then + if elem.isMaxCapability || summon[VarState] == FrozenState then addThisElem(elem) else addThisElem(elem).orElse: @@ -147,17 +147,26 @@ sealed abstract class CaptureSet extends Showable: * this subsumes this.f * x subsumes y ==> x* subsumes y, x subsumes y? * x subsumes y ==> x* subsumes y*, x? subsumes y? + * x: x1.type /\ x1 subsumes y ==> x subsumes y */ extension (x: CaptureRef) private def subsumes(y: CaptureRef)(using Context): Boolean = (x eq y) || x.isRootCapability || y.match - case y: TermRef => y.prefix eq x + case y: TermRef => + (y.prefix eq x) + || y.info.match + case y1: CaptureRef => x.subsumes(y1) + case _ => false case MaybeCapability(y1) => x.stripMaybe.subsumes(y1) case _ => false || x.match case ReachCapability(x1) => x1.subsumes(y.stripReach) + case x: TermRef => + x.info match + case x1: CaptureRef => x1.subsumes(y) + case _ => false case _ => false /** {x} <:< this where <:< is subcapturing, but treating all variables @@ -167,11 +176,11 @@ sealed abstract class CaptureSet extends Showable: if comparer.isInstanceOf[ExplainingTypeComparer] then // !!! DEBUG reporting.trace.force(i"$this accountsFor $x, ${x.captureSetOfInfo}?", show = true): elems.exists(_.subsumes(x)) - || !x.isRootCapability && x.captureSetOfInfo.subCaptures(this, frozen = true).isOK + || !x.isMaxCapability && x.captureSetOfInfo.subCaptures(this, frozen = true).isOK else reporting.trace(i"$this accountsFor $x, ${x.captureSetOfInfo}?", show = true): elems.exists(_.subsumes(x)) - || !x.isRootCapability && x.captureSetOfInfo.subCaptures(this, frozen = true).isOK + || !x.isMaxCapability && x.captureSetOfInfo.subCaptures(this, frozen = true).isOK /** 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 @@ -183,7 +192,7 @@ sealed abstract class CaptureSet extends Showable: def mightAccountFor(x: CaptureRef)(using Context): Boolean = reporting.trace(i"$this mightAccountFor $x, ${x.captureSetOfInfo}?", show = true) { elems.exists(_.subsumes(x)) - || !x.isRootCapability + || !x.isMaxCapability && { val elems = x.captureSetOfInfo.elems !elems.isEmpty && elems.forall(mightAccountFor) @@ -383,7 +392,7 @@ object CaptureSet: def apply(elems: CaptureRef*)(using Context): CaptureSet.Const = if elems.isEmpty then empty - else Const(SimpleIdentitySet(elems.map(_.normalizedRef)*)) + else Const(SimpleIdentitySet(elems.map(_.normalizedRef.ensuring(_.isTrackableRef))*)) def apply(elems: Refs)(using Context): CaptureSet.Const = if elems.isEmpty then empty else Const(elems) @@ -491,6 +500,7 @@ object CaptureSet: CompareResult.LevelError(this, elem) else //if id == 34 then assert(!elem.isUniversalRootCapability) + assert(elem.isTrackableRef, elem) elems += elem if elem.isRootCapability then rootAddedHandler() @@ -1032,7 +1042,9 @@ object CaptureSet: /** The capture set of the type underlying CaptureRef */ def ofInfo(ref: CaptureRef)(using Context): CaptureSet = ref match - case ref: TermRef if ref.isRootCapability => ref.singletonCaptureSet + case ref: (TermRef | TermParamRef) if ref.isMaxCapability => + if ref.isTrackableRef then ref.singletonCaptureSet + else CaptureSet.universal case ReachCapability(ref1) => deepCaptureSet(ref1.widen) .showing(i"Deep capture set of $ref: ${ref1.widen} = $result", capt) case _ => ofType(ref.underlying, followResult = true) @@ -1046,7 +1058,8 @@ object CaptureSet: case tp: TermParamRef => tp.captureSet case tp: TypeRef => - if tp.typeSymbol == defn.Caps_Cap then universal else empty + if tp.derivesFromCapability then universal // TODO: maybe return another value that indicates that the underltinf ref is maximal? + else empty case _: TypeParamRef => empty case CapturingType(parent, refs) => @@ -1074,7 +1087,7 @@ object CaptureSet: case _ => empty recur(tp) - .showing(i"capture set of $tp = $result", captDebug) + //.showing(i"capture set of $tp = $result", captDebug) private def deepCaptureSet(tp: Type)(using Context): CaptureSet = val collect = new TypeAccumulator[CaptureSet]: diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index a5bb8792af2c..5daa56b9cc07 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -157,15 +157,17 @@ object CheckCaptures: case _ => case AnnotatedType(_, ann) if ann.symbol == defn.UncheckedCapturesAnnot => () - case t => + case CapturingType(parent, refs) => if variance >= 0 then - t.captureSet.disallowRootCapability: () => + refs.disallowRootCapability: () => def part = if t eq tp then "" else i"the part $t of " report.error( em"""$what cannot $have $tp since |${part}that type captures the root capability `cap`. |$addendum""", pos) + traverse(parent) + case t => traverseChildren(t) check.traverse(tp) end disallowRootCapabilitiesIn @@ -537,8 +539,8 @@ class CheckCaptures extends Recheck, SymTransformer: */ def addParamArgRefinements(core: Type, initCs: CaptureSet): (Type, CaptureSet) = var refined: Type = core - var allCaptures: CaptureSet = if setup.isCapabilityClassRef(core) - then CaptureSet.universal else initCs + var allCaptures: CaptureSet = + if core.derivesFromCapability then CaptureSet.universal else initCs for (getterName, argType) <- mt.paramNames.lazyZip(argTypes) do val getter = cls.info.member(getterName).suchThat(_.is(ParamAccessor)).symbol if getter.termRef.isTracked && !getter.is(Private) then @@ -572,8 +574,10 @@ class CheckCaptures extends Recheck, SymTransformer: val TypeApply(fn, args) = tree val polyType = atPhase(thisPhase.prev): fn.tpe.widen.asInstanceOf[TypeLambda] + def isExempt(sym: Symbol) = + sym.isTypeTestOrCast || sym == defn.Compiletime_erasedValue for case (arg: TypeTree, formal, pname) <- args.lazyZip(polyType.paramRefs).lazyZip((polyType.paramNames)) do - if !tree.symbol.isTypeTestOrCast then + if !isExempt(tree.symbol) then def where = if fn.symbol.exists then i" in an argument of ${fn.symbol}" else "" disallowRootCapabilitiesIn(arg.knownType, NoSymbol, i"Sealed type variable $pname", "be instantiated to", @@ -1004,7 +1008,7 @@ class CheckCaptures extends Recheck, SymTransformer: if (ares1 eq ares) && (aargs1 eq aargs) then actual else reconstruct(aargs1, ares1) - (resTp, curEnv.captured) + (resTp, CaptureSet(curEnv.captured.elems)) end adaptFun /** Adapt type function type `actual` to the expected type. @@ -1026,14 +1030,14 @@ class CheckCaptures extends Recheck, SymTransformer: if ares1 eq ares then actual else reconstruct(ares1) - (resTp, curEnv.captured) + (resTp, CaptureSet(curEnv.captured.elems)) end adaptTypeFun def adaptInfo(actual: Type, expected: Type, covariant: Boolean): String = val arrow = if covariant then "~~>" else "<~~" i"adapting $actual $arrow $expected" - def adapt(actual: Type, expected: Type, covariant: Boolean): Type = trace(adaptInfo(actual, expected, covariant), recheckr, show = true) { + def adapt(actual: Type, expected: Type, covariant: Boolean): Type = trace(adaptInfo(actual, expected, covariant), recheckr, show = true): if expected.isInstanceOf[WildcardType] then actual else // Decompose the actual type into the inner shape type, the capture set and the box status @@ -1113,7 +1117,22 @@ class CheckCaptures extends Recheck, SymTransformer: adaptedType(!boxed) else adaptedType(boxed) - } + end adapt + + /** If result derives from caps.Capability, yet is not a capturing type itself, + * make its capture set explicit. + */ + def makeCaptureSetExplicit(result: Type) = result match + case CapturingType(_, _) => result + case _ => + if result.derivesFromCapability then + val cap: CaptureRef = actual match + case ref: CaptureRef if ref.isTracked => + ref + case _ => + defn.captureRoot.termRef // TODO: skolemize? + CapturingType(result, cap.singletonCaptureSet) + else result if expected == LhsProto || expected.isSingleton && actual.isSingleton then actual @@ -1129,10 +1148,12 @@ class CheckCaptures extends Recheck, SymTransformer: case _ => case _ => val adapted = adapt(actualw.withReachCaptures(actual), expected, covariant = true) - if adapted ne actualw then - capt.println(i"adapt boxed $actual vs $expected ===> $adapted") - adapted - else actual + makeCaptureSetExplicit: + if adapted ne actualw then + capt.println(i"adapt boxed $actual vs $expected ===> $adapted") + adapted + else + actual end adaptBoxed /** Check overrides again, taking capture sets into account. @@ -1305,7 +1326,7 @@ class CheckCaptures extends Recheck, SymTransformer: case ref: TermParamRef if !allowed.contains(ref) && !seen.contains(ref) => seen += ref - if ref.underlying.isRef(defn.Caps_Cap) then + if ref.underlying.isRef(defn.Caps_Capability) then report.error(i"escaping local reference $ref", tree.srcPos) else val widened = ref.captureSetOfInfo diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index e6953dbf67b7..0175d40c186c 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -23,7 +23,6 @@ trait SetupAPI: def setupUnit(tree: Tree, recheckDef: DefRecheck)(using Context): Unit def isPreCC(sym: Symbol)(using Context): Boolean def postCheck()(using Context): Unit - def isCapabilityClassRef(tp: Type)(using Context): Boolean object Setup: @@ -58,8 +57,21 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: private val toBeUpdated = new mutable.HashSet[Symbol] private def newFlagsFor(symd: SymDenotation)(using Context): FlagSet = - if symd.isAllOf(PrivateParamAccessor) && symd.owner.is(CaptureChecked) && !symd.hasAnnotation(defn.ConstructorOnlyAnnot) - then symd.flags &~ Private | Recheck.ResetPrivate + + object containsCovarRetains extends TypeAccumulator[Boolean]: + def apply(x: Boolean, tp: Type): Boolean = + if x then true + else if tp.derivesFromCapability && variance >= 0 then true + else tp match + case AnnotatedType(_, ann) if ann.symbol.isRetains && variance >= 0 => true + case _ => foldOver(x, tp) + def apply(tp: Type): Boolean = apply(false, tp) + + if symd.isAllOf(PrivateParamAccessor) + && symd.owner.is(CaptureChecked) + && !symd.hasAnnotation(defn.ConstructorOnlyAnnot) + && containsCovarRetains(symd.symbol.originDenotation.info) + then symd.flags &~ Private else symd.flags def isPreCC(sym: Symbol)(using Context): Boolean = @@ -68,31 +80,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: && !sym.owner.is(CaptureChecked) && !defn.isFunctionSymbol(sym.owner) - private val capabilityClassMap = new util.HashMap[Symbol, Boolean] - - /** Check if the class is capability, which means: - * 1. the class has a capability annotation, - * 2. or at least one of its parent type has universal capability. - */ - def isCapabilityClassRef(tp: Type)(using Context): Boolean = tp.dealiasKeepAnnots match - case _: TypeRef | _: AppliedType => - val sym = tp.classSymbol - def checkSym: Boolean = - sym.hasAnnotation(defn.CapabilityAnnot) - || sym.info.parents.exists(hasUniversalCapability) - sym.isClass && capabilityClassMap.getOrElseUpdate(sym, checkSym) - case _ => false - - private def hasUniversalCapability(tp: Type)(using Context): Boolean = tp.dealiasKeepAnnots match - case CapturingType(parent, refs) => - refs.isUniversal || hasUniversalCapability(parent) - case AnnotatedType(parent, ann) => - if ann.symbol.isRetains then - try ann.tree.toCaptureSet.isUniversal || hasUniversalCapability(parent) - catch case ex: IllegalCaptureRef => false - else hasUniversalCapability(parent) - case tp => isCapabilityClassRef(tp) - private def fluidify(using Context) = new TypeMap with IdempotentCaptRefMap: def apply(t: Type): Type = t match case t: MethodType => @@ -196,7 +183,9 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: case cls: ClassSymbol if !defn.isFunctionClass(cls) && cls.is(CaptureChecked) => cls.paramGetters.foldLeft(tp) { (core, getter) => - if atPhase(thisPhase.next)(getter.termRef.isTracked) then + if atPhase(thisPhase.next)(getter.termRef.isTracked) + && !getter.is(Tracked) + then val getterType = mapInferred(refine = false)(tp.memberInfo(getter)).strippedDealias RefinedType(core, getter.name, @@ -317,10 +306,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: case t: TypeVar => this(t.underlying) case t => - // Map references to capability classes C to C^ - if isCapabilityClassRef(t) - then CapturingType(t, defn.expandedUniversalSet, boxed = false) - else recur(t) + recur(t) end expandAliases val tp1 = expandAliases(tp) // TODO: Do we still need to follow aliases? @@ -592,7 +578,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: if sym.isClass then !sym.isPureClass else - sym != defn.Caps_Cap && instanceCanBeImpure(tp.superType) + sym != defn.Caps_Capability && instanceCanBeImpure(tp.superType) case tp: (RefinedOrRecType | MatchType) => instanceCanBeImpure(tp.underlying) case tp: AndType => diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 11a4a8473e79..52535f26c692 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -991,7 +991,7 @@ class Definitions { @tu lazy val CapsModule: Symbol = requiredModule("scala.caps") @tu lazy val captureRoot: TermSymbol = CapsModule.requiredValue("cap") - @tu lazy val Caps_Cap: TypeSymbol = CapsModule.requiredType("Cap") + @tu lazy val Caps_Capability: TypeSymbol = CapsModule.requiredType("Capability") @tu lazy val Caps_reachCapability: TermSymbol = CapsModule.requiredMethod("reachCapability") @tu lazy val CapsUnsafeModule: Symbol = requiredModule("scala.caps.unsafe") @tu lazy val Caps_unsafeAssumePure: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumePure") @@ -1014,7 +1014,6 @@ class Definitions { @tu lazy val BeanPropertyAnnot: ClassSymbol = requiredClass("scala.beans.BeanProperty") @tu lazy val BooleanBeanPropertyAnnot: ClassSymbol = requiredClass("scala.beans.BooleanBeanProperty") @tu lazy val BodyAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Body") - @tu lazy val CapabilityAnnot: ClassSymbol = requiredClass("scala.annotation.capability") @tu lazy val ChildAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Child") @tu lazy val ContextResultCountAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ContextResultCount") @tu lazy val ProvisionalSuperClassAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ProvisionalSuperClass") @@ -2033,7 +2032,7 @@ class Definitions { */ @tu lazy val ccExperimental: Set[Symbol] = Set( CapsModule, CapsModule.moduleClass, PureClass, - CapabilityAnnot, RequiresCapabilityAnnot, + RequiresCapabilityAnnot, RetainsAnnot, RetainsCapAnnot, RetainsByNameAnnot) /** Experimental language features defined in `scala.runtime.stdLibPatches.language.experimental`. diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index c2c502a984c4..a9bb0406a14d 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -842,13 +842,27 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling val refs1 = tp1.captureSet try if refs1.isAlwaysEmpty then recur(tp1, parent2) - else subCaptures(refs1, refs2, frozenConstraint).isOK - && sameBoxed(tp1, tp2, refs1) - && (recur(tp1.widen.stripCapturing, parent2) - || tp1.isInstanceOf[SingletonType] && recur(tp1, parent2) - // this alternative is needed in case the right hand side is a - // capturing type that contains the lhs as an alternative of a union type. - ) + else + // The singletonOK branch is because we sometimes have a larger capture set in a singleton + // than in its underlying type. An example is `f: () -> () ->{x} T`, which might be + // the type of a closure. In that case the capture set of `f.type` is `{x}` but the + // capture set of the underlying type is `{}`. So without the `singletonOK` test, a singleton + // might not be a subtype of its underlying type. Examples where this arises is + // capt-capibility.scala and function-combinators.scala + val singletonOK = tp1 match + case tp1: SingletonType + if subCaptures(tp1.underlying.captureSet, refs2, frozen = true).isOK => + recur(tp1.widen, tp2) + case _ => + false + singletonOK + || subCaptures(refs1, refs2, frozenConstraint).isOK + && sameBoxed(tp1, tp2, refs1) + && (recur(tp1.widen.stripCapturing, parent2) + || tp1.isInstanceOf[SingletonType] && recur(tp1, parent2) + // this alternative is needed in case the right hand side is a + // capturing type that contains the lhs as an alternative of a union type. + ) catch case ex: AssertionError => println(i"assertion failed while compare captured $tp1 <:< $tp2") throw ex diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index eeffc41d4159..5be6774d0ff0 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2259,7 +2259,7 @@ object Types extends TypeUtils { * set of the underlying type is not always empty. */ final def isTracked(using Context): Boolean = - isTrackableRef && (isRootCapability || !captureSetOfInfo.isAlwaysEmpty) + isTrackableRef && (isMaxCapability || !captureSetOfInfo.isAlwaysEmpty) /** Is this a reach reference of the form `x*`? */ def isReach(using Context): Boolean = false // overridden in AnnotatedType @@ -2273,6 +2273,9 @@ object Types extends TypeUtils { /** Is this reference the generic root capability `cap` ? */ def isRootCapability(using Context): Boolean = false + /** Is this reference capability that does not derive from another capability ? */ + def isMaxCapability(using Context): Boolean = false + /** Normalize reference so that it can be compared with `eq` for equality */ def normalizedRef(using Context): CaptureRef = this @@ -3010,6 +3013,10 @@ object Types extends TypeUtils { override def isRootCapability(using Context): Boolean = name == nme.CAPTURE_ROOT && symbol == defn.captureRoot + override def isMaxCapability(using Context): Boolean = + import cc.* + this.derivesFromCapability && symbol.isStableMember + override def normalizedRef(using Context): CaptureRef = if isTrackableRef then symbol.termRef else this } @@ -4809,6 +4816,9 @@ object Types extends TypeUtils { def kindString: String = "Term" def copyBoundType(bt: BT): Type = bt.paramRefs(paramNum) override def isTrackableRef(using Context) = true + override def isMaxCapability(using Context) = + import cc.* + this.derivesFromCapability } private final class TermParamRefImpl(binder: TermLambda, paramNum: Int) extends TermParamRef(binder, paramNum) @@ -6155,8 +6165,15 @@ object Types extends TypeUtils { def inverse: BiTypeMap /** A restriction of this map to a function on tracked CaptureRefs */ - def forward(ref: CaptureRef): CaptureRef = this(ref) match - case result: CaptureRef if result.isTrackableRef => result + def forward(ref: CaptureRef): CaptureRef = + val result = this(ref) + def ensureTrackable(tp: Type): CaptureRef = tp match + case tp: CaptureRef => + if tp.isTrackableRef then tp + else ensureTrackable(tp.underlying) + case _ => + assert(false, i"not a trackable captureRef ref: $result, ${result.underlyingIterator.toList}") + ensureTrackable(result) /** A restriction of the inverse to a function on tracked CaptureRefs */ def backward(ref: CaptureRef): CaptureRef = inverse(ref) match diff --git a/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala b/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala index 4020291dded0..6529eed77fa0 100644 --- a/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala +++ b/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala @@ -34,7 +34,9 @@ object OverridingPairs: */ protected def exclude(sym: Symbol): Boolean = !sym.memberCanMatchInheritedSymbols - || isCaptureChecking && sym.is(Recheck.ResetPrivate) + || isCaptureChecking && atPhase(ctx.phase.prev)(sym.is(Private)) + // for capture checking we drop the private flag of certain parameter accessors + // but these still need no overriding checks /** The parents of base that are checked when deciding whether an overriding * pair has already been treated in a parent class. diff --git a/compiler/src/dotty/tools/dotc/transform/Recheck.scala b/compiler/src/dotty/tools/dotc/transform/Recheck.scala index f809fbd176ce..79dfe3393578 100644 --- a/compiler/src/dotty/tools/dotc/transform/Recheck.scala +++ b/compiler/src/dotty/tools/dotc/transform/Recheck.scala @@ -27,14 +27,6 @@ import annotation.tailrec object Recheck: import tpd.* - /** A flag used to indicate that a ParamAccessor has been temporarily made not-private - * Only used at the start of the Recheck phase, reset at its end. - * The flag repurposes the Scala2ModuleVar flag. No confusion is possible since - * Scala2ModuleVar cannot be also ParamAccessors. - */ - val ResetPrivate = Scala2ModuleVar - val ResetPrivateParamAccessor = ResetPrivate | ParamAccessor - /** Attachment key for rechecked types of TypeTrees */ val RecheckedType = Property.Key[Type] diff --git a/library/src/scala/CanThrow.scala b/library/src/scala/CanThrow.scala index c7f23a393715..91c94229c43c 100644 --- a/library/src/scala/CanThrow.scala +++ b/library/src/scala/CanThrow.scala @@ -6,9 +6,9 @@ import annotation.{implicitNotFound, experimental, capability} * experimental.saferExceptions feature, a `throw Ex()` expression will require * a given of class `CanThrow[Ex]` to be available. */ -@experimental @capability +@experimental @implicitNotFound("The capability to throw exception ${E} is missing.\nThe capability can be provided by one of the following:\n - Adding a using clause `(using CanThrow[${E}])` to the definition of the enclosing method\n - Adding `throws ${E}` clause after the result type of the enclosing method\n - Wrapping this piece of code with a `try` block that catches ${E}") -erased class CanThrow[-E <: Exception] +erased class CanThrow[-E <: Exception] extends caps.Capability @experimental object unsafeExceptions: diff --git a/library/src/scala/annotation/capability.scala b/library/src/scala/annotation/capability.scala index 4696ed6a015e..d3453e3c8168 100644 --- a/library/src/scala/annotation/capability.scala +++ b/library/src/scala/annotation/capability.scala @@ -11,4 +11,6 @@ import annotation.experimental * THere, the capture set of any instance of `CanThrow` is assumed to be * `{*}`. */ -@experimental final class capability extends StaticAnnotation +@experimental +@deprecated("To make a class a capability, let it derive from the `Capability` trait instead") +final class capability extends StaticAnnotation diff --git a/library/src/scala/caps.scala b/library/src/scala/caps.scala index c7fc8e7ba584..808bdba34e3f 100644 --- a/library/src/scala/caps.scala +++ b/library/src/scala/caps.scala @@ -4,16 +4,17 @@ import annotation.experimental @experimental object caps: - class Cap // should be @erased + trait Capability extends Any + + /** The universal capture reference */ + val cap: Capability = new Capability() {} /** The universal capture reference (deprecated) */ @deprecated("Use `cap` instead") - val `*`: Cap = cap - - /** The universal capture reference */ - val cap: Cap = Cap() + val `*`: Capability = cap - given Cap = cap + @deprecated("Use `Capability` instead") + type Cap = Capability /** Reach capabilities x* which appear as terms in @retains annotations are encoded * as `caps.reachCapability(x)`. When converted to CaptureRef types in capture sets diff --git a/tests/disabled/pos/lazylist.scala b/tests/disabled/pos/lazylist.scala index c24f8677b91f..e56eb484894c 100644 --- a/tests/disabled/pos/lazylist.scala +++ b/tests/disabled/pos/lazylist.scala @@ -34,7 +34,7 @@ object LazyNil extends LazyList[Nothing]: def map[A, B](xs: {*} LazyList[A], f: {*} A => B): {f, xs} LazyList[B] = xs.map(f) -@annotation.capability class Cap +class Cap extends caps.Capability def test(cap1: Cap, cap2: Cap, cap3: Cap) = def f[T](x: LazyList[T]): LazyList[T] = if cap1 == cap1 then x else LazyNil diff --git a/tests/neg-custom-args/captures/box-unsoundness.scala b/tests/neg-custom-args/captures/box-unsoundness.scala index d1331f16df1f..8c1c22bc7fa6 100644 --- a/tests/neg-custom-args/captures/box-unsoundness.scala +++ b/tests/neg-custom-args/captures/box-unsoundness.scala @@ -1,4 +1,3 @@ -//@annotation.capability class CanIO { def use(): Unit = () } def use[X](x: X): (op: X -> Unit) -> Unit = op => op(x) def test(io: CanIO^): Unit = diff --git a/tests/neg-custom-args/captures/byname.check b/tests/neg-custom-args/captures/byname.check index e06a3a1f8268..b9e5c81b721d 100644 --- a/tests/neg-custom-args/captures/byname.check +++ b/tests/neg-custom-args/captures/byname.check @@ -1,18 +1,18 @@ -- 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 -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/byname.scala:4:2 ----------------------------------------- 4 | def f() = if cap1 == cap1 then g else g // error | ^ - | Found: (x$0: Int) ->{cap2} Int - | Required: (x$0: Int) -> Int + | Found: ((x$0: Int) ->{cap2} Int)^{} + | Required: Int -> Int | | Note that the expected type Int ->{} Int | is the previously inferred result type of method test | which is also the type seen in separately compiled sources. - | The new inferred type (x$0: Int) ->{cap2} Int + | The new inferred type ((x$0: Int) ->{cap2} Int)^{} | must conform to this type. 5 | def g(x: Int) = if cap2 == cap2 then 1 else x 6 | def g2(x: Int) = if cap1 == cap1 then 1 else x diff --git a/tests/neg-custom-args/captures/byname.scala b/tests/neg-custom-args/captures/byname.scala index 279122f54735..0ed3a09cb414 100644 --- a/tests/neg-custom-args/captures/byname.scala +++ b/tests/neg-custom-args/captures/byname.scala @@ -1,4 +1,4 @@ -@annotation.capability class Cap +class Cap extends caps.Capability def test(cap1: Cap, cap2: Cap) = def f() = if cap1 == cap1 then g else g // error diff --git a/tests/neg-custom-args/captures/capt-box-env.scala b/tests/neg-custom-args/captures/capt-box-env.scala index 605b446d5262..bfe1874d073b 100644 --- a/tests/neg-custom-args/captures/capt-box-env.scala +++ b/tests/neg-custom-args/captures/capt-box-env.scala @@ -1,4 +1,4 @@ -@annotation.capability class Cap +class Cap extends caps.Capability class Pair[+A, +B](x: A, y: B): def fst: A = x diff --git a/tests/neg-custom-args/captures/capt-box.scala b/tests/neg-custom-args/captures/capt-box.scala index 634470704fc5..291882bed36d 100644 --- a/tests/neg-custom-args/captures/capt-box.scala +++ b/tests/neg-custom-args/captures/capt-box.scala @@ -1,4 +1,4 @@ -@annotation.capability class Cap +class Cap extends caps.Capability def test(x: Cap) = diff --git a/tests/neg-custom-args/captures/capt-wf2.scala b/tests/neg-custom-args/captures/capt-wf2.scala index 6c65e0dc77f7..8bb04a230fdd 100644 --- a/tests/neg-custom-args/captures/capt-wf2.scala +++ b/tests/neg-custom-args/captures/capt-wf2.scala @@ -1,4 +1,4 @@ -@annotation.capability class C +class C extends caps.Capability def test(c: C) = var x: Any^{c} = ??? diff --git a/tests/neg-custom-args/captures/capt1.check b/tests/neg-custom-args/captures/capt1.check index 74b9db728983..0e99d1876d3c 100644 --- a/tests/neg-custom-args/captures/capt1.check +++ b/tests/neg-custom-args/captures/capt1.check @@ -49,6 +49,6 @@ 34 | val z3 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | Sealed type variable X cannot be instantiated to box () ->{x} Cap since - | the part C^ of that type captures the root capability `cap`. + | the part Cap of that type captures the root capability `cap`. | This is often caused by a local capability in an argument of method h | leaking as part of its result. diff --git a/tests/neg-custom-args/captures/caseclass/Test_2.scala b/tests/neg-custom-args/captures/caseclass/Test_2.scala index bffc0a295bdc..9d97d5537c72 100644 --- a/tests/neg-custom-args/captures/caseclass/Test_2.scala +++ b/tests/neg-custom-args/captures/caseclass/Test_2.scala @@ -1,4 +1,4 @@ -@annotation.capability class C +class C extends caps.Capability def test(c: C) = val pure: () -> Unit = () => () val impure: () => Unit = pure diff --git a/tests/neg-custom-args/captures/cc-this.scala b/tests/neg-custom-args/captures/cc-this.scala index 4c05be702c51..e4336ed457af 100644 --- a/tests/neg-custom-args/captures/cc-this.scala +++ b/tests/neg-custom-args/captures/cc-this.scala @@ -1,4 +1,4 @@ -@annotation.capability class Cap +class Cap extends caps.Capability def eff(using Cap): Unit = () diff --git a/tests/neg-custom-args/captures/cc-this2.check b/tests/neg-custom-args/captures/cc-this2.check index bd9a1085d262..6cb3010d6174 100644 --- a/tests/neg-custom-args/captures/cc-this2.check +++ b/tests/neg-custom-args/captures/cc-this2.check @@ -2,7 +2,7 @@ -- Error: tests/neg-custom-args/captures/cc-this2/D_2.scala:3:8 -------------------------------------------------------- 3 | this: D^ => // error | ^^ - |reference (caps.cap : caps.Cap) captured by this self type is not included in the allowed capture set {} of pure base class class C + |reference (caps.cap : caps.Capability) captured by this self type is not included in the allowed capture set {} of pure base class class C -- [E058] Type Mismatch Error: tests/neg-custom-args/captures/cc-this2/D_2.scala:2:6 ----------------------------------- 2 |class D extends C: // error | ^ diff --git a/tests/neg-custom-args/captures/cc-this3.scala b/tests/neg-custom-args/captures/cc-this3.scala index 25af19dd6c4a..0a36cde8173b 100644 --- a/tests/neg-custom-args/captures/cc-this3.scala +++ b/tests/neg-custom-args/captures/cc-this3.scala @@ -1,4 +1,4 @@ -@annotation.capability class Cap +class Cap extends caps.Capability def eff(using Cap): Unit = () diff --git a/tests/neg-custom-args/captures/cc-this5.check b/tests/neg-custom-args/captures/cc-this5.check index 8affe7005e2e..1329734ce37d 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 | ^ - | (c : Cap^) cannot be referenced here; it is not included in the allowed capture set {} + | (c : Cap) cannot be referenced here; it 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/cc-this5.scala b/tests/neg-custom-args/captures/cc-this5.scala index e84c2a41f55c..4c9a8a706670 100644 --- a/tests/neg-custom-args/captures/cc-this5.scala +++ b/tests/neg-custom-args/captures/cc-this5.scala @@ -1,7 +1,7 @@ class C: val x: C = this -@annotation.capability class Cap +class Cap extends caps.Capability def foo(c: Cap) = object D extends C: // error @@ -17,5 +17,5 @@ def test(c: Cap) = def test2(c: Cap) = class A: - def f = println(c) + def f = println(c) val x: A = this // error diff --git a/tests/neg-custom-args/captures/class-constr.scala b/tests/neg-custom-args/captures/class-constr.scala index 9afb6972ccfa..619fa9fa0341 100644 --- a/tests/neg-custom-args/captures/class-constr.scala +++ b/tests/neg-custom-args/captures/class-constr.scala @@ -1,6 +1,6 @@ import annotation.{capability, constructorOnly} -@capability class Cap +class Cap extends caps.Capability class C(x: Cap, @constructorOnly y: Cap) diff --git a/tests/neg-custom-args/captures/effect-swaps-explicit.check b/tests/neg-custom-args/captures/effect-swaps-explicit.check new file mode 100644 index 000000000000..8c4d1f315fd8 --- /dev/null +++ b/tests/neg-custom-args/captures/effect-swaps-explicit.check @@ -0,0 +1,29 @@ +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps-explicit.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] +63 | fr.await.ok + |-------------------------------------------------------------------------------------------------------------------- + |Inline stack trace + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from effect-swaps-explicit.scala:39 +39 | boundary(Ok(body)) + | ^^^^^^^^ + -------------------------------------------------------------------------------------------------------------------- + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps-explicit.scala:72:10 ------------------------ +72 | Future: fut ?=> // error: type mismatch + | ^ + | Found: Future[box T^?]^{fr, lbl} + | Required: Future[box T^?]^? +73 | fr.await.ok + | + | longer explanation available when compiling with `-explain` +-- Error: tests/neg-custom-args/captures/effect-swaps-explicit.scala:66:15 --------------------------------------------- +66 | Result.make: //lbl ?=> // error, escaping label from Result + | ^^^^^^^^^^^ + |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-explicit.scala b/tests/neg-custom-args/captures/effect-swaps-explicit.scala new file mode 100644 index 000000000000..052beaab01b2 --- /dev/null +++ b/tests/neg-custom-args/captures/effect-swaps-explicit.scala @@ -0,0 +1,74 @@ +object boundary: + + final class Label[-T] // extends caps.Capability + + /** Abort current computation and instead return `value` as the value of + * the enclosing `boundary` call that created `label`. + */ + def break[T](value: T)(using label: Label[T]^): Nothing = ??? + + def apply[T](body: Label[T]^ ?=> T): T = ??? +end boundary + +import boundary.{Label, break} + +trait Async extends caps.Capability +object Async: + def blocking[T](body: Async ?=> T): T = ??? + +class Future[+T]: + this: Future[T]^ => + def await(using Async): T = ??? +object Future: + def apply[T](op: Async ?=> T)(using Async): Future[T]^{op} = ??? + +enum Result[+T, +E]: + case Ok[+T](value: T) extends Result[T, Nothing] + case Err[+E](error: E) extends Result[Nothing, E] + + +object Result: + extension [T, E](r: Result[T, E]^)(using Label[Err[E]]^) + + /** `_.ok` propagates Err to current Label */ + def ok: T = r match + case Ok(value) => value + case Err(value) => break[Err[E]](Err(value)) + + transparent inline def apply[T, E](inline body: Label[Result[T, E]]^ ?=> T): Result[T, E] = + boundary(Ok(body)) + + // same as apply, but not an inline method + def make[T, E](body: Label[Result[T, E]]^ ?=> T): Result[T, E] = + boundary(Ok(body)) + +end Result + +def test[T, E](using Async) = + import Result.* + Async.blocking: async ?=> + val good1: List[Future[Result[T, E]]] => Future[Result[List[T], E]] = frs => + Future: + Result: + frs.map(_.await.ok) // OK + + val good2: Result[Future[T], E] => Future[Result[T, E]] = rf => + Future: + Result: + rf.ok.await // OK, Future argument has type Result[T] + + def fail3(fr: Future[Result[T, E]]^) = + Result: + Future: // error, type mismatch + fr.await.ok + + def fail4[T, E](fr: Future[Result[T, E]]^) = + Result.make: //lbl ?=> // error, escaping label from Result + Future: fut ?=> + fr.await.ok + + def fail5[T, E](fr: Future[Result[T, E]]^) = + Result.make[Future[T], E]: lbl ?=> + Future: fut ?=> // error: type mismatch + fr.await.ok + diff --git a/tests/neg-custom-args/captures/effect-swaps.check b/tests/neg-custom-args/captures/effect-swaps.check index bda3509645d1..ef5a95d333bf 100644 --- a/tests/neg-custom-args/captures/effect-swaps.check +++ b/tests/neg-custom-args/captures/effect-swaps.check @@ -1,21 +1,24 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:64:8 ---------------------------------- -63 | Result: -64 | Future: // error, escaping label from Result +-- [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` --- Error: tests/neg-custom-args/captures/effect-swaps.scala:68:15 ------------------------------------------------------ -68 | Result.make: //lbl ?=> // error, escaping label from Result - | ^^^^^^^^^^^ - |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 +-- [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^?]^? +73 | fr.await.ok + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/effect-swaps.scala b/tests/neg-custom-args/captures/effect-swaps.scala index 1d72077bb8da..d4eed2bae2f2 100644 --- a/tests/neg-custom-args/captures/effect-swaps.scala +++ b/tests/neg-custom-args/captures/effect-swaps.scala @@ -1,8 +1,6 @@ -import annotation.capability - object boundary: - @capability final class Label[-T] + final class Label[-T] extends caps.Capability /** Abort current computation and instead return `value` as the value of * the enclosing `boundary` call that created `label`. @@ -14,7 +12,7 @@ end boundary import boundary.{Label, break} -@capability trait Async +trait Async extends caps.Capability object Async: def blocking[T](body: Async ?=> T): T = ??? @@ -61,10 +59,16 @@ def test[T, E](using Async) = def fail3(fr: Future[Result[T, E]]^) = Result: - Future: // error, escaping label from Result + Future: // error, type mismatch fr.await.ok def fail4[T, E](fr: Future[Result[T, E]]^) = - Result.make: //lbl ?=> // error, escaping label from Result + Result.make: //lbl ?=> // should be error, escaping label from Result but infers Result[Any, Any] Future: fut ?=> fr.await.ok + + def fail5[T, E](fr: Future[Result[T, E]]^) = + Result.make[Future[T], E]: lbl ?=> + Future: fut ?=> // error: type mismatch + fr.await.ok + diff --git a/tests/neg-custom-args/captures/eta.check b/tests/neg-custom-args/captures/eta.check index 91dfdf06d3cd..9850e54a7fdf 100644 --- a/tests/neg-custom-args/captures/eta.check +++ b/tests/neg-custom-args/captures/eta.check @@ -1,7 +1,7 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/eta.scala:4:9 -------------------------------------------- 4 | g // error | ^ - | Found: () ->? A + | Found: (g : () -> A) | Required: () -> Proc^{f} | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/exception-definitions.check b/tests/neg-custom-args/captures/exception-definitions.check index 72b88f252e59..7f915ebd9833 100644 --- a/tests/neg-custom-args/captures/exception-definitions.check +++ b/tests/neg-custom-args/captures/exception-definitions.check @@ -1,7 +1,7 @@ -- Error: tests/neg-custom-args/captures/exception-definitions.scala:3:8 ----------------------------------------------- 3 | self: Err^ => // error | ^^^^ - |reference (caps.cap : caps.Cap) captured by this self type is not included in the allowed capture set {} of pure base class class Throwable + |reference (caps.cap : caps.Capability) captured by this self type is not included in the allowed capture set {} of pure base class class Throwable -- Error: tests/neg-custom-args/captures/exception-definitions.scala:7:12 ---------------------------------------------- 7 | val x = c // error | ^ diff --git a/tests/neg-custom-args/captures/extending-cap-classes.check b/tests/neg-custom-args/captures/extending-cap-classes.check new file mode 100644 index 000000000000..3bdddfd9dd3c --- /dev/null +++ b/tests/neg-custom-args/captures/extending-cap-classes.check @@ -0,0 +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^ + | 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^ + | 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)^{y2} + | Required: C1 + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/extending-cap-classes.scala b/tests/neg-custom-args/captures/extending-cap-classes.scala index 17497e415a1e..6f5a8f48c30a 100644 --- a/tests/neg-custom-args/captures/extending-cap-classes.scala +++ b/tests/neg-custom-args/captures/extending-cap-classes.scala @@ -1,7 +1,5 @@ -import annotation.capability - class C1 -@capability class C2 extends C1 +class C2 extends C1, caps.Capability class C3 extends C2 def test = @@ -9,7 +7,8 @@ def test = val x2: C1 = new C2 // error val x3: C1 = new C3 // error - val y1: C2 = new C2 - val y2: C2 = new C3 - - val z1: C3 = new C3 \ No newline at end of file + val y2: C2 = new C2 + val y3: C3 = new C3 + + val z2: C1 = y2 // error + diff --git a/tests/neg-custom-args/captures/filevar.scala b/tests/neg-custom-args/captures/filevar.scala index 59b8415d6e0f..0d9cbed164e3 100644 --- a/tests/neg-custom-args/captures/filevar.scala +++ b/tests/neg-custom-args/captures/filevar.scala @@ -8,8 +8,8 @@ class Service: var file: File^ = uninitialized // error def log = file.write("log") -def withFile[T](op: (l: caps.Cap) ?-> (f: File^{l}) => T): T = - op(new File) +def withFile[T](op: (l: caps.Capability) ?-> (f: File^{l}) => T): T = + op(using caps.cap)(new File) def test = withFile: f => diff --git a/tests/neg-custom-args/captures/i15772.check b/tests/neg-custom-args/captures/i15772.check index cce58da1b93b..0f8f0bf6eac5 100644 --- a/tests/neg-custom-args/captures/i15772.check +++ b/tests/neg-custom-args/captures/i15772.check @@ -35,7 +35,7 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:44:2 ---------------------------------------- 44 | x: (() -> Unit) // error | ^ - | Found: () ->{x} Unit + | Found: (x : () ->{filesList, sayHello} Unit) | Required: () -> Unit | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i15923.scala b/tests/neg-custom-args/captures/i15923.scala index 754fd0687037..e71f01996938 100644 --- a/tests/neg-custom-args/captures/i15923.scala +++ b/tests/neg-custom-args/captures/i15923.scala @@ -3,9 +3,9 @@ type Id[X] = [T] -> (op: X => T) -> T def mkId[X](x: X): Id[X] = [T] => (op: X => T) => op(x) def bar() = { - def withCap[X](op: (lcap: caps.Cap) ?-> Cap^{lcap} => X): X = { + def withCap[X](op: (lcap: caps.Capability) ?-> Cap^{lcap} => X): X = { val cap: Cap = new Cap { def use() = { println("cap is used"); 0 } } - val result = op(cap) + val result = op(using caps.cap)(cap) result } diff --git a/tests/neg-custom-args/captures/i16725.scala b/tests/neg-custom-args/captures/i16725.scala index ff06b3be78a7..733c2c562bbc 100644 --- a/tests/neg-custom-args/captures/i16725.scala +++ b/tests/neg-custom-args/captures/i16725.scala @@ -1,6 +1,5 @@ import language.experimental.captureChecking -@annotation.capability -class IO: +class IO extends caps.Capability: def brewCoffee(): Unit = ??? def usingIO[T](op: IO => T): T = ??? @@ -8,8 +7,8 @@ type Wrapper[T] = [R] -> (f: T => R) -> R def mk[T](x: T): Wrapper[T] = [R] => f => f(x) def useWrappedIO(wrapper: Wrapper[IO]): () -> Unit = () => - wrapper: io => // error + wrapper: io => io.brewCoffee() def main(): Unit = - val escaped = usingIO(io => useWrappedIO(mk(io))) + val escaped = usingIO(io => useWrappedIO(mk(io))) // error escaped() // boom diff --git a/tests/neg-custom-args/captures/inner-classes.scala b/tests/neg-custom-args/captures/inner-classes.scala index 181b830e4996..fd500e607970 100644 --- a/tests/neg-custom-args/captures/inner-classes.scala +++ b/tests/neg-custom-args/captures/inner-classes.scala @@ -1,6 +1,6 @@ object test: - @annotation.capability class FileSystem + class FileSystem extends caps.Capability def foo(fs: FileSystem) = diff --git a/tests/neg-custom-args/captures/outer-var.check b/tests/neg-custom-args/captures/outer-var.check index c250280961d9..b9f1f57be769 100644 --- a/tests/neg-custom-args/captures/outer-var.check +++ b/tests/neg-custom-args/captures/outer-var.check @@ -1,7 +1,7 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/outer-var.scala:11:8 ------------------------------------- 11 | x = q // error | ^ - | Found: () ->{q} Unit + | Found: (q : Proc) | Required: () ->{p, q²} Unit | | where: q is a parameter in method inner @@ -28,7 +28,7 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/outer-var.scala:14:8 ------------------------------------- 14 | y = q // error | ^ - | Found: () ->{q} Unit + | Found: (q : Proc) | Required: () ->{p} Unit | | Note that reference (q : Proc), defined in method inner diff --git a/tests/neg-custom-args/captures/stack-alloc.scala b/tests/neg-custom-args/captures/stack-alloc.scala index befafbf13003..80e7e4169720 100644 --- a/tests/neg-custom-args/captures/stack-alloc.scala +++ b/tests/neg-custom-args/captures/stack-alloc.scala @@ -5,11 +5,11 @@ class Pooled val stack = mutable.ArrayBuffer[Pooled]() var nextFree = 0 -def withFreshPooled[T](op: (lcap: caps.Cap) ?-> Pooled^{lcap} => T): T = +def withFreshPooled[T](op: (lcap: caps.Capability) ?-> Pooled^{lcap} => T): T = if nextFree >= stack.size then stack.append(new Pooled) val pooled = stack(nextFree) nextFree = nextFree + 1 - val ret = op(pooled) + val ret = op(using caps.cap)(pooled) nextFree = nextFree - 1 ret diff --git a/tests/neg-custom-args/captures/try3.scala b/tests/neg-custom-args/captures/try3.scala index 004cda6a399c..880d20ef16a0 100644 --- a/tests/neg-custom-args/captures/try3.scala +++ b/tests/neg-custom-args/captures/try3.scala @@ -4,7 +4,7 @@ class CT[E] type CanThrow[E] = CT[E]^ type Top = Any^ -def handle[E <: Exception, T <: Top](op: (lcap: caps.Cap) ?-> CT[E]^{lcap} ?=> T)(handler: E => T): T = +def handle[E <: Exception, T <: Top](op: (lcap: caps.Capability) ?-> CT[E]^{lcap} ?=> T)(handler: E => T): T = val x: CT[E] = ??? try op(using caps.cap)(using x) catch case ex: E => handler(ex) diff --git a/tests/neg-custom-args/captures/usingLogFile.check b/tests/neg-custom-args/captures/usingLogFile.check index bf5c1dc4f83a..068d8be78c70 100644 --- a/tests/neg-custom-args/captures/usingLogFile.check +++ b/tests/neg-custom-args/captures/usingLogFile.check @@ -1,12 +1,12 @@ --- Error: tests/neg-custom-args/captures/usingLogFile.scala:23:14 ------------------------------------------------------ -23 | val later = usingLogFile { f => () => f.write(0) } // error +-- Error: tests/neg-custom-args/captures/usingLogFile.scala:22:14 ------------------------------------------------------ +22 | val later = usingLogFile { f => () => f.write(0) } // error | ^^^^^^^^^^^^ | local reference f leaks into outer capture set of type parameter T of method usingLogFile in object Test2 --- Error: tests/neg-custom-args/captures/usingLogFile.scala:28:23 ------------------------------------------------------ -28 | private val later2 = usingLogFile { f => Cell(() => f.write(0)) } // error +-- Error: tests/neg-custom-args/captures/usingLogFile.scala:27:23 ------------------------------------------------------ +27 | private val later2 = usingLogFile { f => Cell(() => f.write(0)) } // error | ^^^^^^^^^^^^ | local reference f leaks into outer capture set of type parameter T of method usingLogFile in object Test2 --- Error: tests/neg-custom-args/captures/usingLogFile.scala:44:16 ------------------------------------------------------ -44 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error +-- Error: tests/neg-custom-args/captures/usingLogFile.scala:43:16 ------------------------------------------------------ +43 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error | ^^^^^^^^^ | local reference f leaks into outer capture set of type parameter T of method usingFile in object Test3 diff --git a/tests/neg-custom-args/captures/usingLogFile.scala b/tests/neg-custom-args/captures/usingLogFile.scala index 67e6f841e7ce..2b46a5401f46 100644 --- a/tests/neg-custom-args/captures/usingLogFile.scala +++ b/tests/neg-custom-args/captures/usingLogFile.scala @@ -1,11 +1,10 @@ import java.io.* -import annotation.capability object Test1: - def usingLogFile[T](op: (local: caps.Cap) ?-> FileOutputStream => T): T = + def usingLogFile[T](op: (local: caps.Capability) ?-> FileOutputStream => T): T = val logFile = FileOutputStream("log") - val result = op(logFile) + val result = op(using caps.cap)(logFile) logFile.close() result diff --git a/tests/neg/unsound-reach.check b/tests/neg/unsound-reach.check index fd5c401416d1..8cabbe1571a0 100644 --- a/tests/neg/unsound-reach.check +++ b/tests/neg/unsound-reach.check @@ -1,5 +1,5 @@ --- Error: tests/neg/unsound-reach.scala:18:9 --------------------------------------------------------------------------- -18 | boom.use(f): (f1: File^{backdoor*}) => // error - | ^^^^^^^^ - | Reach capability backdoor* and universal capability cap cannot both - | appear in the type (x: File^)(op: box File^{backdoor*} => Unit): Unit of this expression +-- Error: tests/neg/unsound-reach.scala:18:13 -------------------------------------------------------------------------- +18 | boom.use(f): (f1: File^{backdoor*}) => // error + | ^^^^^^^^ + | Reach capability backdoor* and universal capability cap cannot both + | appear in the type (x: File^)(op: box File^{backdoor*} => Unit): Unit of this expression diff --git a/tests/neg/unsound-reach.scala b/tests/neg/unsound-reach.scala index 468730168019..48a74f86d311 100644 --- a/tests/neg/unsound-reach.scala +++ b/tests/neg/unsound-reach.scala @@ -5,16 +5,16 @@ trait File: def withFile[R](path: String)(op: File^ => R): R = ??? trait Foo[+X]: - def use(x: File^)(op: X => Unit): Unit + def use(x: File^)(op: X => Unit): Unit class Bar extends Foo[File^]: - def use(x: File^)(op: File^ => Unit): Unit = op(x) + def use(x: File^)(op: File^ => Unit): Unit = op(x) def bad(): Unit = - val backdoor: Foo[File^] = new Bar - val boom: Foo[File^{backdoor*}] = backdoor + val backdoor: Foo[File^] = new Bar + val boom: Foo[File^{backdoor*}] = backdoor - var escaped: File^{backdoor*} = null - withFile("hello.txt"): f => - boom.use(f): (f1: File^{backdoor*}) => // error - escaped = f1 + var escaped: File^{backdoor*} = null + withFile("hello.txt"): f => + boom.use(f): (f1: File^{backdoor*}) => // error + escaped = f1 diff --git a/tests/pos-custom-args/captures/boxed1.scala b/tests/pos-custom-args/captures/boxed1.scala index 8c6b63ef0134..e2ff69c305d2 100644 --- a/tests/pos-custom-args/captures/boxed1.scala +++ b/tests/pos-custom-args/captures/boxed1.scala @@ -1,6 +1,6 @@ class Box[T](val x: T) -@annotation.capability class Cap +class Cap extends caps.Capability def foo(x: => Int): Unit = () diff --git a/tests/pos-custom-args/captures/capt-capability.scala b/tests/pos-custom-args/captures/capt-capability.scala index 830d341c7bca..03b5cb1bbabf 100644 --- a/tests/pos-custom-args/captures/capt-capability.scala +++ b/tests/pos-custom-args/captures/capt-capability.scala @@ -1,7 +1,6 @@ -import annotation.capability +import caps.Capability -@capability class Cap -def f1(c: Cap): () ->{c} c.type = () => c // ok +def f1(c: Capability): () ->{c} c.type = () => c // ok def f2: Int = val g: Boolean => Int = ??? @@ -15,15 +14,15 @@ def f3: Int = x def foo() = - val x: Cap = ??? - val y: Cap = x - val x2: () ->{x} Cap = ??? - val y2: () ->{x} Cap = x2 + val x: Capability = ??? + val y: Capability = x + val x2: () ->{x} Capability = ??? + val y2: () ->{x} Capability = x2 - val z1: () => Cap = f1(x) + val z1: () => Capability = f1(x) def h[X](a: X)(b: X) = a val z2 = - if x == null then () => x else () => Cap() + if x == null then () => x else () => new Capability() {} val _ = x diff --git a/tests/pos-custom-args/captures/caseclass.scala b/tests/pos-custom-args/captures/caseclass.scala index ffbf878dca49..0aa656eaf9cb 100644 --- a/tests/pos-custom-args/captures/caseclass.scala +++ b/tests/pos-custom-args/captures/caseclass.scala @@ -1,4 +1,4 @@ -@annotation.capability class C +class C extends caps.Capability object test1: case class Ref(x: String^) diff --git a/tests/pos-custom-args/captures/cc-this.scala b/tests/pos-custom-args/captures/cc-this.scala index 12c62e99d186..d9705df76c55 100644 --- a/tests/pos-custom-args/captures/cc-this.scala +++ b/tests/pos-custom-args/captures/cc-this.scala @@ -1,4 +1,4 @@ -@annotation.capability class Cap +class Cap extends caps.Capability def eff(using Cap): Unit = () diff --git a/tests/pos-custom-args/captures/eta-expansions.scala b/tests/pos-custom-args/captures/eta-expansions.scala index 1aac7ded1b50..b4e38cdf0856 100644 --- a/tests/pos-custom-args/captures/eta-expansions.scala +++ b/tests/pos-custom-args/captures/eta-expansions.scala @@ -1,4 +1,4 @@ -@annotation.capability class Cap +class Cap extends caps.Capability def test(d: Cap) = def map2(xs: List[Int])(f: Int => Int): List[Int] = xs.map(f) diff --git a/tests/neg-custom-args/captures/extending-impure-function.scala b/tests/pos-custom-args/captures/extending-impure-function.scala similarity index 100% rename from tests/neg-custom-args/captures/extending-impure-function.scala rename to tests/pos-custom-args/captures/extending-impure-function.scala diff --git a/tests/pos-custom-args/captures/filevar-expanded.scala b/tests/pos-custom-args/captures/filevar-expanded.scala new file mode 100644 index 000000000000..13051994f346 --- /dev/null +++ b/tests/pos-custom-args/captures/filevar-expanded.scala @@ -0,0 +1,36 @@ +import language.experimental.captureChecking +import compiletime.uninitialized + +object test1: + class File: + def write(x: String): Unit = ??? + + class Service(f: File^): + def log = f.write("log") + + def withFile[T](op: (f: File^) => T): T = + op(new File) + + def test = + withFile: f => + val o = Service(f) + o.log + +object test2: + class IO + + class File: + def write(x: String): Unit = ??? + + class Service(io: IO^): + var file: File^{io} = uninitialized + def log = file.write("log") + + def withFile[T](io2: IO^)(op: (f: File^{io2}) => T): T = + op(new File) + + def test(io3: IO^) = + withFile(io3): f => + val o = Service(io3) + o.file = f + o.log diff --git a/tests/pos-custom-args/captures/filevar-tracked.scala b/tests/pos-custom-args/captures/filevar-tracked.scala new file mode 100644 index 000000000000..dc8d0b18908b --- /dev/null +++ b/tests/pos-custom-args/captures/filevar-tracked.scala @@ -0,0 +1,37 @@ +import language.experimental.captureChecking +import language.experimental.modularity +import compiletime.uninitialized + +object test1: + class File: + def write(x: String): Unit = ??? + + class Service(f: File^): + def log = f.write("log") + + def withFile[T](op: (f: File^) => T): T = + op(new File) + + def test = + withFile: f => + val o = Service(f) + o.log + +object test2: + class IO extends caps.Capability + + class File: + def write(x: String): Unit = ??? + + class Service(tracked val io: IO): + var file: File^{io} = uninitialized + def log = file.write("log") + + def withFile[T](io2: IO)(op: (f: File^{io2}) => T): T = + op(new File) + + def test(io3: IO) = + withFile(io3): f => + val o = Service(io3) + o.file = f + o.log diff --git a/tests/pos-custom-args/captures/filevar.scala b/tests/pos-custom-args/captures/filevar.scala index a6cc7ca9ff47..9ab34fe617b5 100644 --- a/tests/pos-custom-args/captures/filevar.scala +++ b/tests/pos-custom-args/captures/filevar.scala @@ -1,5 +1,4 @@ import language.experimental.captureChecking -import annotation.capability import compiletime.uninitialized object test1: @@ -18,7 +17,7 @@ object test1: o.log object test2: - @capability class IO + class IO extends caps.Capability class File: def write(x: String): Unit = ??? @@ -27,11 +26,11 @@ object test2: var file: File^{io} = uninitialized def log = file.write("log") - def withFile[T](io: IO)(op: (f: File^{io}) => T): T = + def withFile[T](io2: IO)(op: (f: File^{io2}) => T): T = op(new File) - def test(io: IO) = - withFile(io): f => - val o = Service(io) + def test(io3: IO) = + withFile(io3): f => + val o = Service(io3) o.file = f o.log diff --git a/tests/pos-custom-args/captures/i16116.scala b/tests/pos-custom-args/captures/i16116.scala index 0311e744f146..979bfdbe4328 100644 --- a/tests/pos-custom-args/captures/i16116.scala +++ b/tests/pos-custom-args/captures/i16116.scala @@ -15,8 +15,7 @@ object CpsMonad { @experimental object Test { - @capability - class CpsTransform[F[_]] { + class CpsTransform[F[_]] extends caps.Capability { def await[T](ft: F[T]): T^{ this } = ??? } diff --git a/tests/pos-custom-args/captures/i16226.scala b/tests/pos-custom-args/captures/i16226.scala index 4cd7f0ceea81..071eefbd3420 100644 --- a/tests/pos-custom-args/captures/i16226.scala +++ b/tests/pos-custom-args/captures/i16226.scala @@ -1,4 +1,4 @@ -@annotation.capability class Cap +class Cap extends caps.Capability class LazyRef[T](val elem: () => T): val get: () ->{elem} T = elem diff --git a/tests/pos-custom-args/captures/i19751.scala b/tests/pos-custom-args/captures/i19751.scala index b6023cc0ff87..b41017f4f3e7 100644 --- a/tests/pos-custom-args/captures/i19751.scala +++ b/tests/pos-custom-args/captures/i19751.scala @@ -1,9 +1,8 @@ import language.experimental.captureChecking -import annotation.capability import caps.cap trait Ptr[A] -@capability trait Scope: +trait Scope extends caps.Capability: def allocate(size: Int): Ptr[Unit]^{this} diff --git a/tests/pos-custom-args/captures/lazyref.scala b/tests/pos-custom-args/captures/lazyref.scala index 3dae51b491b4..2e3a0030bcdc 100644 --- a/tests/pos-custom-args/captures/lazyref.scala +++ b/tests/pos-custom-args/captures/lazyref.scala @@ -1,4 +1,4 @@ -@annotation.capability class Cap +class Cap extends caps.Capability class LazyRef[T](val elem: () => T): val get: () ->{elem} T = elem diff --git a/tests/pos-custom-args/captures/lists.scala b/tests/pos-custom-args/captures/lists.scala index 99505f0bb7a2..5f4991c6be54 100644 --- a/tests/pos-custom-args/captures/lists.scala +++ b/tests/pos-custom-args/captures/lists.scala @@ -18,7 +18,7 @@ object NIL extends LIST[Nothing]: def map[A, B](f: A => B)(xs: LIST[A]): LIST[B] = xs.map(f) -@annotation.capability class Cap +class Cap extends caps.Capability def test(c: Cap, d: Cap, e: Cap) = def f(x: Cap): Unit = if c == x then () @@ -30,7 +30,7 @@ def test(c: Cap, d: Cap, e: Cap) = CONS(z, ys) val zsc: LIST[Cap ->{d, y} Unit] = zs val z1 = zs.head - val z1c: Cap^ ->{y, d} Unit = z1 + val z1c: Cap ->{y, d} Unit = z1 val ys1 = zs.tail val y1 = ys1.head diff --git a/tests/pos-custom-args/captures/logger-tracked.scala b/tests/pos-custom-args/captures/logger-tracked.scala new file mode 100644 index 000000000000..053731de444d --- /dev/null +++ b/tests/pos-custom-args/captures/logger-tracked.scala @@ -0,0 +1,67 @@ +import language.experimental.saferExceptions +import language.experimental.modularity + +class FileSystem extends caps.Capability + +class Logger(using tracked val fs: FileSystem): + def log(s: String): Unit = ??? + +def test(using fs: FileSystem) = + val l: Logger^{fs} = Logger(using fs) + l.log("hello world!") + val xs: LazyList[Int]^{l} = + LazyList.from(1) + .map { i => + l.log(s"computing elem # $i") + i * i + } + +trait LazyList[+A]: + def isEmpty: Boolean + def head: A + def tail: LazyList[A]^{this} + +object LazyNil extends LazyList[Nothing]: + def isEmpty: Boolean = true + def head = ??? + def tail = ??? + +final class LazyCons[+T](val x: T, val xs: () => LazyList[T]^) extends LazyList[T]: + def isEmpty = false + def head = x + def tail: LazyList[T]^{this} = xs() +end LazyCons + +extension [A](x: A) + def #::(xs1: => LazyList[A]^): LazyList[A]^{xs1} = + LazyCons(x, () => xs1) + +extension [A](xs: LazyList[A]^) + def map[B](f: A => B): LazyList[B]^{xs, f} = + if xs.isEmpty then LazyNil + else f(xs.head) #:: xs.tail.map(f) + +object LazyList: + def from(start: Int): LazyList[Int] = + start #:: from(start + 1) + +class Pair[+A, +B](x: A, y: B): + def fst: A = x + def snd: B = y + +def test2(ct: CanThrow[Exception], fs: FileSystem) = + def x: Int ->{ct} String = ??? + def y: Logger^{fs} = ??? + def p = Pair[Int ->{ct} String, Logger^{fs}](x, y) + def p3 = Pair(x, y) + def f = () => p.fst + + +/* + val l1: Int => String = ??? + val l2: Object^{c} = ??? + val pd = () => Pair(l1, l2) + val p2: Pair[Int => String, Object]^{c} = pd() + val hd = () => p2.fst + +*/ \ No newline at end of file diff --git a/tests/pos-custom-args/captures/logger.scala b/tests/pos-custom-args/captures/logger.scala index d95eeaae74cf..81eeb521fee5 100644 --- a/tests/pos-custom-args/captures/logger.scala +++ b/tests/pos-custom-args/captures/logger.scala @@ -1,7 +1,7 @@ -import annotation.capability import language.experimental.saferExceptions +import language.experimental.modularity -@capability class FileSystem +class FileSystem extends caps.Capability class Logger(using fs: FileSystem): def log(s: String): Unit = ??? diff --git a/tests/pos-custom-args/captures/nested-classes-tracked.scala b/tests/pos-custom-args/captures/nested-classes-tracked.scala new file mode 100644 index 000000000000..1c81441f321b --- /dev/null +++ b/tests/pos-custom-args/captures/nested-classes-tracked.scala @@ -0,0 +1,22 @@ +import language.experimental.captureChecking +import language.experimental.modularity +import annotation.{capability, constructorOnly} + +class IO extends caps.Capability +class Blah +class Pkg(using tracked val io: IO): + class Foo: + def m(foo: Blah^{io}) = ??? +class Pkg2(using tracked val io: IO): + class Foo: + def m(foo: Blah^{io}): Any = io; ??? + +def main(using io: IO) = + val pkg = Pkg() + val f = pkg.Foo() + f.m(???) + val pkg2 = Pkg2() + val f2 = pkg2.Foo() + f2.m(???) + + diff --git a/tests/pos-custom-args/captures/nested-classes.scala b/tests/pos-custom-args/captures/nested-classes.scala index b16fc4365183..4a76a88c03ff 100644 --- a/tests/pos-custom-args/captures/nested-classes.scala +++ b/tests/pos-custom-args/captures/nested-classes.scala @@ -1,9 +1,10 @@ import language.experimental.captureChecking +import language.experimental.modularity import annotation.{capability, constructorOnly} -@capability class IO +class IO extends caps.Capability class Blah -class Pkg(using @constructorOnly io: IO): +class Pkg(using io: IO): class Foo: def m(foo: Blah^{io}) = ??? class Pkg2(using io: IO): @@ -13,9 +14,9 @@ class Pkg2(using io: IO): def main(using io: IO) = val pkg = Pkg() val f = pkg.Foo() - f.m(???) + val x1 = f.m(???) val pkg2 = Pkg2() val f2 = pkg2.Foo() - f2.m(???) + val x2 = f2.m(???) diff --git a/tests/pos-custom-args/captures/null-logger.scala b/tests/pos-custom-args/captures/null-logger.scala index 0b32d045778c..d532b5f74b38 100644 --- a/tests/pos-custom-args/captures/null-logger.scala +++ b/tests/pos-custom-args/captures/null-logger.scala @@ -1,7 +1,6 @@ -import annotation.capability import annotation.constructorOnly -@capability class FileSystem +class FileSystem extends caps.Capability class NullLogger(using @constructorOnly fs: FileSystem) diff --git a/tests/pos-custom-args/captures/pairs.scala b/tests/pos-custom-args/captures/pairs.scala index e15a76970c29..da7f30185ad3 100644 --- a/tests/pos-custom-args/captures/pairs.scala +++ b/tests/pos-custom-args/captures/pairs.scala @@ -1,6 +1,6 @@ //class CC //type Cap = CC^ -@annotation.capability class Cap +class Cap extends caps.Capability object Generic: @@ -13,6 +13,6 @@ object Generic: def g(x: Cap): Unit = if d == x then () val p = Pair(f, g) val x1 = p.fst - val x1c: Cap^ ->{c} Unit = x1 + val x1c: Cap ->{c} Unit = x1 val y1 = p.snd - val y1c: Cap^ ->{d} Unit = y1 + val y1c: Cap ->{d} Unit = y1 diff --git a/tests/pos-custom-args/captures/reaches.scala b/tests/pos-custom-args/captures/reaches.scala index f17c25712c39..f82c792c8445 100644 --- a/tests/pos-custom-args/captures/reaches.scala +++ b/tests/pos-custom-args/captures/reaches.scala @@ -48,7 +48,7 @@ def compose2[A, B, C](f: A => B, g: B => C): A => C = def mapCompose[A](ps: List[(A => A, A => A)]): List[A ->{ps*} A] = ps.map((x, y) => compose1(x, y)) // Does not work if map takes an impure function, see reaches in neg -@annotation.capability class IO +class IO extends caps.Capability def test(io: IO) = val a: () ->{io} Unit = () => () diff --git a/tests/pos-custom-args/captures/try3.scala b/tests/pos-custom-args/captures/try3.scala index b44ea57ccae4..a1a1bab8724a 100644 --- a/tests/pos-custom-args/captures/try3.scala +++ b/tests/pos-custom-args/captures/try3.scala @@ -1,8 +1,7 @@ import language.experimental.erasedDefinitions -import annotation.capability import java.io.IOException -@annotation.capability class CanThrow[-E] +class CanThrow[-E] extends caps.Capability def handle[E <: Exception, T](op: CanThrow[E] ?=> T)(handler: E => T): T = val x: CanThrow[E] = ??? diff --git a/tests/pos-custom-args/captures/vars.scala b/tests/pos-custom-args/captures/vars.scala index a335be96fed1..5c9598fab508 100644 --- a/tests/pos-custom-args/captures/vars.scala +++ b/tests/pos-custom-args/captures/vars.scala @@ -1,4 +1,4 @@ -@annotation.capability class Cap +class Cap extends caps.Capability def test(cap1: Cap, cap2: Cap) = def f(x: String): String = if cap1 == cap1 then "" else "a" diff --git a/tests/pos-with-compiler-cc/dotc/core/Definitions.scala b/tests/pos-with-compiler-cc/dotc/core/Definitions.scala index 603088dd8f26..8faf208e36d0 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Definitions.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Definitions.scala @@ -985,7 +985,6 @@ class Definitions { @tu lazy val BeanPropertyAnnot: ClassSymbol = requiredClass("scala.beans.BeanProperty") @tu lazy val BooleanBeanPropertyAnnot: ClassSymbol = requiredClass("scala.beans.BooleanBeanProperty") @tu lazy val BodyAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Body") - @tu lazy val CapabilityAnnot: ClassSymbol = requiredClass("scala.annotation.capability") @tu lazy val ChildAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Child") @tu lazy val ContextResultCountAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ContextResultCount") @tu lazy val ProvisionalSuperClassAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ProvisionalSuperClass") diff --git a/tests/pos/dotty-experimental.scala b/tests/pos/dotty-experimental.scala index ee9a84a1b497..813c9b5920c1 100644 --- a/tests/pos/dotty-experimental.scala +++ b/tests/pos/dotty-experimental.scala @@ -3,6 +3,6 @@ import language.experimental.captureChecking object test { - val x: caps.Cap = caps.cap + val x: caps.Capability = caps.cap } diff --git a/tests/pos/i20237.scala b/tests/pos/i20237.scala index da3e902b78b4..973f5d2025e3 100644 --- a/tests/pos/i20237.scala +++ b/tests/pos/i20237.scala @@ -1,7 +1,6 @@ import language.experimental.captureChecking -import scala.annotation.capability -@capability class Cap: +class Cap extends caps.Capability: def use[T](body: Cap ?=> T) = body(using this) class Box[T](body: Cap ?=> T): diff --git a/tests/pos/into-bigint.scala b/tests/pos/into-bigint.scala index d7ecee40b3ba..409b5e79da2c 100644 --- a/tests/pos/into-bigint.scala +++ b/tests/pos/into-bigint.scala @@ -14,8 +14,8 @@ object BigInt: @main def Test = val x = BigInt(2) val y = 3 - val a1 = x + y - val a2 = y * x + val a1 = x + y // uses conversion on `y` + val a2 = y * x // uses conversion on `y` val a3 = x * x val a4 = y + y