From c64905f8b11bde4591fe7e942511a8f55e96855b Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 1 May 2025 14:34:43 +0200 Subject: [PATCH 01/19] Record level of current owner when creating Fresh instances --- .../src/dotty/tools/dotc/cc/CaptureSet.scala | 8 ++++---- .../src/dotty/tools/dotc/cc/CheckCaptures.scala | 2 +- compiler/src/dotty/tools/dotc/cc/root.scala | 17 +++++++---------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index 93ca0956baed..5051d62bab0c 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -423,8 +423,8 @@ object CaptureSet: def universalImpliedByCapability(using Context) = defn.universalCSImpliedByCapability - def fresh(owner: Symbol = NoSymbol)(using Context): CaptureSet = - root.Fresh.withOwner(owner).singletonCaptureSet + def fresh()(using Context): CaptureSet = + root.Fresh().singletonCaptureSet /** The shared capture set `{cap.rd}` */ def shared(using Context): CaptureSet = @@ -689,7 +689,7 @@ object CaptureSet: def solve()(using Context): Unit = CCState.withCapAsRoot: // // OK here since we infer parameter types that get checked later val approx = upperApprox(empty) - .map(root.CapToFresh(NoSymbol).inverse) // Fresh --> cap + .map(root.CapToFresh().inverse) // Fresh --> cap .showing(i"solve $this = $result", capt) //println(i"solving var $this $approx ${approx.isConst} deps = ${deps.toList}") val newElems = approx.elems -- elems @@ -1392,7 +1392,7 @@ object CaptureSet: def capturingCase(acc: CaptureSet, parent: Type, refs: CaptureSet) = this(acc, parent) ++ refs def abstractTypeCase(acc: CaptureSet, t: TypeRef, upperBound: Type) = - if includeTypevars && upperBound.isExactlyAny then CaptureSet.fresh(t.symbol) + if includeTypevars && upperBound.isExactlyAny then CaptureSet.fresh() else this(acc, upperBound) collect(CaptureSet.empty, tp) diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 358b99c58078..a2b3fdd78de0 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -825,7 +825,7 @@ class CheckCaptures extends Recheck, SymTransformer: var refined: Type = core var allCaptures: CaptureSet = if core.derivesFromMutable then initCs ++ CaptureSet.fresh() - else if core.derivesFromCapability then initCs ++ root.Fresh.withOwner(core.classSymbol).readOnly.singletonCaptureSet + else if core.derivesFromCapability then initCs ++ root.Fresh().readOnly.singletonCaptureSet else initCs for (getterName, argType) <- mt.paramNames.lazyZip(argTypes) do val getter = cls.info.member(getterName).suchThat(_.isRefiningParamAccessor).symbol diff --git a/compiler/src/dotty/tools/dotc/cc/root.scala b/compiler/src/dotty/tools/dotc/cc/root.scala index 472dd37aa543..b29fb7e661b1 100644 --- a/compiler/src/dotty/tools/dotc/cc/root.scala +++ b/compiler/src/dotty/tools/dotc/cc/root.scala @@ -125,9 +125,9 @@ object root: /** The type of fresh references */ type Fresh = AnnotatedType + /** Constructor and extractor methods for "fresh" capabilities */ object Fresh: - /** Constructor and extractor methods for "fresh" capabilities */ - private def make(owner: Symbol)(using Context): CaptureRef = + def apply(using Context)(owner: Symbol = ctx.owner): CaptureRef = if ccConfig.useSepChecks then val hiddenSet = CaptureSet.HiddenSet(owner) val res = AnnotatedType(cap, Annot(Kind.Fresh(hiddenSet))) @@ -137,9 +137,6 @@ object root: else cap - def withOwner(owner: Symbol)(using Context): CaptureRef = make(owner) - def apply()(using Context): CaptureRef = make(NoSymbol) - def unapply(tp: AnnotatedType): Option[CaptureSet.HiddenSet] = tp.annot match case Annot(Kind.Fresh(hidden)) => Some(hidden) case _ => None @@ -168,14 +165,14 @@ object root: /** Map each occurrence of cap to a different Fresh instance * Exception: CapSet^ stays as it is. */ - class CapToFresh(owner: Symbol)(using Context) extends BiTypeMap, FollowAliasesMap: + class CapToFresh()(using Context) extends BiTypeMap, FollowAliasesMap: thisMap => override def apply(t: Type) = if variance <= 0 then t else t match case t: CaptureRef if t.isCap => - Fresh.withOwner(owner) + Fresh() case t @ CapturingType(parent: TypeRef, _) if parent.symbol == defn.Caps_CapSet => t case t @ CapturingType(_, _) => @@ -213,12 +210,12 @@ object root: end CapToFresh /** Maps cap to fresh */ - def capToFresh(tp: Type, owner: Symbol = NoSymbol)(using Context): Type = - if ccConfig.useSepChecks then CapToFresh(owner)(tp) else tp + def capToFresh(tp: Type)(using Context): Type = + if ccConfig.useSepChecks then CapToFresh()(tp) else tp /** Maps fresh to cap */ def freshToCap(tp: Type)(using Context): Type = - if ccConfig.useSepChecks then CapToFresh(NoSymbol).inverse(tp) else tp + if ccConfig.useSepChecks then CapToFresh().inverse(tp) else tp /** Map top-level free existential variables one-to-one to Fresh instances */ def resultToFresh(tp: Type)(using Context): Type = From b4fff3a8923c32f7ab4a3edd39e997ddacf07d30 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 1 May 2025 14:35:34 +0200 Subject: [PATCH 02/19] Don't increase symLevel for blocks --- compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala | 3 --- compiler/src/dotty/tools/dotc/cc/Setup.scala | 3 --- 2 files changed, 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index a2b3fdd78de0..370a48a6ce91 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -887,9 +887,6 @@ class CheckCaptures extends Recheck, SymTransformer: report.error(em"$refArg is not a tracked capability", refArg.srcPos) case _ => - override def recheckBlock(tree: Block, pt: Type)(using Context): Type = - ccState.inNestedLevel(super.recheckBlock(tree, pt)) - /** Recheck Closure node: add the captured vars of the anonymoys function * to the result type. See also `recheckClosureBlock` which rechecks the * block containing the anonymous function and the Closure node. diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 21c58385b58a..8729d9716518 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -586,9 +586,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: traverse(elems) tpt.setNuType(box(transformInferredType(tpt.tpe))) - case tree: Block => - ccState.inNestedLevel(traverseChildren(tree)) - case _ => traverseChildren(tree) postProcess(tree) From 2352c951fa11b488bcb004d2c87d36997c90fcf2 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 1 May 2025 17:44:11 +0200 Subject: [PATCH 03/19] Don't allow Fresh to subsume TypeParamRefs --- compiler/src/dotty/tools/dotc/cc/CaptureRef.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala b/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala index 98f1502a0c1c..5dfacd8a5a35 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala @@ -267,7 +267,7 @@ trait CaptureRef extends TypeProxy, ValueType: vs.ifNotSeen(this)(hidden.elems.exists(_.subsumes(y))) || !y.stripReadOnly.isCap && !yIsExistential - && !y.isInstanceOf[TermParamRef] + && !y.isInstanceOf[ParamRef] && canAddHidden && vs.addHidden(hidden, y) case x @ root.Result(binder) => From 2953d64bda3e08bfc4dfad448e359a1696a7e708 Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 2 May 2025 15:18:56 +0200 Subject: [PATCH 04/19] Introduce QualifiedCapability extractor --- compiler/src/dotty/tools/dotc/cc/CaptureOps.scala | 6 ++++++ compiler/src/dotty/tools/dotc/cc/CaptureSet.scala | 8 ++------ compiler/src/dotty/tools/dotc/cc/root.scala | 2 ++ compiler/src/dotty/tools/dotc/core/Definitions.scala | 4 +++- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index afe0cfb6a8ff..290a061c38c6 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -694,6 +694,12 @@ abstract class AnnotatedCapability(annotCls: Context ?=> ClassSymbol): protected def unwrappable(using Context): Set[Symbol] end AnnotatedCapability +object QualifiedCapability: + def unapply(tree: AnnotatedType)(using Context): Option[CaptureRef] = tree match + case AnnotatedType(parent: CaptureRef, ann) + if defn.capabilityQualifierAnnots.contains(ann.symbol) => Some(parent) + case _ => None + /** An extractor for `ref @maybeCapability`, which is used to express * the maybe capability `ref?` as a type. */ diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index 5051d62bab0c..835f1a5f7a03 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -623,11 +623,7 @@ object CaptureSet: |elem binder = ${elem.binder}""") false } - case ReachCapability(elem1) => - levelOK(elem1) - case ReadOnlyCapability(elem1) => - levelOK(elem1) - case MaybeCapability(elem1) => + case QualifiedCapability(elem1) => levelOK(elem1) case _ => true @@ -930,7 +926,7 @@ object CaptureSet: override def owner = givenOwner - // assert(id != 34, i"$initialHidden") + //assert(id != 4) private def aliasRef: AnnotatedType | Null = if myElems.size == 1 then diff --git a/compiler/src/dotty/tools/dotc/cc/root.scala b/compiler/src/dotty/tools/dotc/cc/root.scala index b29fb7e661b1..ad6da7c4bccf 100644 --- a/compiler/src/dotty/tools/dotc/cc/root.scala +++ b/compiler/src/dotty/tools/dotc/cc/root.scala @@ -84,6 +84,8 @@ object root: ccs.rootId += 1 ccs.rootId + //assert(id != 4) + override def symbol(using Context) = defn.RootCapabilityAnnot override def tree(using Context) = New(symbol.typeRef, Nil) override def derivedAnnotation(tree: Tree)(using Context): Annotation = this diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 14f2491214e2..58d93f0f4899 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1561,8 +1561,10 @@ class Definitions { @tu lazy val pureSimpleClasses = Set(StringClass, NothingClass, NullClass) ++ ScalaValueClasses() + @tu lazy val capabilityQualifierAnnots: Set[Symbol] = + Set(ReachCapabilityAnnot, ReadOnlyCapabilityAnnot, MaybeCapabilityAnnot) @tu lazy val capabilityWrapperAnnots: Set[Symbol] = - Set(ReachCapabilityAnnot, ReadOnlyCapabilityAnnot, MaybeCapabilityAnnot, RootCapabilityAnnot) + capabilityQualifierAnnots + RootCapabilityAnnot @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)) From 14e992c06f915d2c408592f2d094859f439ede26 Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 2 May 2025 19:51:59 +0200 Subject: [PATCH 05/19] Refactor printing of capturing types and function types Refactor to avoid generating refsText ahead of time. We need to keep the original capture sets or tree lists so that we can record them as seen. --- .../tools/dotc/printing/PlainPrinter.scala | 63 ++++++++++--------- .../tools/dotc/printing/RefinedPrinter.scala | 52 ++++++++------- .../dotty/tools/dotc/reporting/Message.scala | 2 +- tests/neg-custom-args/captures/i15116.check | 24 +++---- 4 files changed, 75 insertions(+), 66 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index a5843b545117..70e6843055cb 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -15,6 +15,7 @@ import util.SourcePosition import scala.util.control.NonFatal import scala.annotation.switch import config.{Config, Feature} +import ast.tpd import cc.* class PlainPrinter(_ctx: Context) extends Printer { @@ -182,14 +183,41 @@ class PlainPrinter(_ctx: Context) extends Printer { private def toTextRetainedElems[T <: Untyped](refs: List[Tree[T]]): Text = "{" ~ Text(refs.map(ref => toTextRetainedElem(ref)), ", ") ~ "}" + type GeneralCaptureSet = CaptureSet | List[tpd.Tree] + + protected def isUniversalCaptureSet(refs: GeneralCaptureSet): Boolean = refs match + case refs: CaptureSet => + // The set if universal if it consists only of caps.cap or + // only of an existential Fresh that is bound to the immediately enclosing method. + val isUniversal = + refs.elems.size == 1 + && (refs.isUniversal + || !printDebug && !printFresh && !showUniqueIds && refs.elems.nth(0).match + case root.Result(binder) => + CCState.openExistentialScopes match + case b :: _ => binder eq b + case _ => false + case _ => + false + ) + isUniversal + || !refs.elems.isEmpty && refs.elems.forall(_.isCapOrFresh) && !printFresh + case (ref: tpd.Tree) :: Nil => ref.symbol == defn.captureRoot + case _ => false + + protected def toTextGeneralCaptureSet(refs: GeneralCaptureSet): Text = refs match + case refs: CaptureSet => toTextCaptureSet(refs) + case refs: List[tpd.Tree] => toTextRetainedElems(refs) + /** Print capturing type, overridden in RefinedPrinter to account for * capturing function types. */ - protected def toTextCapturing(parent: Type, refsText: Text, boxText: Text): Text = + protected def toTextCapturing(parent: Type, refs: GeneralCaptureSet, boxText: Text): Text = changePrec(InfixPrec): - boxText ~ toTextLocal(parent) ~ "^" ~ (refsText provided refsText != rootSetText) - - final protected def rootSetText = Str("{cap}") // TODO Use disambiguation + boxText + ~ toTextLocal(parent) + ~ "^" + ~ toTextGeneralCaptureSet(refs).provided(!isUniversalCaptureSet(refs)) def toText(tp: Type): Text = controlled { homogenize(tp) match { @@ -256,33 +284,10 @@ class PlainPrinter(_ctx: Context) extends Printer { then toText(parent) else - // The set if universal if it consists only of caps.cap or - // only of an existential Fresh that is bound to the immediately enclosing method. - def isUniversal = - refs.elems.size == 1 - && (refs.isUniversal - || !printDebug && !printFresh && !showUniqueIds && refs.elems.nth(0).match - case root.Result(binder) => - CCState.openExistentialScopes match - case b :: _ => binder eq b - case _ => false - case _ => - false - ) - val refsText = - if isUniversal then - rootSetText - else if !refs.elems.isEmpty && refs.elems.forall(_.isCapOrFresh) && !printFresh then - rootSetText - else - toTextCaptureSet(refs) - toTextCapturing(parent, refsText, boxText) + toTextCapturing(parent, refs, boxText) case tp @ RetainingType(parent, refs) => if Feature.ccEnabledSomewhere then - val refsText = refs match - case ref :: Nil if ref.symbol == defn.captureRoot => rootSetText - case _ => toTextRetainedElems(refs) - toTextCapturing(parent, refsText, "") ~ Str("R").provided(printDebug) + toTextCapturing(parent, refs, "") ~ Str("R").provided(printDebug) else toText(parent) case tp: PreviousErrorType if ctx.settings.XprintTypes.value => "" // do not print previously reported error message because they may try to print this error type again recursively diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 3d987982cc20..4399e88f63b7 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -159,17 +159,22 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { private def arrow(isGiven: Boolean, isPure: Boolean): String = (if isGiven then "?" else "") + (if isPure then "->" else "=>") - private def toTextFunction(tp: AppliedType, refs: Text = Str("")): Text = + private def toTextFunction(tp: AppliedType, refs: GeneralCaptureSet | Null): Text = val AppliedType(tycon, args) = (tp: @unchecked) val tsym = tycon.typeSymbol - val isContextual = tsym.name.isContextFunction - val capturesRoot = refs == rootSetText - val isPure = - Feature.pureFunsEnabled && !tsym.name.isImpureFunction && !capturesRoot - toTextFunction(args.init, args.last, tp, refs.provided(!capturesRoot), isContextual, isPure) - - private def toTextFunction(args: List[Type], res: Type, fn: MethodType | AppliedType, refs: Text, - isContextual: Boolean, isPure: Boolean): Text = + toTextFunction(args.init, args.last, tp, refs, + isContextual = tsym.name.isContextFunction, + isPure = Feature.pureFunsEnabled && !tsym.name.isImpureFunction) + + protected def funMiddleText(isContextual: Boolean, isPure: Boolean, refs: GeneralCaptureSet | Null): Text = + val (printPure, refsText) = + if refs == null then (isPure, Str("")) + else if isUniversalCaptureSet(refs) then (false, Str("")) + else (isPure, toTextGeneralCaptureSet(refs)) + arrow(isContextual, printPure) ~ refsText + + private def toTextFunction(args: List[Type], res: Type, fn: MethodType | AppliedType, + refs: GeneralCaptureSet | Null, isContextual: Boolean, isPure: Boolean): Text = changePrec(GlobalPrec): val argStr: Text = args match case arg :: Nil if !defn.isDirectTupleNType(arg) && !isContextual => @@ -179,30 +184,29 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "(" ~ argsText(args) ~ ")" - argStr ~ " " ~ arrow(isContextual, isPure) ~ refs ~ " " + argStr ~ " " + ~ funMiddleText(isContextual, isPure, refs) ~ " " ~ fn.match case fn: MethodType => CCState.inNewExistentialScope(fn)(argText(res)) case _ => argText(res) - protected def toTextMethodAsFunction(info: Type, isPure: Boolean, refs: Text = Str("")): Text = + protected def toTextMethodAsFunction(info: Type, isPure: Boolean, refs: GeneralCaptureSet | Null): Text = def recur(tp: Type, enclInfo: MethodType | Null): Text = tp match case tp: MethodType => val isContextual = tp.isImplicitMethod - val capturesRoot = refs == rootSetText if cc.isCaptureCheckingOrSetup && tp.allParamNamesSynthetic && !tp.looksResultDependent && !tp.looksParamDependent && !showUniqueIds && !printDebug && !printFresh - then + then // cc.Setup converts all functions to dependent functions. Undo that when printing. - toTextFunction(tp.paramInfos, tp.resType, tp, refs.provided(!capturesRoot), isContextual, isPure && !capturesRoot) + toTextFunction(tp.paramInfos, tp.resType, tp, refs, isContextual, isPure) else changePrec(GlobalPrec): "(" ~ paramsText(tp) ~ ") " - ~ arrow(isContextual, isPure && !capturesRoot) - ~ refs.provided(!capturesRoot) + ~ funMiddleText(isContextual, isPure, refs) ~ " " ~ recur(tp.resultType, tp) case tp: PolyType => @@ -274,7 +278,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case _ => val tsym = tycon.typeSymbol if tycon.isRepeatedParam then toTextLocal(args.head) ~ "*" - else if defn.isFunctionSymbol(tsym) then toTextFunction(tp) + else if defn.isFunctionSymbol(tsym) then toTextFunction(tp, null) else if isInfixType(tp) then val l :: r :: Nil = args: @unchecked val opName = tyconName(tycon) @@ -303,8 +307,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { toTextMethodAsFunction(tp.refinedInfo, isPure = Feature.pureFunsEnabled && !tp.typeSymbol.name.isImpureFunction, refs = tp.parent match - case CapturingType(_, cs) => toTextCaptureSet(cs) - case _ => "") + case CapturingType(_, cs) => cs + case _ => null) case tp: TypeRef => if (tp.symbol.isAnonymousClass && !showUniqueIds) toText(tp.info) @@ -320,7 +324,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case tp: ClassInfo => if tp.cls.derivesFrom(defn.PolyFunctionClass) then tp.member(nme.apply).info match - case info: PolyType => toTextMethodAsFunction(info, isPure = false) + case info: PolyType => toTextMethodAsFunction(info, isPure = false, refs = null) case _ => toTextParents(tp.parents) ~~ "{...}" else toTextParents(tp.parents) ~~ "{...}" case JavaArrayType(elemtp) => @@ -849,13 +853,13 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } } - override protected def toTextCapturing(tp: Type, refsText: Text, boxText: Text): Text = tp match + override protected def toTextCapturing(tp: Type, refs: GeneralCaptureSet, boxText: Text): Text = tp match case tp: AppliedType if defn.isFunctionSymbol(tp.typeSymbol) && !printDebug => - boxText ~ toTextFunction(tp, refsText) + boxText ~ toTextFunction(tp, refs) case tp: RefinedType if defn.isFunctionType(tp) && !printDebug => - boxText ~ toTextMethodAsFunction(tp.refinedInfo, isPure = !tp.typeSymbol.name.isImpureFunction, refsText) + boxText ~ toTextMethodAsFunction(tp.refinedInfo, isPure = !tp.typeSymbol.name.isImpureFunction, refs) case _ => - super.toTextCapturing(tp, refsText, boxText) + super.toTextCapturing(tp, refs, boxText) override def toText[T <: Untyped](tree: Tree[T]): Text = controlled { import untpd.* diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 1ac5c6ecf407..a3b5a6ba9f9c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -225,7 +225,7 @@ object Message: case tp: SkolemType => seen.record(tp.repr.toString, isType = true, tp) case _ => super.toTextRef(tp) - override def toTextMethodAsFunction(info: Type, isPure: Boolean, refs: Text): Text = + override def toTextMethodAsFunction(info: Type, isPure: Boolean, refs: GeneralCaptureSet): Text = info match case info: LambdaType => seen.openLambda(info) diff --git a/tests/neg-custom-args/captures/i15116.check b/tests/neg-custom-args/captures/i15116.check index 0a16af9f6704..6e222ce1070a 100644 --- a/tests/neg-custom-args/captures/i15116.check +++ b/tests/neg-custom-args/captures/i15116.check @@ -1,11 +1,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15116.scala:3:13 ---------------------------------------- 3 | val x = Foo(m) // error | ^^^^^^ - | Found: Foo{val m²: (Bar.this.m : String^)}^{Bar.this.m} + | Found: Foo{val m: (Bar.this.m² : String^)}^{Bar.this.m²} | Required: Foo | - | where: m is a value in class Bar - | m² is a value in class Foo + | where: m is a value in class Foo + | m² is a value in class Bar | | | Note that the expected type Foo @@ -18,11 +18,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15116.scala:5:13 ---------------------------------------- 5 | val x = Foo(m) // error | ^^^^^^ - | Found: Foo{val m²: (Baz.this.m : String^)}^{Baz.this.m} + | Found: Foo{val m: (Baz.this.m² : String^)}^{Baz.this.m²} | Required: Foo | - | where: m is a value in trait Baz - | m² is a value in class Foo + | where: m is a value in class Foo + | m² is a value in trait Baz | | | Note that the expected type Foo @@ -35,11 +35,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15116.scala:7:13 ---------------------------------------- 7 | val x = Foo(m) // error | ^^^^^^ - | Found: Foo{val m²: (Bar1.this.m : String^)}^{Bar1.this.m} + | Found: Foo{val m: (Bar1.this.m² : String^)}^{Bar1.this.m²} | Required: Foo | - | where: m is a value in class Bar1 - | m² is a value in class Foo + | where: m is a value in class Foo + | m² is a value in class Bar1 | | | Note that the expected type Foo @@ -52,11 +52,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15116.scala:9:13 ---------------------------------------- 9 | val x = Foo(m) // error | ^^^^^^ - | Found: Foo{val m²: (Baz2.this.m : String^)}^{Baz2.this.m} + | Found: Foo{val m: (Baz2.this.m² : String^)}^{Baz2.this.m²} | Required: Foo | - | where: m is a value in trait Baz2 - | m² is a value in class Foo + | where: m is a value in class Foo + | m² is a value in trait Baz2 | | | Note that the expected type Foo From 11ca325551155c607bddb286f0744aabff340014 Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 3 May 2025 10:44:35 +0200 Subject: [PATCH 06/19] Explain root capabilities in error messages --- .../src/dotty/tools/dotc/cc/CaptureOps.scala | 3 - .../dotty/tools/dotc/cc/CheckCaptures.scala | 8 +- compiler/src/dotty/tools/dotc/cc/root.scala | 26 ----- .../tools/dotc/printing/PlainPrinter.scala | 11 +- .../tools/dotc/printing/RefinedPrinter.scala | 4 +- .../src/dotty/tools/dotc/printing/Texts.scala | 2 +- .../dotty/tools/dotc/reporting/Message.scala | 49 ++++++++- .../captures/boundschecks3.check | 8 ++ .../captures/box-adapt-cases.check | 6 +- .../captures/box-adapt-contra.check | 3 + tests/neg-custom-args/captures/byname.check | 2 + .../captures/capt-depfun.check | 2 + tests/neg-custom-args/captures/capt1.check | 15 +++ .../captures/cc-existential-conformance.check | 30 ++++-- .../neg-custom-args/captures/cc-poly-2.check | 2 + tests/neg-custom-args/captures/cc-this2.check | 2 + tests/neg-custom-args/captures/cc-this3.check | 2 + tests/neg-custom-args/captures/cc-this4.check | 2 + .../neg-custom-args/captures/contracap.check | 2 + .../captures/depfun-reach.check | 16 ++- .../captures/effect-swaps-explicit.check | 19 ++-- .../captures/effect-swaps.check | 17 +-- .../captures/erased-methods2.check | 23 ++-- .../captures/existential-mapping.check | 100 ++++++++++++++---- .../captures/filevar-expanded.check | 5 +- tests/neg-custom-args/captures/filevar.check | 2 + .../captures/heal-tparam-cs.check | 16 ++- tests/neg-custom-args/captures/i15116.check | 12 ++- tests/neg-custom-args/captures/i15772.check | 11 ++ tests/neg-custom-args/captures/i16114.check | 10 ++ tests/neg-custom-args/captures/i16226.check | 25 +++-- tests/neg-custom-args/captures/i19330.check | 5 +- tests/neg-custom-args/captures/i21313.check | 2 + tests/neg-custom-args/captures/i21401.check | 16 ++- tests/neg-custom-args/captures/i21614.check | 18 ++-- tests/neg-custom-args/captures/i21920.check | 12 ++- .../captures/lazylists-exceptions.check | 2 + tests/neg-custom-args/captures/lazyref.check | 14 +++ .../captures/leaking-iterators.check | 6 +- tests/neg-custom-args/captures/levels.check | 2 + .../captures/linear-buffer-2.check | 10 ++ .../captures/linear-buffer.check | 10 ++ .../neg-custom-args/captures/outer-var.check | 11 +- tests/neg-custom-args/captures/reaches.check | 60 ++++++++--- tests/neg-custom-args/captures/reaches2.check | 22 ++-- tests/neg-custom-args/captures/real-try.check | 8 ++ tests/neg-custom-args/captures/sep-box.check | 2 + .../captures/sep-compose.check | 38 +++++-- .../captures/sep-consume.check | 4 + .../captures/sep-counter.check | 8 +- .../captures/sep-curried.check | 27 ++++- tests/neg-custom-args/captures/sep-list.check | 2 + .../neg-custom-args/captures/sep-pairs.check | 31 ++++-- tests/neg-custom-args/captures/sep-use.check | 8 ++ tests/neg-custom-args/captures/sep-use2.check | 19 +++- .../neg-custom-args/captures/sepchecks2.check | 20 +++- .../neg-custom-args/captures/sepchecks4.check | 2 + .../neg-custom-args/captures/sepchecks5.check | 2 + .../captures/shared-capability.check | 2 + .../captures/simple-using.check | 6 +- tests/neg-custom-args/captures/try.check | 25 ++++- .../captures/unsound-reach-4.check | 4 + .../captures/unsound-reach.check | 6 ++ .../captures/usingLogFile.check | 30 ++++-- .../captures/vars-simple.check | 2 + tests/neg-custom-args/captures/vars.check | 3 + .../captures/widen-reach.check | 7 +- 67 files changed, 683 insertions(+), 198 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index 290a061c38c6..71031a791b01 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -15,9 +15,6 @@ import CaptureSet.VarState /** Attachment key for capturing type trees */ private val Captures: Key[CaptureSet] = Key() -/** Context property to print root.Fresh(...) as "fresh" instead of "cap" */ -val PrintFresh: Key[Unit] = Key() - /** Are we at checkCaptures phase? */ def isCaptureChecking(using Context): Boolean = ctx.phaseId == Phases.checkCapturesPhase.id diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 370a48a6ce91..ecee5738c4bc 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -373,8 +373,7 @@ class CheckCaptures extends Recheck, SymTransformer: case added: CaptureRef => target.elems += added case added: CaptureSet => target.elems ++= added.elems case _ => - inContext(root.printContext(added, res.blocking)): - report.error(msg(provisional = false), pos) + report.error(msg(provisional = false), pos) case _ => /** Check subcapturing `{elem} <: cs`, report error on failure */ @@ -1325,9 +1324,8 @@ class CheckCaptures extends Recheck, SymTransformer: actualBoxed case fail: CompareFailure => capt.println(i"conforms failed for ${tree}: $actual vs $expected") - inContext(root.printContext(actualBoxed, expected1)): - err.typeMismatch(tree.withType(actualBoxed), expected1, - addApproxAddenda( + err.typeMismatch(tree.withType(actualBoxed), expected1, + addApproxAddenda( addenda ++ errorNotes(fail.errorNotes), expected1)) actual diff --git a/compiler/src/dotty/tools/dotc/cc/root.scala b/compiler/src/dotty/tools/dotc/cc/root.scala index ad6da7c4bccf..81993c0f7d74 100644 --- a/compiler/src/dotty/tools/dotc/cc/root.scala +++ b/compiler/src/dotty/tools/dotc/cc/root.scala @@ -328,30 +328,4 @@ object root: throw ex end toResultInResults - /** 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` 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(_) => false - case t: TermRef => t.isCap || this(x, t.widen) - case CapturingType(t1, refs) => refs.containsCap || this(x, t1) - case x: ThisType => false - case _ => foldOver(x, t) - - def containsCap(x: Type | CaptureSet): Boolean = x match - case tp: Type => - hasCap(false, tp) - case refs: CaptureSet => - refs.elems.exists(_.stripReadOnly.isCap) - - if refs.exists(containsCap) then - ctx.withProperty(PrintFresh, Some(())) - else - ctx - end printContext end root \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 70e6843055cb..6f9d3a28f725 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -31,9 +31,6 @@ class PlainPrinter(_ctx: Context) extends Printer { /** Print Fresh instances as */ protected def ccVerbose = ctx.settings.YccVerbose.value - /** Print Fresh instances as "fresh" */ - protected def printFresh = ccVerbose || ctx.property(PrintFresh).isDefined - private var openRecs: List[RecType] = Nil protected def maxToTextRecursions: Int = 100 @@ -192,7 +189,7 @@ class PlainPrinter(_ctx: Context) extends Printer { val isUniversal = refs.elems.size == 1 && (refs.isUniversal - || !printDebug && !printFresh && !showUniqueIds && refs.elems.nth(0).match + || !printDebug && !ccVerbose && !showUniqueIds && refs.elems.nth(0).match case root.Result(binder) => CCState.openExistentialScopes match case b :: _ => binder eq b @@ -201,7 +198,7 @@ class PlainPrinter(_ctx: Context) extends Printer { false ) isUniversal - || !refs.elems.isEmpty && refs.elems.forall(_.isCapOrFresh) && !printFresh + || !refs.elems.isEmpty && refs.elems.forall(_.isCapOrFresh) && !ccVerbose case (ref: tpd.Tree) :: Nil => ref.symbol == defn.captureRoot case _ => false @@ -466,12 +463,12 @@ class PlainPrinter(_ctx: Context) extends Printer { val vbleText: Text = CCState.openExistentialScopes.indexOf(binder) match case -1 => "" - case n => "outer_" * n ++ (if printFresh then "localcap" else "cap") + case n => "outer_" * n ++ (if ccVerbose then "localcap" else "cap") vbleText ~ hashStr(binder) ~ Str(idStr).provided(showUniqueIds) case tp @ root.Fresh(hidden) => val idStr = if showUniqueIds then s"#${tp.rootAnnot.id}" else "" if ccVerbose then s"" - else if printFresh then "fresh" + else if ccVerbose then "fresh" else "cap" case tp => toText(tp) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 4399e88f63b7..ecc1250cbe7a 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -156,7 +156,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { else simpleNameString(tsym) } - private def arrow(isGiven: Boolean, isPure: Boolean): String = + protected def arrow(isGiven: Boolean, isPure: Boolean): String = (if isGiven then "?" else "") + (if isPure then "->" else "=>") private def toTextFunction(tp: AppliedType, refs: GeneralCaptureSet | Null): Text = @@ -197,7 +197,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if cc.isCaptureCheckingOrSetup && tp.allParamNamesSynthetic && !tp.looksResultDependent && !tp.looksParamDependent - && !showUniqueIds && !printDebug && !printFresh + && !showUniqueIds && !printDebug && !ccVerbose then // cc.Setup converts all functions to dependent functions. Undo that when printing. toTextFunction(tp.paramInfos, tp.resType, tp, refs, isContextual, isPure) diff --git a/compiler/src/dotty/tools/dotc/printing/Texts.scala b/compiler/src/dotty/tools/dotc/printing/Texts.scala index 475e2c6900d5..63f6c0aff895 100644 --- a/compiler/src/dotty/tools/dotc/printing/Texts.scala +++ b/compiler/src/dotty/tools/dotc/printing/Texts.scala @@ -137,7 +137,7 @@ object Texts { case _ => relems.foldLeft(-1)((acc, relem) => acc max relem.maxLine) } - def mkString(width: Int, withLineNumbers: Boolean): String = { + def mkString(width: Int = Int.MaxValue, withLineNumbers: Boolean = false): String = { val sb = new StringBuilder val numberWidth = if (withLineNumbers) (2 * maxLine.toString.length) + 2 else 0 layout(width - numberWidth).print(sb, numberWidth) diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index a3b5a6ba9f9c..6b884556d492 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -8,6 +8,7 @@ import printing.{RefinedPrinter, MessageLimiter, ErrorMessageLimiter} import printing.Texts.Text import printing.Formatting.hl import config.SourceVersion +import cc.{CaptureRef, CaptureSet, root} import scala.language.unsafeNulls import scala.annotation.threadUnsafe @@ -40,7 +41,7 @@ object Message: i"\n$what can be rewritten automatically under -rewrite $optionStr." else "" - private type Recorded = Symbol | ParamRef | SkolemType + private type Recorded = Symbol | ParamRef | SkolemType | CaptureRef private case class SeenKey(str: String, isType: Boolean) @@ -132,7 +133,7 @@ object Message: end record /** Create explanation for single `Recorded` type or symbol */ - private def explanation(entry: AnyRef)(using Context): String = + private def explanation(entry: AnyRef, key: String)(using Context): String = def boundStr(bound: Type, default: ClassSymbol, cmp: String) = if (bound.isRef(default)) "" else i"$cmp $bound" @@ -152,7 +153,7 @@ object Message: "" } - entry match { + entry match case param: TypeParamRef => s"is a type variable${addendum("constraint", TypeComparer.bounds(param))}" case param: TermParamRef => @@ -166,7 +167,25 @@ object Message: s"is a ${ctx.printer.kindString(sym)}${sym.showExtendedLocation}${addendum("bounds", info)}" case tp: SkolemType => s"is an unknown value of type ${tp.widen.show}" - } + case ref: CaptureRef => + val relation = + if List("^", "=>", "?=>").exists(key.startsWith) then "refers to" + else "is" + def ownerStr(owner: Symbol): String = + if owner.isConstructor then + i"constructor of ${ownerStr(owner.owner)}" + else if owner.isAnonymousFunction then + i"anonymous fucntion of type ${owner.info}" + else if owner.name.toString.contains('$') then + ownerStr(owner.owner) + else + owner.show + val descr = + if ref.isCap then "the universal root capability" + else ref match + case root.Fresh(hidden) => i"a fresh root capability created in ${ownerStr(hidden.owner)}" + case root.Result(binder) => i"a root capability associated with the result type of $binder" + s"$relation $descr" end explanation /** Produce a where clause with explanations for recorded iterms. @@ -177,6 +196,7 @@ object Message: case param: ParamRef => false case skolem: SkolemType => true case sym: Symbol => ctx.gadt.contains(sym) && ctx.gadt.fullBounds(sym) != TypeBounds.empty + case ref: CaptureRef => ref.isRootCapability } val toExplain: List[(String, Recorded)] = seen.toList.flatMap { kvs => @@ -201,7 +221,7 @@ object Message: } } - val explainParts = toExplain.map { case (str, entry) => (str, explanation(entry)) } + val explainParts = toExplain.map { case (str, entry) => (str, explanation(entry, str)) } val explainLines = columnar(explainParts) if (explainLines.isEmpty) "" else i"where: $explainLines%\n %\n" end explanations @@ -225,6 +245,25 @@ object Message: case tp: SkolemType => seen.record(tp.repr.toString, isType = true, tp) case _ => super.toTextRef(tp) + override def toTextCaptureRef(tp: Type): Text = tp match + case tp: CaptureRef if tp.isRootCapability && !tp.isReadOnly => + seen.record("cap", isType = false, tp) + case _ => super.toTextCaptureRef(tp) + + override def toTextCapturing(parent: Type, refs: GeneralCaptureSet, boxText: Text) = refs match + case refs: CaptureSet + if isUniversalCaptureSet(refs) && !defn.isFunctionType(parent) && !printDebug => + boxText ~ toTextLocal(parent) ~ seen.record("^", isType = true, refs.elems.nth(0)) + case _ => + super.toTextCapturing(parent, refs, boxText) + + override def funMiddleText(isContextual: Boolean, isPure: Boolean, refs: GeneralCaptureSet | Null): Text = + refs match + case refs: CaptureSet if isUniversalCaptureSet(refs) => + seen.record(arrow(isContextual, isPure = false), isType = true, refs.elems.nth(0)) + case _ => + super.funMiddleText(isContextual, isPure, refs) + override def toTextMethodAsFunction(info: Type, isPure: Boolean, refs: GeneralCaptureSet): Text = info match case info: LambdaType => diff --git a/tests/neg-custom-args/captures/boundschecks3.check b/tests/neg-custom-args/captures/boundschecks3.check index 035d327e3d71..3121ee229cf6 100644 --- a/tests/neg-custom-args/captures/boundschecks3.check +++ b/tests/neg-custom-args/captures/boundschecks3.check @@ -3,22 +3,30 @@ | ^ | Type argument box test.Tree^ does not conform to upper bound test.Tree in inferred type test.C[box test.Tree^] | + | where: ^ refers to the universal root capability + | | longer explanation available when compiling with `-explain` -- [E057] Type Mismatch Error: tests/neg-custom-args/captures/boundschecks3.scala:10:11 -------------------------------- 10 | type T = C[Tree^] // error | ^ | Type argument box test.Tree^ does not conform to upper bound test.Tree in inferred type test.C[box test.Tree^] | + | where: ^ refers to the universal root capability + | | longer explanation available when compiling with `-explain` -- [E057] Type Mismatch Error: tests/neg-custom-args/captures/boundschecks3.scala:11:11 -------------------------------- 11 | val bar: T -> T = ??? // error | ^ |Type argument box test.Tree^ does not conform to upper bound test.Tree in subpart test.C[box test.Tree^] of inferred type test.C[box test.Tree^] -> test.C[box test.Tree^] | + |where: ^ refers to the universal root capability + | | longer explanation available when compiling with `-explain` -- [E057] Type Mismatch Error: tests/neg-custom-args/captures/boundschecks3.scala:12:11 -------------------------------- 12 | val baz: C[Tree^] -> Unit = ??? // error | ^ |Type argument box test.Tree^ does not conform to upper bound test.Tree in subpart test.C[box test.Tree^] of inferred type test.C[box test.Tree^] -> Unit | + |where: ^ refers to the universal root capability + | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/box-adapt-cases.check b/tests/neg-custom-args/captures/box-adapt-cases.check index c89f7a09f293..a3902a96994d 100644 --- a/tests/neg-custom-args/captures/box-adapt-cases.check +++ b/tests/neg-custom-args/captures/box-adapt-cases.check @@ -2,7 +2,11 @@ 8 | x.value(cap => cap.use()) // error, was OK | ^^^^^^^^^^^^^^^^ | Found: (cap: box Cap^?) => Int - | Required: (cap: box Cap^) ->{fresh} Int + | Required: (cap: box Cap^) =>² Int + | + | where: => refers to the universal root capability + | =>² refers to a fresh root capability created in method test1 + | ^ refers to the universal root capability | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/box-adapt-cases.scala:15:10 ------------------------------ diff --git a/tests/neg-custom-args/captures/box-adapt-contra.check b/tests/neg-custom-args/captures/box-adapt-contra.check index fd36f23aa999..b0bdc1d51ce1 100644 --- a/tests/neg-custom-args/captures/box-adapt-contra.check +++ b/tests/neg-custom-args/captures/box-adapt-contra.check @@ -8,6 +8,9 @@ | ^^^^^^^^^^^^^^^^^^^ | Cap^{c} => Unit cannot be box-converted to box Cap^{c} ->{cap, c} Unit | since the additional capture set {c} resulting from box conversion is not allowed in box Cap^{c} => Unit + | + | where: => refers to the universal root capability + | cap is the universal root capability -- Error: tests/neg-custom-args/captures/box-adapt-contra.scala:19:54 -------------------------------------------------- 19 | val f3: (Cap^{c} -> Unit) => Unit = useCap3[Cap^{c}](c) // error | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/neg-custom-args/captures/byname.check b/tests/neg-custom-args/captures/byname.check index a6d04354bbbf..41a5785bc294 100644 --- a/tests/neg-custom-args/captures/byname.check +++ b/tests/neg-custom-args/captures/byname.check @@ -10,6 +10,8 @@ | Found: () ?->{cap1} Int ->{cap1} Int | Required: () ?=> Int ->{cap2} Int | + | where: ?=> refers to a fresh root capability created in method test + | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/byname.scala:19:5 ---------------------------------------- 19 | h(g()) // error diff --git a/tests/neg-custom-args/captures/capt-depfun.check b/tests/neg-custom-args/captures/capt-depfun.check index d7ad819fb423..b9c94c644db6 100644 --- a/tests/neg-custom-args/captures/capt-depfun.check +++ b/tests/neg-custom-args/captures/capt-depfun.check @@ -4,6 +4,8 @@ | Found: Str^{} ->{ac, y, z} Str^{y, z} | Required: Str^{y, z} => Str^{y, z} | + | where: => refers to a fresh root capability created in value dc + | | 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 diff --git a/tests/neg-custom-args/captures/capt1.check b/tests/neg-custom-args/captures/capt1.check index e0eb8731b3de..f53a4387f2fc 100644 --- a/tests/neg-custom-args/captures/capt1.check +++ b/tests/neg-custom-args/captures/capt1.check @@ -42,33 +42,48 @@ | ^^^^^^^^^ | Type variable X of method h cannot be instantiated to () -> box C^ since | the part box C^ of that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:36:24 ---------------------------------------- 36 | val z2 = h[() -> Cap](() => x) // error // error | ^^^^^^^ | Found: () ->{x} box C^{x} | Required: () -> box C^ | + | where: ^ refers to a fresh root capability created in value z2 + | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/capt1.scala:38:13 ------------------------------------------------------------- 38 | val z3 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // error | ^^^^^^^^^^^^^^^^^^^^^^^ | Type variable X of method h cannot be instantiated to box () ->{x} C^ since | the part C^ of that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- 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 () => C^. | This type hides capabilities {x} + | + | where: => refers to a fresh root capability created in value z1 + | ^ refers to a fresh root capability created in value z1 -- 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 () => C^. | This type hides capabilities {x} + | + | where: => refers to a fresh root capability created in value z1 + | ^ refers to a fresh root capability created in value z1 -- 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 () => C^. | This type hides capabilities {x} + | + | where: => refers to a fresh root capability created in value z1 + | ^ refers to a fresh root capability created in value z1 diff --git a/tests/neg-custom-args/captures/cc-existential-conformance.check b/tests/neg-custom-args/captures/cc-existential-conformance.check index 65367fad1df1..5fe06dc28712 100644 --- a/tests/neg-custom-args/captures/cc-existential-conformance.check +++ b/tests/neg-custom-args/captures/cc-existential-conformance.check @@ -2,29 +2,37 @@ 8 | val y: A -> Fun[B^] = x // error | ^ | Found: (x : A -> (x²: A) -> B^) - | Required: A -> A -> B^ + | Required: A -> A -> B^² | - | where: x is a value in method test + | where: ^ refers to a root capability associated with the result type of (x²: A): B^ + | ^² refers to a fresh root capability created in value y + | x is a value in method test | x² is a reference to a value parameter | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-existential-conformance.scala:9:29 -------------------- 9 | val z: A -> (x: A) -> B^ = y // error | ^ - | Found: (y : A -> A -> B^) - | Required: A -> (x: A) -> B^ + | Found: (y : A -> A -> B^) + | Required: A -> (x: A) -> B^² | - | Note that the existential capture root in B^ - | cannot subsume the capability cap + | where: ^ refers to a fresh root capability created in value y + | ^² refers to a root capability associated with the result type of (x: A): B^² + | + | + | Note that the existential capture root in B^ + | cannot subsume the capability cap | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-existential-conformance.scala:13:19 ------------------- 13 | val y: Fun[B^] = x // error | ^ | Found: (x : (x²: A) -> B^) - | Required: A -> B^ + | Required: A -> B^² | - | where: x is a value in method test2 + | where: ^ refers to a root capability associated with the result type of (x²: A): B^ + | ^² refers to a fresh root capability created in value y + | x is a value in method test2 | x² is a reference to a value parameter | | longer explanation available when compiling with `-explain` @@ -32,7 +40,11 @@ 14 | val z: (x: A) -> B^ = y // error | ^ | Found: (y : A -> B^) - | Required: (x: A) -> B^ + | Required: (x: A) -> B^² + | + | where: ^ refers to a fresh root capability created in value y + | ^² refers to a root capability associated with the result type of (x: A): B^² + | | | Note that the existential capture root in B^ | cannot subsume the capability cap diff --git a/tests/neg-custom-args/captures/cc-poly-2.check b/tests/neg-custom-args/captures/cc-poly-2.check index 90568de385b3..fcefeec5fee7 100644 --- a/tests/neg-custom-args/captures/cc-poly-2.check +++ b/tests/neg-custom-args/captures/cc-poly-2.check @@ -4,6 +4,8 @@ | Found: (d : Test.D^) | Required: Test.D^{c1} | + | where: ^ refers to a fresh root capability created in value d + | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-poly-2.scala:16:20 ------------------------------------ 16 | val _: D^{c1} = x // error diff --git a/tests/neg-custom-args/captures/cc-this2.check b/tests/neg-custom-args/captures/cc-this2.check index dc61fe2e0396..932e7c336ef0 100644 --- a/tests/neg-custom-args/captures/cc-this2.check +++ b/tests/neg-custom-args/captures/cc-this2.check @@ -9,4 +9,6 @@ | illegal inheritance: self type D^ of class D does not conform to self type C | of parent class C | + | where: ^ refers to the universal root capability + | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/cc-this3.check b/tests/neg-custom-args/captures/cc-this3.check index d57471c6872e..cdd91767ce22 100644 --- a/tests/neg-custom-args/captures/cc-this3.check +++ b/tests/neg-custom-args/captures/cc-this3.check @@ -4,6 +4,8 @@ | illegal inheritance: self type B^ of class B does not conform to self type A^{} | of parent class A | + | where: ^ refers to the universal root capability + | | longer explanation available when compiling with `-explain` -- [E058] Type Mismatch Error: tests/neg-custom-args/captures/cc-this3.scala:11:6 -------------------------------------- 11 |class C(val f: () => Int) extends A // error diff --git a/tests/neg-custom-args/captures/cc-this4.check b/tests/neg-custom-args/captures/cc-this4.check index d650d905f9b5..d97c304a9cd3 100644 --- a/tests/neg-custom-args/captures/cc-this4.check +++ b/tests/neg-custom-args/captures/cc-this4.check @@ -4,4 +4,6 @@ | Found: (C.this : C^) | Required: C | + | where: ^ refers to the universal root capability + | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/contracap.check b/tests/neg-custom-args/captures/contracap.check index 31b58acb80d5..e2a3e0958674 100644 --- a/tests/neg-custom-args/captures/contracap.check +++ b/tests/neg-custom-args/captures/contracap.check @@ -4,4 +4,6 @@ | Found: (f : (Ref[Int]^, Ref[Int]^) -> Unit) | Required: (Ref[Int]^{a}, Ref[Int]^{a}) -> Unit | + | where: ^ refers to the universal root capability + | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/depfun-reach.check b/tests/neg-custom-args/captures/depfun-reach.check index c4b74c5123c6..e3a8f63b641b 100644 --- a/tests/neg-custom-args/captures/depfun-reach.check +++ b/tests/neg-custom-args/captures/depfun-reach.check @@ -1,22 +1,30 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/depfun-reach.scala:12:27 --------------------------------- 12 | List(() => op.foreach((f,g) => { f(); g() })) // error (???) | ^^^^^^^^^^^^^^^^^^^^ - | Found: (x$1: (box () ->? Unit, box () ->? Unit)^?) ->? Unit - | Required: (x$1: (box () ->{op*} Unit, box () ->{op*} Unit)) => Unit + | Found: (x$1: (box () ->? Unit, box () ->? Unit)^?) ->? Unit + | Required: (x$1: (box () ->{op*} Unit, box () ->{op*} Unit)) => Unit + | + | where: => refers to a fresh root capability created in anonymous fucntion of type (): Unit | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/depfun-reach.scala:19:4 ---------------------------------- 19 | op // error | ^^ | Found: (op : (xs: List[(X, box () ->{io} Unit)]) => List[box () ->{xs*} Unit]) - | Required: (xs: List[(X, box () ->{io} Unit)]) => List[() -> Unit] + | Required: (xs: List[(X, box () ->{io} Unit)]) =>² List[() -> Unit] + | + | where: => refers to a fresh root capability created in method foo + | =>² refers to a fresh root capability created in method foo | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/depfun-reach.scala:26:60 --------------------------------- 26 | val b: (xs: List[() ->{io} Unit]) => List[() ->{} Unit] = a // error | ^ | Found: (a : (xs: List[box () ->{io} Unit]) => List[box () ->{xs*} Unit]) - | Required: (xs: List[box () ->{io} Unit]) => List[() -> Unit] + | Required: (xs: List[box () ->{io} Unit]) =>² List[() -> Unit] + | + | where: => refers to a fresh root capability created in value a + | =>² refers to a fresh root capability created in value b | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/depfun-reach.scala:18:17 ------------------------------------------------------ diff --git a/tests/neg-custom-args/captures/effect-swaps-explicit.check b/tests/neg-custom-args/captures/effect-swaps-explicit.check index 9fc99f1a10d2..7eafba36e38e 100644 --- a/tests/neg-custom-args/captures/effect-swaps-explicit.check +++ b/tests/neg-custom-args/captures/effect-swaps-explicit.check @@ -17,12 +17,16 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps-explicit.scala:69:10 ------------------------ 69 | Future: fut ?=> // error, type mismatch | ^ - |Found: (contextual$9: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]^) ?->{fr, async} - | box Future[box T^?]^{fr, contextual$9} - |Required: (contextual$9: boundary.Label[Result[box Future[box T^?]^?, box E^?]]^) ?->{fresh} box Future[box T^?]^? + | Found: (contextual$9: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]^) ?->{fr, async} + | box Future[box T^?]^{fr, contextual$9} + | Required: (contextual$9: boundary.Label[Result[box Future[box T^?]^?, box E^?]]^) ?=> box Future[box T^?]^? | - |Note that reference contextual$9.type - |cannot be included in outer capture set ? + | where: ?=> refers to a fresh root capability created in method fail4 + | ^ refers to the universal root capability + | + | + | Note that reference contextual$9.type + | cannot be included in outer capture set ? 70 | fr.await.ok | | longer explanation available when compiling with `-explain` @@ -30,7 +34,10 @@ 73 | Result.make[Future[T], E]: lbl ?=> // error: type mismatch | ^ |Found: (lbl: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]^) ?->{fr, async} Future[box T^?]^{fr, lbl} - |Required: (lbl: boundary.Label[Result[Future[T], E]]^) ?->{fresh} Future[T] + |Required: (lbl: boundary.Label[Result[Future[T], E]]^) ?=> Future[T] + | + |where: ?=> refers to a fresh root capability created in method fail5 + | ^ refers to the universal root capability 74 | Future: fut ?=> 75 | fr.await.ok | diff --git a/tests/neg-custom-args/captures/effect-swaps.check b/tests/neg-custom-args/captures/effect-swaps.check index dc832fca0ee9..53fa5ccda75b 100644 --- a/tests/neg-custom-args/captures/effect-swaps.check +++ b/tests/neg-custom-args/captures/effect-swaps.check @@ -17,12 +17,15 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:69:10 --------------------------------- 69 | Future: fut ?=> // error, type mismatch | ^ - |Found: (contextual$9: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]) ?->{fr, async} - | box Future[box T^?]^{fr, contextual$9} - |Required: (contextual$9: boundary.Label[Result[box Future[box T^?]^?, box E^?]]) ?->{fresh} box Future[box T^?]^? + | Found: (contextual$9: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]) ?->{fr, async} + | box Future[box T^?]^{fr, contextual$9} + | Required: (contextual$9: boundary.Label[Result[box Future[box T^?]^?, box E^?]]) ?=> box Future[box T^?]^? | - |Note that reference contextual$9.type - |cannot be included in outer capture set ? + | where: ?=> refers to a fresh root capability created in method fail4 + | + | + | Note that reference contextual$9.type + | cannot be included in outer capture set ? 70 | fr.await.ok | | longer explanation available when compiling with `-explain` @@ -30,7 +33,9 @@ 73 | Result.make[Future[T], E]: lbl ?=> // error: type mismatch | ^ |Found: (lbl: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]) ?->{fr, async} Future[box T^?]^{fr, lbl} - |Required: (lbl: boundary.Label[Result[Future[T], E]]) ?->{fresh} Future[T] + |Required: (lbl: boundary.Label[Result[Future[T], E]]) ?=> Future[T] + | + |where: ?=> refers to a fresh root capability created in method fail5 74 | Future: fut ?=> 75 | fr.await.ok | diff --git a/tests/neg-custom-args/captures/erased-methods2.check b/tests/neg-custom-args/captures/erased-methods2.check index 876ff36b3696..95a1280e8524 100644 --- a/tests/neg-custom-args/captures/erased-methods2.check +++ b/tests/neg-custom-args/captures/erased-methods2.check @@ -1,11 +1,16 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/erased-methods2.scala:20:4 ------------------------------- 20 | = (x$1: CT[Ex3]^) // error | ^ - | Found: (erased x$1: CT[Ex3]^) ?->? (erased x$2: CT[Ex2]^?) ?->{x$1} Unit - | Required: (erased x$1: CT[Ex3]^) ?->{fresh} (erased x$2: CT[Ex2]^) ?->{localcap} Unit + |Found: (erased x$1: CT[Ex3]^) ?->? (erased x$2: CT[Ex2]^?) ?->{x$1} Unit + |Required: (erased x$1: CT[Ex3]^) ?=> (erased x$2: CT[Ex2]^) ?=>² Unit | - | Note that the existential capture root in (erased x$2: CT[Ex2]^) ?=> Unit - | cannot subsume the capability x$1.type since that capability is not a SharedCapability + |where: ?=> refers to a fresh root capability created in method foo9a + | ?=>² refers to a root capability associated with the result type of (using erased x$1: CT[Ex3]^): (erased x$2: CT[Ex2]^) ?=>² Unit + | ^ refers to the universal root capability + | + | + |Note that the existential capture root in (erased x$2: CT[Ex2]^) ?=> Unit + |cannot subsume the capability x$1.type since that capability is not a SharedCapability 21 | ?=> (x$2: CT[Ex2]^) 22 | ?=> 23 | //given (CT[Ex3]^) = x$1 @@ -16,9 +21,15 @@ 31 | = (erased x$1: CT[Ex3]^) // error | ^ |Found: (erased x$1: CT[Ex3]^) ?->? (erased x$1: CT[Ex2]^?) ?->{x$1} (erased x$2: CT[Ex1]^?) ?->{x$1} Unit - |Required: (erased x$1: CT[Ex3]^) ?->{fresh} (erased x$1: CT[Ex2]^) ?->{localcap} (erased x$2: CT[Ex1]^) ?->{localcap} Unit + |Required: (erased x$1: CT[Ex3]^) ?=> (erased x$1: CT[Ex2]^) ?=>² (erased x$2: CT[Ex1]^) ?=>³ Unit + | + |where: ?=> refers to a fresh root capability created in method foo10a + | ?=>² refers to a root capability associated with the result type of (using erased x$1: CT[Ex3]^): (erased x$1: CT[Ex2]^) ?=>² (erased x$2: CT[Ex1]^) ?=>³ Unit + | ?=>³ refers to a root capability associated with the result type of (using erased x$1: CT[Ex2]^): (erased x$2: CT[Ex1]^) ?=>³ Unit + | ^ refers to the universal root capability + | | - |Note that the existential capture root in (erased x$1: CT[Ex2]^) ?=> (erased x$2: CT[Ex1]^) ?->{localcap} Unit + |Note that the existential capture root in (erased x$1: CT[Ex2]^) ?=> (erased x$2: CT[Ex1]^) ?=> Unit |cannot subsume the capability x$1.type since that capability is not a SharedCapability 32 | ?=> (erased x$2: CT[Ex2]^) 33 | ?=> (erased x$3: CT[Ex1]^) diff --git a/tests/neg-custom-args/captures/existential-mapping.check b/tests/neg-custom-args/captures/existential-mapping.check index 974c5c38f74c..37b85fe0edd2 100644 --- a/tests/neg-custom-args/captures/existential-mapping.check +++ b/tests/neg-custom-args/captures/existential-mapping.check @@ -3,87 +3,145 @@ | ^^^^^^^^^^^^^^^^^^^^ | Array[box C^] captures the root capability `cap` in invariant position. | This capability cannot be converted to an existential in the result type of a function. + | + | where: ^ refers to the universal root capability -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:9:25 --------------------------- 9 | val _: (x: C^) -> C = x1 // error | ^^ - | Found: (x1 : (x: C^) -> C^{localcap}) + | Found: (x1 : (x: C^) -> C^²) | Required: (x: C^) -> C | + | where: ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: C^): C^² + | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:12:20 -------------------------- 12 | val _: C^ -> C = x2 // error | ^^ - | Found: (x2 : C^ -> C^{fresh}) + | Found: (x2 : C^ -> C^²) | Required: C^ -> C | + | where: ^ refers to the universal root capability + | ^² refers to a fresh root capability created in value x2 + | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:15:30 -------------------------- 15 | val _: A^ -> (x: C^) -> C = x3 // error | ^^ - | Found: (x3 : A^ -> (x: C^) -> C^{localcap}) - | Required: A^ -> (x: C^) -> C + | Found: (x3 : A^ -> (x: C^) -> C^²) + | Required: A^ -> (x: C^) -> C + | + | where: ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: C^): C^² | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:18:25 -------------------------- 18 | val _: A^ -> C^ -> C = x4 // error | ^^ - | Found: (x4 : A^ -> C^ -> C^{fresh}) + | Found: (x4 : A^ -> C^ -> C^²) | Required: A^ -> C^ -> C | + | where: ^ refers to the universal root capability + | ^² refers to a fresh root capability created in value x4 + | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:21:30 -------------------------- 21 | val _: A^ -> (x: C^) -> C = x5 // error | ^^ - | Found: (x5 : A^ -> (x: C^) -> C^{localcap}) - | Required: A^ -> (x: C^) -> C + | Found: (x5 : A^ -> (x: C^) -> C^²) + | Required: A^ -> (x: C^) -> C + | + | where: ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: C^): C^² | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:24:30 -------------------------- 24 | val _: A^ -> (x: C^) => C = x6 // error | ^^ - | Found: (x6 : A^ -> (x: C^) ->{fresh} C^{localcap}) - | Required: A^ -> (x: C^) ->{fresh} C + | Found: (x6 : A^ -> (x: C^) => C^²) + | Required: A^ -> (x: C^) =>² C + | + | where: => refers to a fresh root capability created in value x6 + | =>² refers to a fresh root capability created in method Test + | ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: C^): C^² | | longer explanation available when compiling with `-explain` -- [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} C^{localcap}) - | Required: (x: C^) ->{fresh} C + | Found: (y1 : (x: C^) => C^²) + | Required: (x: C^) =>² C + | + | where: => refers to a fresh root capability created in value y1 + | =>² refers to a fresh root capability created in method Test + | ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (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} C^{fresh}) - | Required: C^ ->{fresh} C + | Found: (y2 : C^ => C^²) + | Required: C^ =>² C + | + | where: => refers to a fresh root capability created in value y2 + | =>² refers to a fresh root capability created in method Test + | ^ refers to the universal root capability + | ^² refers to a fresh root capability created in value y2 | | 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} (x: C^) ->{fresh} C^{localcap}) - | Required: A^ ->{fresh} (x: C^) ->{fresh} C + | Found: (y3 : A^ => (x: C^) =>² C^²) + | Required: A^ =>³ (x: C^) =>⁴ C + | + | where: => refers to a fresh root capability created in value y3 + | =>² refers to a fresh root capability created in value y3 + | =>³ refers to a fresh root capability created in method Test + | =>⁴ refers to a fresh root capability created in method Test + | ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: C^): 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} C^ ->{fresh} C^{fresh}) - | Required: A^ ->{fresh} C^ ->{fresh} C + | Found: (y4 : A^ => C^ =>² C^²) + | Required: A^ =>³ C^ =>⁴ C + | + | where: => refers to a fresh root capability created in value y4 + | =>² refers to a fresh root capability created in value y4 + | =>³ refers to a fresh root capability created in method Test + | =>⁴ refers to a fresh root capability created in method Test + | ^ refers to the universal root capability + | ^² refers to a fresh root capability created in value y4 | | 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^) -> C^{localcap}) - | Required: A^ ->{fresh} (x: C^) -> C + | Found: (y5 : A^ => (x: C^) -> C^²) + | Required: A^ =>² (x: C^) -> C + | + | where: => refers to a fresh root capability created in value y5 + | =>² refers to a fresh root capability created in method Test + | ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (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} (x: C^) ->{fresh} C^{localcap}) - | Required: A^ ->{fresh} (x: C^) ->{fresh} C + | Found: (y6 : A^ => (x: C^) =>² C^²) + | Required: A^ =>³ (x: C^) =>⁴ C + | + | where: => refers to a fresh root capability created in value y6 + | =>² refers to a fresh root capability created in value y6 + | =>³ refers to a fresh root capability created in method Test + | =>⁴ refers to a fresh root capability created in method Test + | ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: C^): C^² | | 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 index f48736984a36..7678c3314c5d 100644 --- a/tests/neg-custom-args/captures/filevar-expanded.check +++ b/tests/neg-custom-args/captures/filevar-expanded.check @@ -3,7 +3,7 @@ | ^^^ | Separation failure: argument of type (io3 : test2.IO^) | to method withFile: [T](io2: test2.IO^)(op: (f: test2.File^{io2}) => T): T - | corresponds to capture-polymorphic formal parameter io2 of type test2.IO^ + | corresponds to capture-polymorphic formal parameter io2 of type test2.IO^² | and hides capabilities {io3}. | Some of these overlap with the captures of the second argument with type (f: test2.File^{io3}) ->{io3} Unit. | @@ -12,3 +12,6 @@ | Capture set of second argument : {io3} | Footprint set of second argument : {io3} | The two sets overlap at : {io3} + | + | where: ^ refers to a fresh root capability created in method test + | ^² refers to a fresh root capability created in method test diff --git a/tests/neg-custom-args/captures/filevar.check b/tests/neg-custom-args/captures/filevar.check index 22efd36053b4..499084a2b386 100644 --- a/tests/neg-custom-args/captures/filevar.check +++ b/tests/neg-custom-args/captures/filevar.check @@ -3,6 +3,8 @@ | ^ | Mutable variable file cannot have type File^ since | that type captures the root capability `cap`. + | + | where: ^ refers to a fresh root capability created in variable file -- Warning: tests/neg-custom-args/captures/filevar.scala:11:55 --------------------------------------------------------- 11 |def withFile[T](op: (l: caps.Capability) ?-> (f: File^{l}) => T): T = | ^ diff --git a/tests/neg-custom-args/captures/heal-tparam-cs.check b/tests/neg-custom-args/captures/heal-tparam-cs.check index 4cd30d74f4dc..c9dfdec1d378 100644 --- a/tests/neg-custom-args/captures/heal-tparam-cs.check +++ b/tests/neg-custom-args/captures/heal-tparam-cs.check @@ -2,7 +2,11 @@ 10 | val test1 = localCap { c => // error | ^ | Found: (c: Capp^) ->? box () ->{c} Unit - | Required: (c: Capp^) ->{fresh} box () ->? Unit + | Required: (c: Capp^) => box () ->? Unit + | + | where: => refers to a fresh root capability created in value test1 + | ^ refers to the universal root capability + | | | Note that reference c.type | cannot be included in outer capture set ? @@ -14,7 +18,11 @@ 15 | localCap { c => // error | ^ | Found: (x$0: Capp^) ->? () ->{x$0} Unit - | Required: (c: Capp^) -> () ->{localcap} Unit + | Required: (c: Capp^) -> () => Unit + | + | where: => refers to a root capability associated with the result type of (c: Capp^): () => Unit + | ^ refers to the universal root capability + | | | Note that the existential capture root in () => Unit | cannot subsume the capability x$0.type since that capability is not a SharedCapability @@ -37,6 +45,8 @@ | Found: (io: Capp^) ->? () ->{io} Unit | Required: (io: Capp^) -> () -> Unit | + | where: ^ refers to the universal root capability + | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/heal-tparam-cs.scala:44:4 -------------------------------- 44 | io => () => io.use() // error @@ -44,4 +54,6 @@ | Found: (io: Capp^) ->? () ->{io} Unit | Required: (io: Capp^) -> () -> Unit | + | where: ^ refers to the universal root capability + | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i15116.check b/tests/neg-custom-args/captures/i15116.check index 6e222ce1070a..511212769289 100644 --- a/tests/neg-custom-args/captures/i15116.check +++ b/tests/neg-custom-args/captures/i15116.check @@ -4,7 +4,8 @@ | Found: Foo{val m: (Bar.this.m² : String^)}^{Bar.this.m²} | Required: Foo | - | where: m is a value in class Foo + | where: ^ refers to a fresh root capability created in class Bar + | m is a value in class Foo | m² is a value in class Bar | | @@ -21,7 +22,8 @@ | Found: Foo{val m: (Baz.this.m² : String^)}^{Baz.this.m²} | Required: Foo | - | where: m is a value in class Foo + | where: ^ refers to a fresh root capability created in trait Baz + | m is a value in class Foo | m² is a value in trait Baz | | @@ -38,7 +40,8 @@ | Found: Foo{val m: (Bar1.this.m² : String^)}^{Bar1.this.m²} | Required: Foo | - | where: m is a value in class Foo + | where: ^ refers to a fresh root capability created in class Bar1 + | m is a value in class Foo | m² is a value in class Bar1 | | @@ -55,7 +58,8 @@ | Found: Foo{val m: (Baz2.this.m² : String^)}^{Baz2.this.m²} | Required: Foo | - | where: m is a value in class Foo + | where: ^ refers to a fresh root capability created in trait Baz2 + | m is a value in class Foo | m² is a value in trait Baz2 | | diff --git a/tests/neg-custom-args/captures/i15772.check b/tests/neg-custom-args/captures/i15772.check index 98791d729c16..59182d9fcf13 100644 --- a/tests/neg-custom-args/captures/i15772.check +++ b/tests/neg-custom-args/captures/i15772.check @@ -3,17 +3,28 @@ | ^^^^^^^ |C^ => Unit cannot be box-converted to box C{val arg: C^}^{c} ->{cap, c} Unit |since the additional capture set {c} resulting from box conversion is not allowed in box C{val arg: C^}^{c} => Unit + | + |where: => refers to the universal root capability + | ^ refers to the universal root capability + | cap is the universal root capability -- Error: tests/neg-custom-args/captures/i15772.scala:29:35 ------------------------------------------------------------ 29 | val boxed2 : Observe[C^] = box2(c) // error | ^^^^^^^ |C^ => Unit cannot be box-converted to box C{val arg: C^}^{c} ->{cap, c} Unit |since the additional capture set {c} resulting from box conversion is not allowed in box C{val arg: C^}^{c} => Unit + | + |where: => refers to the universal root capability + | ^ refers to the universal root capability + | cap is the universal root capability -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:35:34 --------------------------------------- 35 | val boxed2 : Observe[C]^ = box2(c) // error | ^ | Found: box C^ | Required: box C{val arg: C^?}^? | + | where: ^ refers to a fresh root capability created in method c + | + | | Note that the universal capability `cap` | cannot be included in capture set ? | diff --git a/tests/neg-custom-args/captures/i16114.check b/tests/neg-custom-args/captures/i16114.check index e7ae191dbf14..b83631660b9d 100644 --- a/tests/neg-custom-args/captures/i16114.check +++ b/tests/neg-custom-args/captures/i16114.check @@ -3,23 +3,33 @@ | ^^^^ | Type variable T of method expect cannot be instantiated to box Cap^ since | that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- Error: tests/neg-custom-args/captures/i16114.scala:24:13 ------------------------------------------------------------ 24 | expect[Cap^] { // error | ^^^^ | Type variable T of method expect cannot be instantiated to box Cap^ since | that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- Error: tests/neg-custom-args/captures/i16114.scala:30:13 ------------------------------------------------------------ 30 | expect[Cap^] { // error | ^^^^ | Type variable T of method expect cannot be instantiated to box Cap^ since | that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- Error: tests/neg-custom-args/captures/i16114.scala:36:13 ------------------------------------------------------------ 36 | expect[Cap^](io) // error | ^^^^ | Type variable T of method expect cannot be instantiated to box Cap^ since | that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- Error: tests/neg-custom-args/captures/i16114.scala:39:13 ------------------------------------------------------------ 39 | expect[Cap^] { // error | ^^^^ | Type variable T of method expect cannot be instantiated to box Cap^ since | that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability diff --git a/tests/neg-custom-args/captures/i16226.check b/tests/neg-custom-args/captures/i16226.check index 5a3c3b690f6d..90d23c6e73d4 100644 --- a/tests/neg-custom-args/captures/i16226.check +++ b/tests/neg-custom-args/captures/i16226.check @@ -1,19 +1,28 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i16226.scala:13:4 ---------------------------------------- 13 | (ref1, f1) => map[A, B](ref1, f1) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (ref1: LazyRef[box A^?]{val elem: () ->{cap, fresh} A^?}^{io}, f1: (x$0: A^?) => B^?) ->? - | LazyRef[box B^?]{val elem: () ->{localcap} B^?}^{f1, ref1} - | Required: (ref1: LazyRef[A]^{io}, f1: A => B) ->{fresh} LazyRef[B]^{fresh} + |Found: (ref1: LazyRef[box A^?]{val elem: () => A^?}^{io}, f1: A^? => B^?) ->? LazyRef[box B^?]{val elem: () =>² B^?}^{f1, ref1} + |Required: (ref1: LazyRef[A]^{io}, f1: A => B) =>³ LazyRef[B]^ + | + |where: => refers to the universal root capability + | =>² refers to a root capability associated with the result type of (ref1: LazyRef[box A^?]{val elem: () => A^?}^{io}, f1: A^? => B^?): LazyRef[box B^?]{val elem: () =>² B^?}^{f1, ref1} + | =>³ refers to a fresh root capability created in method mapc + | ^ refers to a fresh root capability created in method mapc | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i16226.scala:15:4 ---------------------------------------- 15 | (ref1, f1) => map[A, B](ref1, f1) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (ref1: LazyRef[box A^?]{val elem: () ->{cap, fresh} A^?}^{io}, f1: (x$0: A^?) => B^?) ->? - | LazyRef[box B^?]{val elem: () ->{localcap} B^?}^{f1, ref1} - | Required: (ref: LazyRef[A]^{io}, f: A => B) ->{fresh} LazyRef[B]^{localcap} + |Found: (ref1: LazyRef[box A^?]{val elem: () => A^?}^{io}, f1: A^? => B^?) ->? LazyRef[box B^?]{val elem: () =>² B^?}^{f1, ref1} + |Required: (ref: LazyRef[A]^{io}, f: A => B) =>³ LazyRef[B]^ + | + |where: => refers to the universal root capability + | =>² refers to a root capability associated with the result type of (ref1: LazyRef[box A^?]{val elem: () => A^?}^{io}, f1: A^? => B^?): LazyRef[box B^?]{val elem: () =>² B^?}^{f1, ref1} + | =>³ refers to a fresh root capability created in method mapd + | ^ refers to a root capability associated with the result type of (ref: LazyRef[A]^{io}, f: A => B): LazyRef[B]^ + | | - | Note that the existential capture root in LazyRef[B]^ - | cannot subsume the capability f1.type since that capability is not a SharedCapability + |Note that the existential capture root in LazyRef[B]^ + |cannot subsume the capability f1.type since that capability is not a SharedCapability | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i19330.check b/tests/neg-custom-args/captures/i19330.check index 91ba7f234845..eb02397ba193 100644 --- a/tests/neg-custom-args/captures/i19330.check +++ b/tests/neg-custom-args/captures/i19330.check @@ -14,7 +14,10 @@ 22 | val bad: bar.T = foo(bar) // error | ^^^^^^^^ | Found: bar.T - | Required: () ->{fresh} Logger^{fresh} + | Required: () => Logger^ + | + | where: => refers to a fresh root capability created in value bad + | ^ refers to a fresh root capability created in value bad | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/i19330.scala:16:14 ------------------------------------------------------------ diff --git a/tests/neg-custom-args/captures/i21313.check b/tests/neg-custom-args/captures/i21313.check index f087c1f86d63..245e38db8e51 100644 --- a/tests/neg-custom-args/captures/i21313.check +++ b/tests/neg-custom-args/captures/i21313.check @@ -8,4 +8,6 @@ | Found: (src2 : Source[Int, scala.caps.CapSet^{ac2}]^{}) | Required: Source[Int, scala.caps.CapSet^{ac1}]^ | + | where: ^ refers to a fresh root capability created in method test + | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i21401.check b/tests/neg-custom-args/captures/i21401.check index 8eade64e372f..9f026ae33324 100644 --- a/tests/neg-custom-args/captures/i21401.check +++ b/tests/neg-custom-args/captures/i21401.check @@ -3,16 +3,24 @@ | ^^^ | Type variable T of object Boxed cannot be instantiated to box IO^ since | that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- Error: tests/neg-custom-args/captures/i21401.scala:15:18 ------------------------------------------------------------ 15 | val a = usingIO[IO^](x => x) // error // error | ^^^ | Type variable R of method usingIO cannot be instantiated to box IO^ since | that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21401.scala:15:23 --------------------------------------- 15 | val a = usingIO[IO^](x => x) // error // error | ^^^^^^ | Found: (x: IO^) ->? box IO^{x} - | Required: (x: IO^) ->{fresh} box IO^{fresh} + | Required: (x: IO^) => box IO^² + | + | where: => refers to a fresh root capability created in value a + | ^ refers to the universal root capability + | ^² refers to a fresh root capability created in value a | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/i21401.scala:16:66 ------------------------------------------------------------ @@ -20,13 +28,19 @@ | ^^^ | Type variable R of method usingIO cannot be instantiated to [R, X <: Boxed[box IO^] -> R] => (op: X) -> R since | the part box IO^ of that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- Error: tests/neg-custom-args/captures/i21401.scala:17:29 ------------------------------------------------------------ 17 | val x: Boxed[IO^] = leaked[Boxed[IO^], Boxed[IO^] -> Boxed[IO^]](x => x) // error // error | ^^^^^^^^^^ | Type variable R of value leaked cannot be instantiated to Boxed[box IO^] since | the part box IO^ of that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- Error: tests/neg-custom-args/captures/i21401.scala:17:52 ------------------------------------------------------------ 17 | val x: Boxed[IO^] = leaked[Boxed[IO^], Boxed[IO^] -> Boxed[IO^]](x => x) // error // error | ^^^^^^^^^^^^^^^^^^^^^^^^ | Type variable X of value leaked cannot be instantiated to Boxed[box IO^] -> Boxed[box IO^] since | the part box IO^ of that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability diff --git a/tests/neg-custom-args/captures/i21614.check b/tests/neg-custom-args/captures/i21614.check index e8d4c9f03102..7f2a0e5f5810 100644 --- a/tests/neg-custom-args/captures/i21614.check +++ b/tests/neg-custom-args/captures/i21614.check @@ -1,17 +1,23 @@ -- [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) - | Required: File^ + | Found: (f : F) + | Required: File^ + | + | where: ^ refers to a fresh root capability created in anonymous fucntion of type (f²: F): Logger | | 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*} box Logger{val f: File^{_$1}}^{cap.rd, _$1} - | Required: (_$1: box File^{files*}) => box Logger{val f: File^?}^? + |Found: (_$1: box File^{files*}) ->{files*} box Logger{val f: File^{_$1}}^{cap.rd, _$1} + |Required: (_$1: box File^{files*}) => box Logger{val f: File^?}^? + | + |where: => refers to a fresh root capability created in method mkLoggers2 + | cap is a root capability associated with the result type of (_$1: box File^{files*}): box Logger{val f: File^{_$1}}^{cap.rd, _$1} + | | - | Note that reference .rd - | cannot be included in outer capture set ? + |Note that reference .rd + |cannot be included in outer capture set ? | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i21920.check b/tests/neg-custom-args/captures/i21920.check index 70efe6990d4c..72f92bd6d531 100644 --- a/tests/neg-custom-args/captures/i21920.check +++ b/tests/neg-custom-args/captures/i21920.check @@ -1,10 +1,14 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21920.scala:34:35 --------------------------------------- 34 | val cell: Cell[File] = File.open(f => Cell(() => Seq(f))) // error | ^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (f: File^?) ->? box Cell[box File^?]{val head: () ->? IterableOnce[box File^?]^?}^? - | Required: (f: File^) ->{fresh} box Cell[box File^?]{val head: () ->? IterableOnce[box File^?]^?}^? + | Found: (f: File^?) ->? box Cell[box File^?]{val head: () ->? IterableOnce[box File^?]^?}^? + | Required: (f: File^) => box Cell[box File^?]{val head: () ->? IterableOnce[box File^?]^?}^? | - | Note that the universal capability `cap` - | cannot be included in capture set ? + | where: => refers to a fresh root capability created in value cell + | ^ refers to the universal root capability + | + | + | 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/lazylists-exceptions.check b/tests/neg-custom-args/captures/lazylists-exceptions.check index bdd053910ac8..7f942ed489e4 100644 --- a/tests/neg-custom-args/captures/lazylists-exceptions.check +++ b/tests/neg-custom-args/captures/lazylists-exceptions.check @@ -4,6 +4,8 @@ | The result of `try` cannot have type LazyList[Int]^{cap.rd} since | that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. + | + | where: cap is a fresh root capability created in method problem 37 | tabulate(10) { i => 38 | if i > 9 then throw Ex1() 39 | i * i diff --git a/tests/neg-custom-args/captures/lazyref.check b/tests/neg-custom-args/captures/lazyref.check index 8db8791d1123..55a6d4d2575f 100644 --- a/tests/neg-custom-args/captures/lazyref.check +++ b/tests/neg-custom-args/captures/lazyref.check @@ -32,39 +32,53 @@ | Separation failure: Illegal access to {LazyRef.this.elem} which is hidden by the previous definition | of value get with type () => T. | This type hides capabilities {LazyRef.this.elem} + | + | where: => refers to a fresh root capability created in value get -- Error: tests/neg-custom-args/captures/lazyref.scala:23:13 ----------------------------------------------------------- 23 | val ref3 = ref1.map(g) // error: separation failure | ^^^^ | Separation failure: Illegal access to {cap1} which is hidden by the previous definition | of value ref2 with type LazyRef[Int]{val elem: () => Int}^{cap2, ref1}. | This type hides capabilities {cap1} + | + | where: => refers to a fresh root capability created in class LazyRef -- Error: tests/neg-custom-args/captures/lazyref.scala:26:9 ------------------------------------------------------------ 26 | if cap1 == cap2 // error: separation failure // error: separation failure | ^^^^ | Separation failure: Illegal access to {cap1} which is hidden by the previous definition | of value ref3 with type LazyRef[Int]{val elem: () => Int}^{cap2, ref1}. | This type hides capabilities {ref2*, cap1} + | + | where: => refers to a fresh root capability created in class LazyRef -- Error: tests/neg-custom-args/captures/lazyref.scala:26:17 ----------------------------------------------------------- 26 | if cap1 == cap2 // error: separation failure // error: separation failure | ^^^^ | Separation failure: Illegal access to {cap2} which is hidden by the previous definition | of value ref3 with type LazyRef[Int]{val elem: () => Int}^{cap2, ref1}. | This type hides capabilities {ref2*, cap1} + | + | where: => refers to a fresh root capability created in class LazyRef -- Error: tests/neg-custom-args/captures/lazyref.scala:27:11 ----------------------------------------------------------- 27 | then ref1 // error: separation failure | ^^^^ | Separation failure: Illegal access to {cap1, ref1} which is hidden by the previous definition | of value ref3 with type LazyRef[Int]{val elem: () => Int}^{cap2, ref1}. | This type hides capabilities {ref2*, cap1} + | + | where: => refers to a fresh root capability created in class LazyRef -- Error: tests/neg-custom-args/captures/lazyref.scala:28:11 ----------------------------------------------------------- 28 | else ref2) // error: separation failure | ^^^^ | Separation failure: Illegal access to {cap1, cap2, ref1} which is hidden by the previous definition | of value ref3 with type LazyRef[Int]{val elem: () => Int}^{cap2, ref1}. | This type hides capabilities {ref2*, cap1} + | + | where: => refers to a fresh root capability created in class LazyRef -- Error: tests/neg-custom-args/captures/lazyref.scala:29:9 ------------------------------------------------------------ 29 | .map(g) // error: separation failure | ^ | Separation failure: Illegal access to {cap2} which is hidden by the previous definition | of value ref3 with type LazyRef[Int]{val elem: () => Int}^{cap2, ref1}. | This type hides capabilities {ref2*, cap1} + | + | where: => refers to a fresh root capability created in class LazyRef diff --git a/tests/neg-custom-args/captures/leaking-iterators.check b/tests/neg-custom-args/captures/leaking-iterators.check index 54a8d6ccea9e..57540903ab82 100644 --- a/tests/neg-custom-args/captures/leaking-iterators.check +++ b/tests/neg-custom-args/captures/leaking-iterators.check @@ -2,7 +2,11 @@ 56 | usingLogFile: log => // error | ^ | Found: (log: java.io.FileOutputStream^) ->? box cctest.Iterator[Int]^{log} - | Required: (log: java.io.FileOutputStream^) ->{fresh} box cctest.Iterator[Int]^? + | Required: (log: java.io.FileOutputStream^) => box cctest.Iterator[Int]^? + | + | where: => refers to a fresh root capability created in method test + | ^ refers to the universal root capability + | | | Note that reference log.type | cannot be included in outer capture set ? diff --git a/tests/neg-custom-args/captures/levels.check b/tests/neg-custom-args/captures/levels.check index 7ef29c36efc3..dd195c9085aa 100644 --- a/tests/neg-custom-args/captures/levels.check +++ b/tests/neg-custom-args/captures/levels.check @@ -3,6 +3,8 @@ | ^^^^^^^^^^^^^^^^ | Type variable T of constructor Ref cannot be instantiated to box String => String since | that type captures the root capability `cap`. + | + | where: => refers to the universal root capability -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/levels.scala:22:11 --------------------------------------- 22 | r.setV(g) // error | ^ diff --git a/tests/neg-custom-args/captures/linear-buffer-2.check b/tests/neg-custom-args/captures/linear-buffer-2.check index 3d64c432d116..1038a98a1de1 100644 --- a/tests/neg-custom-args/captures/linear-buffer-2.check +++ b/tests/neg-custom-args/captures/linear-buffer-2.check @@ -4,26 +4,36 @@ | 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} + | + | where: ^ refers to a fresh root capability created in value buf1 -- 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. + | + | where: ^ refers to a fresh root capability created in value buf1 -- 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. + | + | where: ^ refers to a fresh root capability created in value buf1 -- 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. + | + | where: ^ refers to a fresh root capability created in value buf1 -- 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. + | + | where: ^ refers to a fresh root capability created in method Test5 diff --git a/tests/neg-custom-args/captures/linear-buffer.check b/tests/neg-custom-args/captures/linear-buffer.check index 55af88406356..153c23321091 100644 --- a/tests/neg-custom-args/captures/linear-buffer.check +++ b/tests/neg-custom-args/captures/linear-buffer.check @@ -19,26 +19,36 @@ | 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. + | + | where: ^ refers to a fresh root capability created in method Test -- 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. + | + | where: ^ refers to a fresh root capability created in value buf1 -- 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. + | + | where: ^ refers to a fresh root capability created in value buf1 -- 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. + | + | where: ^ refers to a fresh root capability created in value buf1 -- 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. + | + | where: ^ refers to a fresh root capability created in method Test5 diff --git a/tests/neg-custom-args/captures/outer-var.check b/tests/neg-custom-args/captures/outer-var.check index a49722eb4bb9..ef364c7f5395 100644 --- a/tests/neg-custom-args/captures/outer-var.check +++ b/tests/neg-custom-args/captures/outer-var.check @@ -4,7 +4,8 @@ | Found: (q : () => Unit) | Required: () ->{p, q²} Unit | - | where: q is a parameter in method inner + | where: => refers to a fresh root capability created in method inner + | q is a parameter in method inner | q² is a parameter in method test | | longer explanation available when compiling with `-explain` @@ -14,6 +15,8 @@ | Found: () => Unit | Required: () ->{p, q} Unit | + | where: => refers to a fresh root capability created in method inner + | | 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 @@ -21,6 +24,8 @@ | Found: () => Unit | Required: () ->{p} Unit | + | where: => refers to a fresh root capability created in method inner + | | 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 @@ -28,9 +33,13 @@ | Found: (q : () => Unit) | Required: () ->{p} Unit | + | where: => refers to a fresh root capability created in method inner + | | 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 | ^^^^^^^^^^ | Type variable A of object ListBuffer cannot be instantiated to box () => Unit since | that type captures the root capability `cap`. + | + | where: => refers to the universal root capability diff --git a/tests/neg-custom-args/captures/reaches.check b/tests/neg-custom-args/captures/reaches.check index 8dc433afc979..837571a98473 100644 --- a/tests/neg-custom-args/captures/reaches.check +++ b/tests/neg-custom-args/captures/reaches.check @@ -2,7 +2,10 @@ 22 | usingFile: f => // error | ^ | Found: (f: File^?) ->? Unit - | Required: (f: File^) ->{fresh} Unit + | Required: (f: File^) => Unit + | + | where: => refers to a fresh root capability created in method runAll0 + | ^ refers to the universal root capability 23 | cur = (() => f.write()) :: Nil | | longer explanation available when compiling with `-explain` @@ -10,7 +13,10 @@ 32 | usingFile: f => // error | ^ | Found: (f: File^?) ->? Unit - | Required: (f: File^) ->{fresh} Unit + | Required: (f: File^) => Unit + | + | where: => refers to a fresh root capability created in method runAll1 + | ^ refers to the universal root capability 33 | cur.set: 34 | (() => f.write()) :: Nil | @@ -20,23 +26,33 @@ | ^ | Mutable variable cur cannot have type List[box () => Unit] since | the part box () => Unit of that type captures the root capability `cap`. + | + | where: => refers to a fresh root capability created in variable cur -- Error: tests/neg-custom-args/captures/reaches.scala:44:16 ----------------------------------------------------------- 44 | 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`. + | + | where: => refers to the universal root capability -- [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 + | Required: () =>² Unit + | + | where: => refers to the universal root capability + | =>² refers to a fresh root capability created in value next | | 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] + | Required: List[box () =>² Unit] + | + | where: => refers to the universal root capability + | =>² refers to a fresh root capability created in method runAll3 | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/reaches.scala:54:51 ----------------------------------------------------------- @@ -44,18 +60,26 @@ | ^ | Type variable A of constructor Id cannot be instantiated to box () => Unit since | that type captures the root capability `cap`. + | + | where: => refers to the universal root capability -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:59:27 -------------------------------------- 59 | val id: File^ -> File^ = x => x // error | ^^^^^^ | Found: (x: File^) ->? File^{x} - | Required: (x: File^) -> File^{fresh} + | Required: (x: File^) -> File^² + | + | where: ^ refers to the universal root capability + | ^² refers to a fresh root capability created in value id | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:62:38 -------------------------------------- 62 | val leaked = usingFile[File^{id*}]: f => // error // error | ^ | Found: (f: File^?) ->? box File^? - | Required: (f: File^) ->{fresh} box File^{id*} + | Required: (f: File^) => box File^{id*} + | + | where: => refers to a fresh root capability created in value leaked + | ^ refers to the universal root capability 63 | val f1: File^{id*} = id(f) 64 | f1 | @@ -63,18 +87,25 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:67:32 -------------------------------------- 67 | val id: (x: File^) -> File^ = x => x // error | ^^^^^^ - | Found: (x: File^) ->? File^{x} - | Required: (x: File^) -> File^{localcap} + | Found: (x: File^) ->? File^{x} + | Required: (x: File^) -> File^² + | + | where: ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: File^): File^² | - | Note that the existential capture root in File^ - | cannot subsume the capability x.type since that capability is not a SharedCapability + | + | Note that the existential capture root in File^ + | cannot subsume the capability x.type since that capability is not a SharedCapability | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:70:38 -------------------------------------- 70 | val leaked = usingFile[File^{id*}]: f => // error // error | ^ | Found: (f: File^?) ->? box File^? - | Required: (f: File^) ->{fresh} box File^{id*} + | Required: (f: File^) => box File^{id*} + | + | where: => refers to a fresh root capability created in value leaked + | ^ refers to the universal root capability 71 | val f1: File^{id*} = id(f) 72 | f1 | @@ -87,8 +118,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:91:10 -------------------------------------- 91 | ps.map((x, y) => compose1(x, y)) // error | ^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (x$1: (box (x$0: A^) ->? A^?, box (x$0: A^) ->? A^?)^?) ->? box (x$0: A^?) ->? A^? - | Required: (x$1: (box A ->{ps*} A, box A ->{ps*} A)) ->{fresh} box (x$0: A^?) ->? A^? + | Found: (x$1: (box A^ ->? A^?, box A^ ->? A^?)^?) ->? box A^? ->? A^? + | Required: (x$1: (box A ->{ps*} A, box A ->{ps*} A)) => box A^? ->? A^? + | + | where: => refers to a fresh root capability created in method mapCompose2 + | ^ refers to the universal root capability | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/reaches.scala:62:31 ----------------------------------------------------------- diff --git a/tests/neg-custom-args/captures/reaches2.check b/tests/neg-custom-args/captures/reaches2.check index e9238365ec18..13d305189c3e 100644 --- a/tests/neg-custom-args/captures/reaches2.check +++ b/tests/neg-custom-args/captures/reaches2.check @@ -11,14 +11,16 @@ -- Error: tests/neg-custom-args/captures/reaches2.scala:10:28 ---------------------------------------------------------- 10 | ps.map((x, y) => compose1(x, y)) // error // error // error | ^ - | Separation failure: argument of type A ->{x} A - | to method compose1: [A, B, C](f: A => B, g: B => C): A ->{f, g} C - | corresponds to capture-polymorphic formal parameter f of type box A^? => box A^? - | and hides capabilities {x}. - | Some of these overlap with the captures of the second argument with type A ->{y} A. + |Separation failure: argument of type A ->{x} A + |to method compose1: [A, B, C](f: A => B, g: B => C): A ->{f, g} C + |corresponds to capture-polymorphic formal parameter f of type box A^? => box A^? + |and hides capabilities {x}. + |Some of these overlap with the captures of the second argument with type A ->{y} A. | - | Hidden set of current argument : {x} - | Hidden footprint of current argument : {x, ps*} - | Capture set of second argument : {y} - | Footprint set of second argument : {y, ps*} - | The two sets overlap at : {ps*} + | Hidden set of current argument : {x} + | Hidden footprint of current argument : {x, ps*} + | Capture set of second argument : {y} + | Footprint set of second argument : {y, ps*} + | The two sets overlap at : {ps*} + | + |where: => refers to a fresh root capability created in anonymous fucntion of type (x$1: (box A^? ->{ps*} A^?, box A^? ->{ps*} A^?)^?): A^? ->{ps*} A^? diff --git a/tests/neg-custom-args/captures/real-try.check b/tests/neg-custom-args/captures/real-try.check index 6b478b48515a..c430772a2a0c 100644 --- a/tests/neg-custom-args/captures/real-try.check +++ b/tests/neg-custom-args/captures/real-try.check @@ -10,6 +10,8 @@ | The result of `try` cannot have type () ->{cap.rd} 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. + | + | where: cap is a fresh root capability created in method test 15 | () => foo(1) 16 | catch 17 | case _: Ex1 => ??? @@ -20,6 +22,8 @@ | The result of `try` cannot have type () ->{cap.rd} 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. + | + | where: cap is a fresh root capability created in value x 21 | () => foo(1) 22 | catch 23 | case _: Ex1 => ??? @@ -30,6 +34,8 @@ | The result of `try` cannot have type () ->{cap.rd} 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. + | + | where: cap is a fresh root capability created in value y 27 | () => Cell(foo(1)) 28 | catch 29 | case _: Ex1 => ??? @@ -40,6 +46,8 @@ | 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`. | This is often caused by a locally generated exception capability leaking as part of its result. + | + | where: cap is a fresh root capability created in value b 33 | Cell(() => foo(1)) 34 | catch 35 | case _: Ex1 => ??? diff --git a/tests/neg-custom-args/captures/sep-box.check b/tests/neg-custom-args/captures/sep-box.check index f60f09d906a8..b1752f0ee582 100644 --- a/tests/neg-custom-args/captures/sep-box.check +++ b/tests/neg-custom-args/captures/sep-box.check @@ -12,3 +12,5 @@ | Capture set of second argument : {h2.value*} | Footprint set of second argument : {h2.value*, xs*} | The two sets overlap at : {xs*} + | + | where: ^ refers to a fresh root capability created in method test diff --git a/tests/neg-custom-args/captures/sep-compose.check b/tests/neg-custom-args/captures/sep-compose.check index 3bd1e5b08eeb..fb651c9b2904 100644 --- a/tests/neg-custom-args/captures/sep-compose.check +++ b/tests/neg-custom-args/captures/sep-compose.check @@ -12,6 +12,8 @@ | Capture set of second argument : {f} | Footprint set of second argument : {f, a, io} | The two sets overlap at : {f, a, io} + | + | where: => refers to a fresh root capability created in method test -- Error: tests/neg-custom-args/captures/sep-compose.scala:33:10 ------------------------------------------------------- 33 | seq4(f)(f) // error | ^ @@ -26,6 +28,8 @@ | Capture set of first argument : {f} | Footprint set of first argument : {f, a, io} | The two sets overlap at : {f, a, io} + | + | where: => refers to a fresh root capability created in method test -- Error: tests/neg-custom-args/captures/sep-compose.scala:34:7 -------------------------------------------------------- 34 | seq5(f)(f) // error | ^ @@ -40,6 +44,8 @@ | Capture set of second argument : {f} | Footprint set of second argument : {f, a, io} | The two sets overlap at : {f, a, io} + | + | where: => refers to a fresh root capability created in method test -- Error: tests/neg-custom-args/captures/sep-compose.scala:35:7 -------------------------------------------------------- 35 | seq6(f, f) // error | ^ @@ -54,6 +60,8 @@ | Capture set of second argument : {f} | Footprint set of second argument : {f, a, io} | The two sets overlap at : {f, a, io} + | + | where: => refers to a fresh root capability created in method test -- Error: tests/neg-custom-args/captures/sep-compose.scala:36:10 ------------------------------------------------------- 36 | seq7(f, f) // error | ^ @@ -68,6 +76,8 @@ | Capture set of first argument : {f} | Footprint set of first argument : {f, a, io} | The two sets overlap at : {f, a, io} + | + | where: => refers to a fresh root capability created in method test -- Error: tests/neg-custom-args/captures/sep-compose.scala:37:7 -------------------------------------------------------- 37 | seq8(f)(f) // error | ^ @@ -82,6 +92,8 @@ | Capture set of second argument : {f} | Footprint set of second argument : {f, a, io} | The two sets overlap at : {f, a, io} + | + | where: => refers to a fresh root capability created in method test -- Error: tests/neg-custom-args/captures/sep-compose.scala:40:5 -------------------------------------------------------- 40 | p1(f) // error | ^ @@ -96,17 +108,21 @@ | Capture set of function prefix : {p1} | Footprint set of function prefix : {p1, f, a, io} | The two sets overlap at : {f, a, io} + | + | where: => refers to a fresh root capability created in method test -- 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 hides capabilities {f}. - | Some of these overlap with the captures of the second argument with type (x : () ->{a} Unit). - | - | Hidden set of current argument : {f} - | Hidden footprint of current argument : {f, a, io} - | Capture set of second argument : {x} - | Footprint set of second argument : {x, a, io} - | The two sets overlap at : {a, io} + | 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 hides capabilities {f}. + | Some of these overlap with the captures of the second argument with type (x : () ->{a} Unit). + | + | Hidden set of current argument : {f} + | Hidden footprint of current argument : {f, a, io} + | Capture set of second argument : {x} + | Footprint set of second argument : {x, a, io} + | The two sets overlap at : {a, io} + | + | where: => refers to a fresh root capability created in anonymous fucntion of type (x²: () ->{a} Unit): Unit diff --git a/tests/neg-custom-args/captures/sep-consume.check b/tests/neg-custom-args/captures/sep-consume.check index d8ee2c5ed43e..1add76a54b50 100644 --- a/tests/neg-custom-args/captures/sep-consume.check +++ b/tests/neg-custom-args/captures/sep-consume.check @@ -4,12 +4,16 @@ | Separation failure: Illegal access to (x : Ref^), 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. + | + | where: ^ refers to a fresh root capability created in method test2 -- Error: tests/neg-custom-args/captures/sep-consume.scala:21:16 ------------------------------------------------------- 21 | par(rx, () => x.put(42)) // error | ^ | Separation failure: Illegal access to (x : Ref^), 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. + | + | where: ^ refers to a fresh root capability created in method test2 -- Error: tests/neg-custom-args/captures/sep-consume.scala:26:16 ------------------------------------------------------- 26 | def foo = bad(f) // error | ^ diff --git a/tests/neg-custom-args/captures/sep-counter.check b/tests/neg-custom-args/captures/sep-counter.check index 230c1e6f6e53..cfee7e97b914 100644 --- a/tests/neg-custom-args/captures/sep-counter.check +++ b/tests/neg-custom-args/captures/sep-counter.check @@ -1,7 +1,11 @@ -- Error: tests/neg-custom-args/captures/sep-counter.scala:12:19 ------------------------------------------------------- 12 | def mkCounter(): Pair[Ref^, Ref^] = // error | ^^^^^^^^^^^^^^^^ - | Separation failure in method mkCounter's result type Pair[box Ref^, box Ref^]. + | Separation failure in method mkCounter's result type Pair[box Ref^, box Ref^²]. | One part, box Ref^, hides capabilities {cap}. - | Another part, box Ref^, captures capabilities {cap}. + | Another part, box Ref^², captures capabilities {cap}. | The two sets overlap at cap of value c. + | + | where: ^ refers to a fresh root capability created in method mkCounter + | ^² refers to a fresh root capability created in method mkCounter + | cap is a fresh root capability created in value c diff --git a/tests/neg-custom-args/captures/sep-curried.check b/tests/neg-custom-args/captures/sep-curried.check index edf359c4be14..2ce6cf6bb8b6 100644 --- a/tests/neg-custom-args/captures/sep-curried.check +++ b/tests/neg-custom-args/captures/sep-curried.check @@ -4,13 +4,15 @@ | Found: (y: Ref[Int]^) ->{a} Unit | Required: (y: Ref[Int]^{a}) ->{a} Unit | + | where: ^ refers to the universal root capability + | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/sep-curried.scala:16:6 -------------------------------------------------------- 16 | foo(a)(a) // error | ^ | Separation failure: argument of type (a : Ref[Int]^) | to method foo: (x: Ref[Int]^)(y: Ref[Int]^{a}): Unit - | corresponds to capture-polymorphic formal parameter x of type Ref[Int]^ + | corresponds to capture-polymorphic formal parameter x of type Ref[Int]^² | and hides capabilities {a}. | Some of these overlap with the captures of the second argument with type (a : Ref[Int]^). | @@ -19,12 +21,15 @@ | Capture set of second argument : {a} | Footprint set of second argument : {a} | The two sets overlap at : {a} + | + | where: ^ refers to a fresh root capability created in value a + | ^² refers to a fresh root capability created in method test0 -- Error: tests/neg-custom-args/captures/sep-curried.scala:22:44 ------------------------------------------------------- 22 | val f: (y: Ref[Int]^{a}) ->{a} Unit = foo(a) // error | ^ | Separation failure: argument of type (a : Ref[Int]^) | to a function of type (x: Ref[Int]^) -> (y: Ref[Int]^{a}) ->{x} Unit - | corresponds to capture-polymorphic formal parameter x of type Ref[Int]^ + | corresponds to capture-polymorphic formal parameter x of type Ref[Int]^² | and hides capabilities {a}. | Some of these overlap with the captures of the function result with type (y: Ref[Int]^{a}) ->{a} Unit. | @@ -33,12 +38,15 @@ | Capture set of function result : {a} | Footprint set of function result : {a} | The two sets overlap at : {a} + | + | where: ^ refers to a fresh root capability created in value a + | ^² refers to a fresh root capability created in value f -- Error: tests/neg-custom-args/captures/sep-curried.scala:29:6 -------------------------------------------------------- 29 | foo(a)(a) // error | ^ | Separation failure: argument of type (a : Ref[Int]^) | to a function of type (x: Ref[Int]^) -> (y: Ref[Int]^{a}) ->{x} Unit - | corresponds to capture-polymorphic formal parameter x of type Ref[Int]^ + | corresponds to capture-polymorphic formal parameter x of type Ref[Int]^² | and hides capabilities {a}. | Some of these overlap with the captures of the function result with type (y: Ref[Int]^{a}) ->{a} Unit. | @@ -47,12 +55,15 @@ | Capture set of function result : {a} | Footprint set of function result : {a} | The two sets overlap at : {a} + | + | where: ^ refers to a fresh root capability created in value a + | ^² refers to a fresh root capability created in method test2 -- Error: tests/neg-custom-args/captures/sep-curried.scala:35:9 -------------------------------------------------------- 35 | foo(a)(a) // error | ^ | Separation failure: argument of type (a : Ref[Int]^) | to a function of type (y: Ref[Int]^) ->{a} Unit - | corresponds to capture-polymorphic formal parameter y of type Ref[Int]^ + | corresponds to capture-polymorphic formal parameter y of type Ref[Int]^² | and hides capabilities {a}. | Some of these overlap with the captures of the function prefix. | @@ -61,12 +72,15 @@ | Capture set of function prefix : {a} | Footprint set of function prefix : {a} | The two sets overlap at : {a} + | + | where: ^ refers to a fresh root capability created in value a + | ^² refers to a fresh root capability created in method test3 -- Error: tests/neg-custom-args/captures/sep-curried.scala:42:4 -------------------------------------------------------- 42 | f(a) // error | ^ | Separation failure: argument of type (a : Ref[Int]^) | to a function of type (y: Ref[Int]^) ->{a} Unit - | corresponds to capture-polymorphic formal parameter y of type Ref[Int]^ + | corresponds to capture-polymorphic formal parameter y of type Ref[Int]^² | and hides capabilities {a}. | Some of these overlap with the captures of the function prefix. | @@ -75,3 +89,6 @@ | Capture set of function prefix : {f} | Footprint set of function prefix : {f, a} | The two sets overlap at : {a} + | + | where: ^ refers to a fresh root capability created in value a + | ^² refers to a fresh root capability created in method test4 diff --git a/tests/neg-custom-args/captures/sep-list.check b/tests/neg-custom-args/captures/sep-list.check index 86d4937677e8..c2854f7801c3 100644 --- a/tests/neg-custom-args/captures/sep-list.check +++ b/tests/neg-custom-args/captures/sep-list.check @@ -12,3 +12,5 @@ | Capture set of second argument : {h2} | Footprint set of second argument : {h2, xs*} | The two sets overlap at : {xs*} + | + | where: ^ refers to a fresh root capability created in method test diff --git a/tests/neg-custom-args/captures/sep-pairs.check b/tests/neg-custom-args/captures/sep-pairs.check index fd3e00c138bf..dc59a1ede338 100644 --- a/tests/neg-custom-args/captures/sep-pairs.check +++ b/tests/neg-custom-args/captures/sep-pairs.check @@ -4,6 +4,9 @@ | Found: box Ref^ | Required: box Ref^? | + | where: ^ refers to a fresh root capability created in method twoRefsBad + | + | | Note that the universal capability `cap` | cannot be included in capture set ? | @@ -14,6 +17,9 @@ | Found: box Ref^ | Required: box Ref^? | + | where: ^ refers to a fresh root capability created in method twoRefsBad + | + | | Note that the universal capability `cap` | cannot be included in capture set ? | @@ -21,21 +27,32 @@ -- Error: tests/neg-custom-args/captures/sep-pairs.scala:15:10 --------------------------------------------------------- 15 | val r1: Pair[Ref^, Ref^] = mkPair(r0) // error: overlap at r0 | ^^^^^^^^^^^^^^^^ - | Separation failure in value r1's type Pair[box Ref^, box Ref^]. + | Separation failure in value r1's type Pair[box Ref^, box Ref^²]. | One part, box Ref^, hides capabilities {r0}. - | Another part, box Ref^, captures capabilities {r0}. + | Another part, box Ref^², captures capabilities {r0}. | The two sets overlap at {r0}. + | + | where: ^ refers to a fresh root capability created in value r1 + | ^² refers to a fresh root capability created in value r1 -- Error: tests/neg-custom-args/captures/sep-pairs.scala:13:9 ---------------------------------------------------------- 13 |def bad: Pair[Ref^, Ref^] = // error: overlap at r1*, r0 | ^^^^^^^^^^^^^^^^ - | Separation failure in method bad's result type Pair[box Ref^, box Ref^]. - | One part, box Ref^, hides capabilities {cap, cap, r1*, r0}. - | Another part, box Ref^, captures capabilities {cap, cap, r1*, r0}. + | Separation failure in method bad's result type Pair[box Ref^, box Ref^²]. + | One part, box Ref^, hides capabilities {cap, cap², r1*, r0}. + | Another part, box Ref^², captures capabilities {cap, cap², r1*, r0}. | The two sets overlap at {r1*, r0}. + | + | where: ^ refers to a fresh root capability created in method bad + | ^² refers to a fresh root capability created in method bad + | cap is a fresh root capability created in value r1 + | cap² is a fresh root capability created in value r1 -- Error: tests/neg-custom-args/captures/sep-pairs.scala:44:18 --------------------------------------------------------- 44 | val sameToPair: Pair[Ref^, Ref^] = Pair(fstSame, sndSame) // error | ^^^^^^^^^^^^^^^^ - | Separation failure in value sameToPair's type Pair[box Ref^, box Ref^]. + | Separation failure in value sameToPair's type Pair[box Ref^, box Ref^²]. | One part, box Ref^, hides capabilities {fstSame}. - | Another part, box Ref^, captures capabilities {sndSame}. + | Another part, box Ref^², captures capabilities {sndSame}. | The two sets overlap at cap of value same. + | + | where: ^ refers to a fresh root capability created in value sameToPair + | ^² refers to a fresh root capability created in value sameToPair diff --git a/tests/neg-custom-args/captures/sep-use.check b/tests/neg-custom-args/captures/sep-use.check index 64e2bd7800bc..c52d1598be30 100644 --- a/tests/neg-custom-args/captures/sep-use.check +++ b/tests/neg-custom-args/captures/sep-use.check @@ -4,6 +4,8 @@ | Separation failure: Illegal access to {io} which is hidden by the previous definition | of value x with type () => Unit. | This type hides capabilities {io} + | + | where: => refers to a fresh root capability created in value x -- Error: tests/neg-custom-args/captures/sep-use.scala:12:12 ----------------------------------------------------------- 12 | def x: () => Unit = () => println(io) // error | ^^^^^^^^^^ @@ -14,6 +16,8 @@ | 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} + | + | where: => refers to a fresh root capability created in method x -- Error: tests/neg-custom-args/captures/sep-use.scala:18:10 ----------------------------------------------------------- 18 | def xx: (y: Int) => Unit = _ => println(io) // error | ^^^^^^^^^^^^^^^^ @@ -24,6 +28,8 @@ | 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} + | + | where: => refers to a fresh root capability created in method xx -- Error: tests/neg-custom-args/captures/sep-use.scala:24:19 ----------------------------------------------------------- 24 | def xxx(y: Int): Object^ = io // error | ^^^^^^^ @@ -34,3 +40,5 @@ | 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} + | + | where: ^ refers to a fresh root capability created in method xxx diff --git a/tests/neg-custom-args/captures/sep-use2.check b/tests/neg-custom-args/captures/sep-use2.check index a8e3aa884fd5..d3a22850e4f9 100644 --- a/tests/neg-custom-args/captures/sep-use2.check +++ b/tests/neg-custom-args/captures/sep-use2.check @@ -8,6 +8,8 @@ | Separation failure: Illegal access to {c} which is hidden by the previous definition | of method cc with result type Object^. | This type hides capabilities {c} + | + | where: ^ refers to a fresh root capability created in method cc -- Error: tests/neg-custom-args/captures/sep-use2.scala:12:10 ---------------------------------------------------------- 12 | val x4: Object^ = // error | ^^^^^^^ @@ -23,12 +25,14 @@ | Separation failure: Illegal access to {c} which is hidden by the previous definition | of method cc with result type Object^. | This type hides capabilities {c} + | + | where: ^ refers to a fresh root capability created in method cc -- Error: tests/neg-custom-args/captures/sep-use2.scala:18:8 ----------------------------------------------------------- 18 | { f(cc) } // error // error | ^^ | Separation failure: argument of type (cc : -> Object^) | to a function of type (x: Object^) ->{c} Object^ - | corresponds to capture-polymorphic formal parameter x of type Object^ + | corresponds to capture-polymorphic formal parameter x of type Object^² | and hides capabilities {cap, c}. | Some of these overlap with the captures of the function prefix. | @@ -37,24 +41,32 @@ | Capture set of function prefix : {f} | Footprint set of function prefix : {f, c} | The two sets overlap at : {c} + | + | where: ^ refers to a fresh root capability created in method cc + | ^² refers to a fresh root capability created in value x1 + | cap is a fresh root capability created in method cc -- Error: tests/neg-custom-args/captures/sep-use2.scala:20:6 ----------------------------------------------------------- 20 | { f(c) } // error // error | ^ | Separation failure: Illegal access to {c} which is hidden by the previous definition | of method cc with result type Object^. | This type hides capabilities {c} + | + | where: ^ refers to a fresh root capability created in method cc -- Error: tests/neg-custom-args/captures/sep-use2.scala:20:8 ----------------------------------------------------------- 20 | { f(c) } // error // error | ^ | Separation failure: Illegal access to {c} which is hidden by the previous definition | of method cc with result type Object^. | This type hides capabilities {c} + | + | where: ^ refers to a fresh root capability created in method cc -- Error: tests/neg-custom-args/captures/sep-use2.scala:24:8 ----------------------------------------------------------- 24 | { f(c) } // error | ^ | Separation failure: argument of type (c : Object^) | to a function of type Object^ ->{c} Object^ - | corresponds to capture-polymorphic formal parameter v1 of type Object^ + | corresponds to capture-polymorphic formal parameter v1 of type Object^² | and hides capabilities {c}. | Some of these overlap with the captures of the function prefix. | @@ -63,3 +75,6 @@ | Capture set of function prefix : {f} | Footprint set of function prefix : {f, c} | The two sets overlap at : {c} + | + | where: ^ refers to a fresh root capability created in method test3 + | ^² refers to a fresh root capability created in value x4 diff --git a/tests/neg-custom-args/captures/sepchecks2.check b/tests/neg-custom-args/captures/sepchecks2.check index 47a80306f3b7..1a9adf7c5d1a 100644 --- a/tests/neg-custom-args/captures/sepchecks2.check +++ b/tests/neg-custom-args/captures/sepchecks2.check @@ -4,6 +4,8 @@ | 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 {c} + | + | where: => refers to a fresh root capability created in value xs -- Error: tests/neg-custom-args/captures/sepchecks2.scala:13:7 --------------------------------------------------------- 13 | foo((() => println(c)) :: Nil, c) // error | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -18,13 +20,18 @@ | Capture set of second argument : {c} | Footprint set of second argument : {c} | The two sets overlap at : {c} + | + | where: => refers to a fresh root capability created in method Test2 -- 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^). + | Separation failure in value x1's type (box Object^, box Object^²). | One part, box Object^, hides capabilities {c}. - | Another part, box Object^, captures capabilities {c}. + | Another part, box Object^², captures capabilities {c}. | The two sets overlap at {c}. + | + | where: ^ refers to a fresh root capability created in value x1 + | ^² refers to a fresh root capability created in value x1 -- Error: tests/neg-custom-args/captures/sepchecks2.scala:15:10 -------------------------------------------------------- 15 | val x2: (Object^, Object^{d}) = (d, d) // error | ^^^^^^^^^^^^^^^^^^^^^ @@ -32,13 +39,18 @@ | One part, box Object^, hides capabilities {d}. | Another part, box Object^{d}, captures capabilities {d}. | The two sets overlap at {d}. + | + | where: ^ refers to a fresh root capability created in value x2 -- 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^). + | Separation failure in the argument's adapted type (box Object^, box Object^²). | One part, box Object^, hides capabilities {c}. - | Another part, box Object^, captures capabilities {c}. + | Another part, box Object^², captures capabilities {c}. | The two sets overlap at {c}. + | + | where: ^ refers to a fresh root capability created in method Test6 + | ^² refers to a fresh root capability created in method Test6 -- Error: tests/neg-custom-args/captures/sepchecks2.scala:30:9 --------------------------------------------------------- 30 | val x: (Object^, Object^{c}) = (d, c) // error | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/neg-custom-args/captures/sepchecks4.check b/tests/neg-custom-args/captures/sepchecks4.check index 5934be1ff637..0b5c9ce73ccf 100644 --- a/tests/neg-custom-args/captures/sepchecks4.check +++ b/tests/neg-custom-args/captures/sepchecks4.check @@ -22,3 +22,5 @@ | Capture set of second argument : {io} | Footprint set of second argument : {io} | The two sets overlap at : {io} + | + | where: => refers to a fresh root capability created in method test diff --git a/tests/neg-custom-args/captures/sepchecks5.check b/tests/neg-custom-args/captures/sepchecks5.check index aae5cb89da53..fb8595199852 100644 --- a/tests/neg-custom-args/captures/sepchecks5.check +++ b/tests/neg-custom-args/captures/sepchecks5.check @@ -14,3 +14,5 @@ | Separation failure: Illegal access to (io : Object^), which was passed to a | @consume parameter or was used as a prefix to a @consume method on line 19 | and therefore is no longer available. + | + | where: ^ refers to a fresh root capability created in method test diff --git a/tests/neg-custom-args/captures/shared-capability.check b/tests/neg-custom-args/captures/shared-capability.check index 64fb3eb39d44..15355a9fc5b9 100644 --- a/tests/neg-custom-args/captures/shared-capability.check +++ b/tests/neg-custom-args/captures/shared-capability.check @@ -2,3 +2,5 @@ 9 |def test2(a: Async^): Object^ = a // error | ^^^^^^ | Async^ extends SharedCapability, so it cannot capture `cap` + | + | where: ^ refers to the universal root capability diff --git a/tests/neg-custom-args/captures/simple-using.check b/tests/neg-custom-args/captures/simple-using.check index e46b9e50b58d..5280705d51dc 100644 --- a/tests/neg-custom-args/captures/simple-using.check +++ b/tests/neg-custom-args/captures/simple-using.check @@ -2,7 +2,11 @@ 8 | usingLogFile { f => () => f.write(2) } // error | ^^^^^^^^^^^^^^^^^^^^^^^^^ | Found: (f: java.io.FileOutputStream^) ->? box () ->{f} Unit - | Required: (f: java.io.FileOutputStream^) ->{fresh} box () ->? Unit + | Required: (f: java.io.FileOutputStream^) => box () ->? Unit + | + | where: => refers to a fresh root capability created in method test + | ^ refers to the universal root capability + | | | Note that reference f.type | cannot be included in outer capture set ? diff --git a/tests/neg-custom-args/captures/try.check b/tests/neg-custom-args/captures/try.check index 9cd40a8bc8c4..ddac93881450 100644 --- a/tests/neg-custom-args/captures/try.check +++ b/tests/neg-custom-args/captures/try.check @@ -3,11 +3,17 @@ | ^^^^^^^^^^^^^^^^^^^ | Type variable R of method handle cannot be instantiated to box CT[Exception]^ since | that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:23:49 ------------------------------------------ 23 | val a = handle[Exception, CanThrow[Exception]] { // error // error | ^ | Found: (x: CT[Exception]^) ->? box CT[Exception]^{x} - | Required: (x: CT[Exception]^) ->{fresh} box CT[Exception]^{fresh} + | Required: (x: CT[Exception]^) => box CT[Exception]^² + | + | where: => refers to a fresh root capability created in value a + | ^ refers to the universal root capability + | ^² refers to a fresh root capability created in value a 24 | (x: CanThrow[Exception]) => x 25 | }{ | @@ -16,7 +22,10 @@ 29 | val b = handle[Exception, () -> Nothing] { // error | ^ | Found: (x: CT[Exception]^) ->? () ->{x} Nothing - | Required: (x: CT[Exception]^) ->{fresh} () -> Nothing + | Required: (x: CT[Exception]^) => () -> Nothing + | + | where: => refers to a fresh root capability created in value b + | ^ refers to the universal root capability 30 | (x: CanThrow[Exception]) => () => raise(new Exception)(using x) 31 | } { | @@ -25,7 +34,11 @@ 35 | val xx = handle { // error | ^ | Found: (x: CT[Exception]^) ->? box () ->{x} Int - | Required: (x: CT[Exception]^) ->{fresh} box () ->? Int + | Required: (x: CT[Exception]^) => box () ->? Int + | + | where: => refers to a fresh root capability created in value xx + | ^ refers to the universal root capability + | | | Note that reference x.type | cannot be included in outer capture set ? @@ -40,7 +53,11 @@ 47 |val global: () -> Int = handle { // error | ^ | Found: (x: CT[Exception]^) ->? box () ->{x} Int - | Required: (x: CT[Exception]^) ->{fresh} box () ->? Int + | Required: (x: CT[Exception]^) => box () ->? Int + | + | where: => refers to a fresh root capability created in value global + | ^ refers to the universal root capability + | | | Note that reference x.type | cannot be included in outer capture set ? diff --git a/tests/neg-custom-args/captures/unsound-reach-4.check b/tests/neg-custom-args/captures/unsound-reach-4.check index f21d1403eb3d..29e317b90396 100644 --- a/tests/neg-custom-args/captures/unsound-reach-4.check +++ b/tests/neg-custom-args/captures/unsound-reach-4.check @@ -3,12 +3,16 @@ | ^^^^^^^^^^ | Type variable X of trait Foo cannot be instantiated to File^ since | that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- [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^] | + | where: ^ refers to a fresh root capability created in value backdoor + | | longer explanation available when compiling with `-explain` -- [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 diff --git a/tests/neg-custom-args/captures/unsound-reach.check b/tests/neg-custom-args/captures/unsound-reach.check index a1ebd30e4915..f5e0249da9af 100644 --- a/tests/neg-custom-args/captures/unsound-reach.check +++ b/tests/neg-custom-args/captures/unsound-reach.check @@ -3,15 +3,21 @@ | ^^^^^^^^^^ | Type variable X of trait Foo cannot be instantiated to File^ since | that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- Error: tests/neg-custom-args/captures/unsound-reach.scala:14:19 ----------------------------------------------------- 14 |class Bar2 extends Foo2[File^]: // error | ^ | Type variable X of constructor Foo2 cannot be instantiated to box File^ since | that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- [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^] | + | where: ^ refers to a fresh root capability created in value backdoor + | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/usingLogFile.check b/tests/neg-custom-args/captures/usingLogFile.check index 1c77435685c7..5c14890efc52 100644 --- a/tests/neg-custom-args/captures/usingLogFile.check +++ b/tests/neg-custom-args/captures/usingLogFile.check @@ -2,7 +2,11 @@ 22 | val later = usingLogFile { f => () => f.write(0) } // error | ^^^^^^^^^^^^^^^^^^^^^^^^^ | Found: (f: java.io.FileOutputStream^) ->? box () ->{f} Unit - | Required: (f: java.io.FileOutputStream^) ->{fresh} box () ->? Unit + | Required: (f: java.io.FileOutputStream^) => box () ->? Unit + | + | where: => refers to a fresh root capability created in value later + | ^ refers to the universal root capability + | | | Note that reference f.type | cannot be included in outer capture set ? @@ -11,18 +15,26 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/usingLogFile.scala:27:36 --------------------------------- 27 | private val later2 = usingLogFile { f => Cell(() => f.write(0)) } // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (f: java.io.FileOutputStream^) ->? box Test2.Cell[box () ->{f} Unit]^? - | Required: (f: java.io.FileOutputStream^) ->{fresh} box Test2.Cell[box () ->? Unit]^? + | Found: (f: java.io.FileOutputStream^) ->? box Test2.Cell[box () ->{f} Unit]^? + | Required: (f: java.io.FileOutputStream^) => box Test2.Cell[box () ->? Unit]^? + | + | where: => refers to a fresh root capability created in value later2 + | ^ refers to the universal root capability + | | - | Note that reference f.type - | cannot be included in outer capture set ? + | Note that reference f.type + | cannot be included in outer capture set ? | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/usingLogFile.scala:43:33 --------------------------------- 43 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Found: (f: java.io.OutputStream^) ->? box Int ->{f} Unit - | Required: (f: java.io.OutputStream^) ->{fresh} box Int ->? Unit + | Required: (f: java.io.OutputStream^) => box Int ->? Unit + | + | where: => refers to a fresh root capability created in value later + | ^ refers to the universal root capability + | | | Note that reference f.type | cannot be included in outer capture set ? @@ -32,7 +44,11 @@ 52 | usingLogger(_, l => () => l.log("test"))) // error after checking mapping scheme | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Found: (_$1: java.io.OutputStream^) ->? box () ->{_$1} Unit - | Required: (_$1: java.io.OutputStream^) ->{fresh} box () ->? Unit + | Required: (_$1: java.io.OutputStream^) => box () ->? Unit + | + | where: => refers to a fresh root capability created in value later + | ^ refers to the universal root capability + | | | Note that reference _$1.type | cannot be included in outer capture set ? diff --git a/tests/neg-custom-args/captures/vars-simple.check b/tests/neg-custom-args/captures/vars-simple.check index 71fab0dcf7d2..5f5c6c365638 100644 --- a/tests/neg-custom-args/captures/vars-simple.check +++ b/tests/neg-custom-args/captures/vars-simple.check @@ -4,6 +4,8 @@ | Found: String => String | Required: String ->{cap1, cap2} String | + | where: => refers to a fresh root capability created in method scope + | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/vars-simple.scala:16:8 ----------------------------------- 16 | a = g // error diff --git a/tests/neg-custom-args/captures/vars.check b/tests/neg-custom-args/captures/vars.check index fae2645fcb8b..d8b55afd298c 100644 --- a/tests/neg-custom-args/captures/vars.check +++ b/tests/neg-custom-args/captures/vars.check @@ -25,6 +25,9 @@ | Found: (cap3: CC^) ->? box String ->{cap3} String | Required: (cap3: CC^) -> box String ->? String | + | where: ^ refers to the universal root capability + | + | | Note that reference cap3.type | cannot be included in outer capture set ? 37 | def g(x: String): String = if cap3 == cap3 then "" else "a" diff --git a/tests/neg-custom-args/captures/widen-reach.check b/tests/neg-custom-args/captures/widen-reach.check index e2028941a009..3448088abc02 100644 --- a/tests/neg-custom-args/captures/widen-reach.check +++ b/tests/neg-custom-args/captures/widen-reach.check @@ -3,11 +3,16 @@ | ^^^^^^^^ | Type variable T of trait Foo cannot be instantiated to IO^ since | that type captures the root capability `cap`. + | + | where: ^ refers to the universal root capability -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/widen-reach.scala:9:24 ----------------------------------- 9 | val foo: IO^ -> IO^ = x => x // error // error | ^^^^^^ | Found: (x: IO^) ->? IO^{x} - | Required: (x: IO^) -> IO^{fresh} + | Required: (x: IO^) -> IO^² + | + | where: ^ refers to the universal root capability + | ^² refers to a fresh root capability created in value foo | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/widen-reach.scala:13:26 ------------------------------------------------------- From 4705a47355f609c744c30037329f81d1fa54d38a Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 3 May 2025 20:42:00 +0200 Subject: [PATCH 07/19] More detailed explanations where Fresh instances come from --- .../src/dotty/tools/dotc/cc/CaptureOps.scala | 7 +- .../src/dotty/tools/dotc/cc/CaptureSet.scala | 8 +- .../dotty/tools/dotc/cc/CheckCaptures.scala | 50 +++-- compiler/src/dotty/tools/dotc/cc/Setup.scala | 10 +- .../src/dotty/tools/dotc/cc/Synthetics.scala | 3 +- compiler/src/dotty/tools/dotc/cc/root.scala | 22 +- .../dotty/tools/dotc/reporting/Message.scala | 12 +- .../dotty/tools/dotc/transform/Recheck.scala | 4 +- tests/neg-custom-args/captures/byname.check | 6 +- .../captures/capt-depfun.check | 2 +- tests/neg-custom-args/captures/capt1.check | 18 +- .../captures/cc-existential-conformance.check | 8 +- .../neg-custom-args/captures/cc-poly-2.check | 2 +- .../captures/depfun-reach.check | 14 +- .../captures/effect-swaps-explicit.check | 16 +- .../captures/effect-swaps.check | 14 +- .../captures/erased-methods2.check | 4 +- .../captures/existential-mapping.check | 48 ++-- .../captures/filevar-expanded.check | 24 +- tests/neg-custom-args/captures/filevar.check | 2 +- .../captures/heal-tparam-cs.check | 12 +- tests/neg-custom-args/captures/i15116.check | 8 +- tests/neg-custom-args/captures/i15772.check | 2 +- tests/neg-custom-args/captures/i16226.check | 6 +- tests/neg-custom-args/captures/i19330.check | 4 +- tests/neg-custom-args/captures/i21313.check | 6 +- tests/neg-custom-args/captures/i21401.check | 10 +- tests/neg-custom-args/captures/i21614.check | 8 +- tests/neg-custom-args/captures/i21920.check | 12 +- .../captures/lazylists-exceptions.check | 2 +- tests/neg-custom-args/captures/lazyref.check | 14 +- .../captures/leaking-iterators.check | 12 +- .../captures/linear-buffer-2.check | 10 +- .../captures/linear-buffer.check | 10 +- .../neg-custom-args/captures/outer-var.check | 4 +- tests/neg-custom-args/captures/reaches.check | 46 ++-- tests/neg-custom-args/captures/reaches2.check | 2 +- tests/neg-custom-args/captures/real-try.check | 8 +- tests/neg-custom-args/captures/sep-box.check | 22 +- .../captures/sep-compose.check | 208 +++++++++--------- .../captures/sep-consume.check | 4 +- .../captures/sep-counter.check | 6 +- .../captures/sep-curried.check | 120 +++++----- tests/neg-custom-args/captures/sep-list.check | 22 +- .../neg-custom-args/captures/sep-pairs.check | 28 +-- tests/neg-custom-args/captures/sep-use.check | 8 +- tests/neg-custom-args/captures/sep-use2.check | 58 ++--- .../neg-custom-args/captures/sepchecks2.check | 42 ++-- .../neg-custom-args/captures/sepchecks4.check | 22 +- .../neg-custom-args/captures/sepchecks5.check | 2 +- .../captures/simple-using.check | 12 +- tests/neg-custom-args/captures/try.check | 42 ++-- .../captures/unsound-reach-4.check | 2 +- .../captures/unsound-reach.check | 2 +- .../captures/usingLogFile.check | 48 ++-- .../captures/widen-reach.check | 2 +- 56 files changed, 566 insertions(+), 534 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index 71031a791b01..ff27573fe716 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -462,7 +462,9 @@ extension (tp: Type) if args.forall(_.isAlwaysPure) then // Also map existentials in results to reach capabilities if all // preceding arguments are known to be always pure - t.derivedFunctionOrMethod(args, apply(root.resultToFresh(res))) + t.derivedFunctionOrMethod( + args, + apply(root.resultToFresh(res, i"when instantiating $t"))) else t case _ => @@ -795,7 +797,8 @@ abstract class DeepTypeAccumulator[T](using Context) extends TypeAccumulator[T]: case AnnotatedType(parent, _) => this(acc, parent) case t @ FunctionOrMethod(args, res) => - if args.forall(_.isAlwaysPure) then this(acc, root.resultToFresh(res)) + if args.forall(_.isAlwaysPure) then + this(acc, root.resultToFresh(res, i" when instantiating $t")) else acc case _ => foldOver(acc, t) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index 835f1a5f7a03..c186ff855fb2 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -423,8 +423,8 @@ object CaptureSet: def universalImpliedByCapability(using Context) = defn.universalCSImpliedByCapability - def fresh()(using Context): CaptureSet = - root.Fresh().singletonCaptureSet + def fresh(purpose: => String)(using Context): CaptureSet = + root.Fresh(purpose).singletonCaptureSet /** The shared capture set `{cap.rd}` */ def shared(using Context): CaptureSet = @@ -685,7 +685,7 @@ object CaptureSet: def solve()(using Context): Unit = CCState.withCapAsRoot: // // OK here since we infer parameter types that get checked later val approx = upperApprox(empty) - .map(root.CapToFresh().inverse) // Fresh --> cap + .map(root.CapToFresh("").inverse) // Fresh --> cap .showing(i"solve $this = $result", capt) //println(i"solving var $this $approx ${approx.isConst} deps = ${deps.toList}") val newElems = approx.elems -- elems @@ -1388,7 +1388,7 @@ object CaptureSet: def capturingCase(acc: CaptureSet, parent: Type, refs: CaptureSet) = this(acc, parent) ++ refs def abstractTypeCase(acc: CaptureSet, t: TypeRef, upperBound: Type) = - if includeTypevars && upperBound.isExactlyAny then CaptureSet.fresh() + if includeTypevars && upperBound.isExactlyAny then CaptureSet.fresh(i" when computing deep capture set of $t") else this(acc, upperBound) collect(CaptureSet.empty, tp) diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index ecee5738c4bc..7db6f91eeea9 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -714,7 +714,7 @@ 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(root.Fresh())) + val argType0 = recheck(arg, pt.stripCapturing.capturing(root.Fresh(i" when instantiating argument of unsafeAssumePure"))) val argType = if argType0.captureSet.isAlwaysEmpty then argType0 else argType0.widen.stripCapturing @@ -729,12 +729,16 @@ class CheckCaptures extends Recheck, SymTransformer: * occurrences are replaced by `Fresh` instances. Also, 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 = root.capToFresh(formal) + protected override def recheckArg(arg: Tree, formal: Type, pref: ParamRef, app: Apply)(using Context): Type = + val fn = app.symbol + def purpose = + if fn.exists then i" when checking argument to parameter ${pref.paramName} of $fn" + else "" + val freshenedFormal = root.capToFresh(formal, purpose) val argType = recheck(arg, freshenedFormal) .showing(i"recheck arg $arg vs $freshenedFormal = $result", capt) if formal.hasAnnotation(defn.UseAnnot) || formal.hasAnnotation(defn.ConsumeAnnot) then - // The @use and/or @consume annotation is added to `formal` by `prepareFunction` + // The @use and/or @consume annotation is added to `formal` by `prep areFunction` capt.println(i"charging deep capture set of $arg: ${argType} = ${argType.deepCaptureSet}") markFree(argType.deepCaptureSet, arg) if formal.containsCap then @@ -764,7 +768,11 @@ class CheckCaptures extends Recheck, SymTransformer: */ protected override def recheckApplication(tree: Apply, qualType: Type, funType: MethodType, argTypes: List[Type])(using Context): Type = - val appType = root.resultToFresh(super.recheckApplication(tree, qualType, funType, argTypes)) + val fn = tree.symbol + def methDescr = if fn.exists then i"$fn's type " else "" + val appType = root.resultToFresh( + super.recheckApplication(tree, qualType, funType, argTypes), + i" when instantiating $methDescr$funType") val qualCaptures = qualType.captureSet val argCaptures = for (argType, formal) <- argTypes.lazyZip(funType.paramInfos) yield @@ -823,8 +831,10 @@ class CheckCaptures extends Recheck, SymTransformer: 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 ++ root.Fresh().readOnly.singletonCaptureSet + if core.derivesFromMutable then + initCs ++ CaptureSet.fresh(i" when constructing mutable $core") + else if core.derivesFromCapability then + initCs ++ root.Fresh(i" when constructing Capability instance $core").readOnly.singletonCaptureSet else initCs for (getterName, argType) <- mt.paramNames.lazyZip(argTypes) do val getter = cls.info.member(getterName).suchThat(_.isRefiningParamAccessor).symbol @@ -864,8 +874,10 @@ class CheckCaptures extends Recheck, SymTransformer: val meth = tree.fun match case fun @ Select(qual, nme.apply) => qual.symbol.orElse(fun.symbol) case fun => fun.symbol + def methDescr = if meth.exists then i"$meth's type " else "" disallowCapInTypeArgs(tree.fun, meth, tree.args) - val res = root.resultToFresh(super.recheckTypeApply(tree, pt)) + val funType = super.recheckTypeApply(tree, pt) + val res = root.resultToFresh(funType, i" when instantiating $methDescr$funType") includeCallCaptures(tree.symbol, res, tree) checkContains(tree) res @@ -925,14 +937,16 @@ class CheckCaptures extends Recheck, SymTransformer: // neg-custom-args/captures/vars.scala. That's why this code is conditioned. // to apply only to closures that are not eta expansions. assert(paramss1.isEmpty) - val respt = root.resultToFresh: - pt match - case defn.RefinedFunctionOf(rinfo) => - val paramTypes = params.map(_.asInstanceOf[ValDef].tpt.nuType) - rinfo.instantiate(paramTypes) - case _ => - resType - val res = root.resultToFresh(mdef.tpt.nuType) + val respt0 = pt match + case defn.RefinedFunctionOf(rinfo) => + val paramTypes = params.map(_.asInstanceOf[ValDef].tpt.nuType) + rinfo.instantiate(paramTypes) + case _ => + resType + val respt = root.resultToFresh(respt0, + i" when instantiating expected result type $respt0 of lambda") + val res = root.resultToFresh(mdef.tpt.nuType, + i" when instantiating result type ${mdef.tpt.nuType} of lambda") // We need to open existentials here in order not to get vars mixed up in them // We do the proper check with existentials when we are finished with the closure block. capt.println(i"pre-check closure $expr of type $res against $respt") @@ -1632,7 +1646,9 @@ class CheckCaptures extends Recheck, SymTransformer: memberTp, otherTp.derivedTypeBounds( otherTp.lo, - hi.derivedCapturingType(parent, root.Fresh().singletonCaptureSet)))) + hi.derivedCapturingType(parent, + root.Fresh(i" when instantiating upper bound of member overridden by $member") + .singletonCaptureSet)))) case _ => None case _ => None case _ => None diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 8729d9716518..d6cc9dc6b888 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -472,7 +472,12 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: val tp3 = if sym.isType then stripImpliedCaptureSet(tp2) else tp2 - if freshen then root.capToFresh(tp3).tap(addOwnerAsHidden(_, sym)) + if freshen then + root.capToFresh(tp3, + if sym.is(Method) then i" in the result type of $sym" + else if sym.exists then i" in the type of $sym" + else "") + .tap(addOwnerAsHidden(_, sym)) else tp3 end transformExplicitType @@ -568,7 +573,8 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: traverse(fn) for case arg: TypeTree <- args do if defn.isTypeTestOrCast(fn.symbol) then - arg.setNuType(root.capToFresh(arg.tpe)) + arg.setNuType( + root.capToFresh(arg.tpe, i" of type argument ${arg.tpe}")) else transformTT(arg, NoSymbol, boxed = true) // type arguments in type applications are boxed diff --git a/compiler/src/dotty/tools/dotc/cc/Synthetics.scala b/compiler/src/dotty/tools/dotc/cc/Synthetics.scala index cdc3afff8a2d..0ec4d1e20a17 100644 --- a/compiler/src/dotty/tools/dotc/cc/Synthetics.scala +++ b/compiler/src/dotty/tools/dotc/cc/Synthetics.scala @@ -116,7 +116,8 @@ 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.fresh(i" when instantiating argument of unapply with type $info")) val trackedParam = info.paramRefs.head def newResult(tp: Type): Type = tp match case tp: MethodOrPoly => diff --git a/compiler/src/dotty/tools/dotc/cc/root.scala b/compiler/src/dotty/tools/dotc/cc/root.scala index 81993c0f7d74..bb8144bbd052 100644 --- a/compiler/src/dotty/tools/dotc/cc/root.scala +++ b/compiler/src/dotty/tools/dotc/cc/root.scala @@ -61,7 +61,7 @@ object root: enum Kind: case Result(binder: MethodType) - case Fresh(hidden: CaptureSet.HiddenSet) + case Fresh(hidden: CaptureSet.HiddenSet)(val purpose: () => String) case Global override def equals(other: Any): Boolean = @@ -129,10 +129,10 @@ object root: /** Constructor and extractor methods for "fresh" capabilities */ object Fresh: - def apply(using Context)(owner: Symbol = ctx.owner): CaptureRef = + def apply(using Context)(purpose: => String, owner: Symbol = ctx.owner): CaptureRef = if ccConfig.useSepChecks then val hiddenSet = CaptureSet.HiddenSet(owner) - val res = AnnotatedType(cap, Annot(Kind.Fresh(hiddenSet))) + val res = AnnotatedType(cap, Annot(Kind.Fresh(hiddenSet)(() => purpose))) hiddenSet.owningCap = res //assert(hiddenSet.id != 3) res @@ -167,14 +167,14 @@ object root: /** Map each occurrence of cap to a different Fresh instance * Exception: CapSet^ stays as it is. */ - class CapToFresh()(using Context) extends BiTypeMap, FollowAliasesMap: + class CapToFresh(purpose: => String)(using Context) extends BiTypeMap, FollowAliasesMap: thisMap => override def apply(t: Type) = if variance <= 0 then t else t match case t: CaptureRef if t.isCap => - Fresh() + Fresh(purpose) case t @ CapturingType(parent: TypeRef, _) if parent.symbol == defn.Caps_CapSet => t case t @ CapturingType(_, _) => @@ -212,15 +212,15 @@ object root: end CapToFresh /** Maps cap to fresh */ - def capToFresh(tp: Type)(using Context): Type = - if ccConfig.useSepChecks then CapToFresh()(tp) else tp + def capToFresh(tp: Type, purpose: => String)(using Context): Type = + if ccConfig.useSepChecks then CapToFresh(purpose)(tp) else tp /** Maps fresh to cap */ def freshToCap(tp: Type)(using Context): Type = - if ccConfig.useSepChecks then CapToFresh().inverse(tp) else tp + if ccConfig.useSepChecks then CapToFresh("").inverse(tp) else tp /** Map top-level free existential variables one-to-one to Fresh instances */ - def resultToFresh(tp: Type)(using Context): Type = + def resultToFresh(tp: Type, purpose: => String)(using Context): Type = val subst = new TypeMap: val seen = EqHashMap[Annotation, CaptureRef]() var localBinders: SimpleIdentitySet[MethodType] = SimpleIdentitySet.empty @@ -228,7 +228,7 @@ object root: def apply(t: Type): Type = t match case t @ Result(binder) => if localBinders.contains(binder) then t // keep bound references - else seen.getOrElseUpdate(t.annot, Fresh()) // map free references to Fresh() + else seen.getOrElseUpdate(t.annot, Fresh(purpose)) // map free references to Fresh() case t: MethodType => // skip parameters val saved = localBinders @@ -294,7 +294,7 @@ object root: val (k, v) = it.next if v.annot eq t.annot then ref = k if ref == null then - ref = Fresh() + ref = Fresh("") seen(ref) = t ref case _ => mapOver(t) diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 6b884556d492..cccd5b0284bc 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -8,7 +8,7 @@ import printing.{RefinedPrinter, MessageLimiter, ErrorMessageLimiter} import printing.Texts.Text import printing.Formatting.hl import config.SourceVersion -import cc.{CaptureRef, CaptureSet, root} +import cc.{CaptureRef, CaptureSet, root, rootAnnot} import scala.language.unsafeNulls import scala.annotation.threadUnsafe @@ -175,7 +175,7 @@ object Message: if owner.isConstructor then i"constructor of ${ownerStr(owner.owner)}" else if owner.isAnonymousFunction then - i"anonymous fucntion of type ${owner.info}" + i"anonymous function of type ${owner.info}" else if owner.name.toString.contains('$') then ownerStr(owner.owner) else @@ -183,7 +183,13 @@ object Message: val descr = if ref.isCap then "the universal root capability" else ref match - case root.Fresh(hidden) => i"a fresh root capability created in ${ownerStr(hidden.owner)}" + case ref @ root.Fresh(hidden) => + val (kind: root.Kind.Fresh) = ref.rootAnnot.kind: @unchecked + val purpose = kind.purpose() + val descr = + if purpose.startsWith(" in the ") then purpose + else i" created in ${ownerStr(hidden.owner)}$purpose" + i"a fresh root capability$descr" case root.Result(binder) => i"a root capability associated with the result type of $binder" s"$relation $descr" end explanation diff --git a/compiler/src/dotty/tools/dotc/transform/Recheck.scala b/compiler/src/dotty/tools/dotc/transform/Recheck.scala index b891df666c6c..97c37de2de08 100644 --- a/compiler/src/dotty/tools/dotc/transform/Recheck.scala +++ b/compiler/src/dotty/tools/dotc/transform/Recheck.scala @@ -304,7 +304,7 @@ abstract class Recheck extends Phase, SymTransformer: /** A hook to massage the type of an applied method */ protected def prepareFunction(funtpe: MethodType, meth: Symbol)(using Context): MethodType = funtpe - protected def recheckArg(arg: Tree, formal: Type)(using Context): Type = + protected def recheckArg(arg: Tree, formal: Type, pref: ParamRef, app: Apply)(using Context): Type = recheck(arg, formal) /** A hook to check all the parts of an application: @@ -336,7 +336,7 @@ abstract class Recheck extends Phase, SymTransformer: else fntpe.paramInfos def recheckArgs(args: List[Tree], formals: List[Type], prefs: List[ParamRef]): List[Type] = args match case arg :: args1 => - val argType = recheckArg(arg, normalizeByName(formals.head)) + val argType = recheckArg(arg, normalizeByName(formals.head), prefs.head, tree) val formals1 = if fntpe.isParamDependent then formals.tail.map(_.substParam(prefs.head, argType)) diff --git a/tests/neg-custom-args/captures/byname.check b/tests/neg-custom-args/captures/byname.check index 41a5785bc294..f3ce717db467 100644 --- a/tests/neg-custom-args/captures/byname.check +++ b/tests/neg-custom-args/captures/byname.check @@ -7,10 +7,10 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/byname.scala:10:6 ---------------------------------------- 10 | h(f2()) // error | ^^^^ - | Found: () ?->{cap1} Int ->{cap1} Int - | Required: () ?=> Int ->{cap2} Int + |Found: () ?->{cap1} Int ->{cap1} Int + |Required: () ?=> Int ->{cap2} Int | - | where: ?=> refers to a fresh root capability created in method test + |where: ?=> refers to a fresh root capability created in method test when checking argument to parameter ff of method h | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/byname.scala:19:5 ---------------------------------------- diff --git a/tests/neg-custom-args/captures/capt-depfun.check b/tests/neg-custom-args/captures/capt-depfun.check index b9c94c644db6..a5db4da7a319 100644 --- a/tests/neg-custom-args/captures/capt-depfun.check +++ b/tests/neg-custom-args/captures/capt-depfun.check @@ -4,7 +4,7 @@ | Found: Str^{} ->{ac, y, z} Str^{y, z} | Required: Str^{y, z} => Str^{y, z} | - | where: => refers to a fresh root capability created in value dc + | where: => refers to a fresh root capability in the type of value dc | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/capt-depfun.scala:11:24 ------------------------------------------------------- diff --git a/tests/neg-custom-args/captures/capt1.check b/tests/neg-custom-args/captures/capt1.check index f53a4387f2fc..b0a27b134b60 100644 --- a/tests/neg-custom-args/captures/capt1.check +++ b/tests/neg-custom-args/captures/capt1.check @@ -47,10 +47,10 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:36:24 ---------------------------------------- 36 | val z2 = h[() -> Cap](() => x) // error // error | ^^^^^^^ - | Found: () ->{x} box C^{x} - | Required: () -> box C^ + |Found: () ->{x} box C^{x} + |Required: () -> box C^ | - | where: ^ refers to a fresh root capability created in value z2 + |where: ^ refers to a fresh root capability created in value z2 when checking argument to parameter a of method h | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/capt1.scala:38:13 ------------------------------------------------------------- @@ -67,8 +67,8 @@ | of value z1 with type () => C^. | This type hides capabilities {x} | - | where: => refers to a fresh root capability created in value z1 - | ^ refers to a fresh root capability created in value z1 + | where: => refers to a fresh root capability in the type of value z1 + | ^ refers to a fresh root capability in the type of value z1 -- Error: tests/neg-custom-args/captures/capt1.scala:44:12 ------------------------------------------------------------- 44 | () => x // error: separation | ^ @@ -76,8 +76,8 @@ | of value z1 with type () => C^. | This type hides capabilities {x} | - | where: => refers to a fresh root capability created in value z1 - | ^ refers to a fresh root capability created in value z1 + | where: => refers to a fresh root capability in the type of value z1 + | ^ refers to a fresh root capability in the type of value z1 -- Error: tests/neg-custom-args/captures/capt1.scala:47:2 -------------------------------------------------------------- 47 | x // error: separation | ^ @@ -85,5 +85,5 @@ | of value z1 with type () => C^. | This type hides capabilities {x} | - | where: => refers to a fresh root capability created in value z1 - | ^ refers to a fresh root capability created in value z1 + | where: => refers to a fresh root capability in the type of value z1 + | ^ refers to a fresh root capability in the type of value z1 diff --git a/tests/neg-custom-args/captures/cc-existential-conformance.check b/tests/neg-custom-args/captures/cc-existential-conformance.check index 5fe06dc28712..d3236ce642b0 100644 --- a/tests/neg-custom-args/captures/cc-existential-conformance.check +++ b/tests/neg-custom-args/captures/cc-existential-conformance.check @@ -5,7 +5,7 @@ | Required: A -> A -> B^² | | where: ^ refers to a root capability associated with the result type of (x²: A): B^ - | ^² refers to a fresh root capability created in value y + | ^² refers to a fresh root capability in the type of value y | x is a value in method test | x² is a reference to a value parameter | @@ -16,7 +16,7 @@ | Found: (y : A -> A -> B^) | Required: A -> (x: A) -> B^² | - | where: ^ refers to a fresh root capability created in value y + | where: ^ refers to a fresh root capability in the type of value y | ^² refers to a root capability associated with the result type of (x: A): B^² | | @@ -31,7 +31,7 @@ | Required: A -> B^² | | where: ^ refers to a root capability associated with the result type of (x²: A): B^ - | ^² refers to a fresh root capability created in value y + | ^² refers to a fresh root capability in the type of value y | x is a value in method test2 | x² is a reference to a value parameter | @@ -42,7 +42,7 @@ | Found: (y : A -> B^) | Required: (x: A) -> B^² | - | where: ^ refers to a fresh root capability created in value y + | where: ^ refers to a fresh root capability in the type of value y | ^² refers to a root capability associated with the result type of (x: A): B^² | | diff --git a/tests/neg-custom-args/captures/cc-poly-2.check b/tests/neg-custom-args/captures/cc-poly-2.check index fcefeec5fee7..28e0e1863760 100644 --- a/tests/neg-custom-args/captures/cc-poly-2.check +++ b/tests/neg-custom-args/captures/cc-poly-2.check @@ -4,7 +4,7 @@ | Found: (d : Test.D^) | Required: Test.D^{c1} | - | where: ^ refers to a fresh root capability created in value d + | where: ^ refers to a fresh root capability in the type of value d | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-poly-2.scala:16:20 ------------------------------------ diff --git a/tests/neg-custom-args/captures/depfun-reach.check b/tests/neg-custom-args/captures/depfun-reach.check index e3a8f63b641b..9f47722078aa 100644 --- a/tests/neg-custom-args/captures/depfun-reach.check +++ b/tests/neg-custom-args/captures/depfun-reach.check @@ -1,10 +1,10 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/depfun-reach.scala:12:27 --------------------------------- 12 | List(() => op.foreach((f,g) => { f(); g() })) // error (???) | ^^^^^^^^^^^^^^^^^^^^ - | Found: (x$1: (box () ->? Unit, box () ->? Unit)^?) ->? Unit - | Required: (x$1: (box () ->{op*} Unit, box () ->{op*} Unit)) => Unit + |Found: (x$1: (box () ->? Unit, box () ->? Unit)^?) ->? Unit + |Required: (x$1: (box () ->{op*} Unit, box () ->{op*} Unit)) => Unit | - | where: => refers to a fresh root capability created in anonymous fucntion of type (): Unit + |where: => refers to a fresh root capability created in anonymous function of type (): Unit when checking argument to parameter op of method foreach | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/depfun-reach.scala:19:4 ---------------------------------- @@ -13,8 +13,8 @@ | Found: (op : (xs: List[(X, box () ->{io} Unit)]) => List[box () ->{xs*} Unit]) | Required: (xs: List[(X, box () ->{io} Unit)]) =>² List[() -> Unit] | - | where: => refers to a fresh root capability created in method foo - | =>² refers to a fresh root capability created in method foo + | where: => refers to a fresh root capability in the type of parameter op + | =>² refers to a fresh root capability in the result type of method foo | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/depfun-reach.scala:26:60 --------------------------------- @@ -23,8 +23,8 @@ | Found: (a : (xs: List[box () ->{io} Unit]) => List[box () ->{xs*} Unit]) | Required: (xs: List[box () ->{io} Unit]) =>² List[() -> Unit] | - | where: => refers to a fresh root capability created in value a - | =>² refers to a fresh root capability created in value b + | where: => refers to a fresh root capability in the type of value a + | =>² refers to a fresh root capability in the type of value b | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/depfun-reach.scala:18:17 ------------------------------------------------------ diff --git a/tests/neg-custom-args/captures/effect-swaps-explicit.check b/tests/neg-custom-args/captures/effect-swaps-explicit.check index 7eafba36e38e..c7cbb351318a 100644 --- a/tests/neg-custom-args/captures/effect-swaps-explicit.check +++ b/tests/neg-custom-args/captures/effect-swaps-explicit.check @@ -17,16 +17,16 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps-explicit.scala:69:10 ------------------------ 69 | Future: fut ?=> // error, type mismatch | ^ - | Found: (contextual$9: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]^) ?->{fr, async} - | box Future[box T^?]^{fr, contextual$9} - | Required: (contextual$9: boundary.Label[Result[box Future[box T^?]^?, box E^?]]^) ?=> box Future[box T^?]^? + |Found: (contextual$9: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]^) ?->{fr, async} + | box Future[box T^?]^{fr, contextual$9} + |Required: (contextual$9: boundary.Label[Result[box Future[box T^?]^?, box E^?]]^) ?=> box Future[box T^?]^? | - | where: ?=> refers to a fresh root capability created in method fail4 - | ^ refers to the universal root capability + |where: ?=> refers to a fresh root capability created in method fail4 when checking argument to parameter body of method make + | ^ refers to the universal root capability | | - | Note that reference contextual$9.type - | cannot be included in outer capture set ? + |Note that reference contextual$9.type + |cannot be included in outer capture set ? 70 | fr.await.ok | | longer explanation available when compiling with `-explain` @@ -36,7 +36,7 @@ |Found: (lbl: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]^) ?->{fr, async} Future[box T^?]^{fr, lbl} |Required: (lbl: boundary.Label[Result[Future[T], E]]^) ?=> Future[T] | - |where: ?=> refers to a fresh root capability created in method fail5 + |where: ?=> refers to a fresh root capability created in method fail5 when checking argument to parameter body of method make | ^ refers to the universal root capability 74 | Future: fut ?=> 75 | fr.await.ok diff --git a/tests/neg-custom-args/captures/effect-swaps.check b/tests/neg-custom-args/captures/effect-swaps.check index 53fa5ccda75b..350a7271aefb 100644 --- a/tests/neg-custom-args/captures/effect-swaps.check +++ b/tests/neg-custom-args/captures/effect-swaps.check @@ -17,15 +17,15 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:69:10 --------------------------------- 69 | Future: fut ?=> // error, type mismatch | ^ - | Found: (contextual$9: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]) ?->{fr, async} - | box Future[box T^?]^{fr, contextual$9} - | Required: (contextual$9: boundary.Label[Result[box Future[box T^?]^?, box E^?]]) ?=> box Future[box T^?]^? + |Found: (contextual$9: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]) ?->{fr, async} + | box Future[box T^?]^{fr, contextual$9} + |Required: (contextual$9: boundary.Label[Result[box Future[box T^?]^?, box E^?]]) ?=> box Future[box T^?]^? | - | where: ?=> refers to a fresh root capability created in method fail4 + |where: ?=> refers to a fresh root capability created in method fail4 when checking argument to parameter body of method make | | - | Note that reference contextual$9.type - | cannot be included in outer capture set ? + |Note that reference contextual$9.type + |cannot be included in outer capture set ? 70 | fr.await.ok | | longer explanation available when compiling with `-explain` @@ -35,7 +35,7 @@ |Found: (lbl: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]) ?->{fr, async} Future[box T^?]^{fr, lbl} |Required: (lbl: boundary.Label[Result[Future[T], E]]) ?=> Future[T] | - |where: ?=> refers to a fresh root capability created in method fail5 + |where: ?=> refers to a fresh root capability created in method fail5 when checking argument to parameter body of method make 74 | Future: fut ?=> 75 | fr.await.ok | diff --git a/tests/neg-custom-args/captures/erased-methods2.check b/tests/neg-custom-args/captures/erased-methods2.check index 95a1280e8524..80d2db450c40 100644 --- a/tests/neg-custom-args/captures/erased-methods2.check +++ b/tests/neg-custom-args/captures/erased-methods2.check @@ -4,7 +4,7 @@ |Found: (erased x$1: CT[Ex3]^) ?->? (erased x$2: CT[Ex2]^?) ?->{x$1} Unit |Required: (erased x$1: CT[Ex3]^) ?=> (erased x$2: CT[Ex2]^) ?=>² Unit | - |where: ?=> refers to a fresh root capability created in method foo9a + |where: ?=> refers to a fresh root capability in the result type of method foo9a | ?=>² refers to a root capability associated with the result type of (using erased x$1: CT[Ex3]^): (erased x$2: CT[Ex2]^) ?=>² Unit | ^ refers to the universal root capability | @@ -23,7 +23,7 @@ |Found: (erased x$1: CT[Ex3]^) ?->? (erased x$1: CT[Ex2]^?) ?->{x$1} (erased x$2: CT[Ex1]^?) ?->{x$1} Unit |Required: (erased x$1: CT[Ex3]^) ?=> (erased x$1: CT[Ex2]^) ?=>² (erased x$2: CT[Ex1]^) ?=>³ Unit | - |where: ?=> refers to a fresh root capability created in method foo10a + |where: ?=> refers to a fresh root capability in the result type of method foo10a | ?=>² refers to a root capability associated with the result type of (using erased x$1: CT[Ex3]^): (erased x$1: CT[Ex2]^) ?=>² (erased x$2: CT[Ex1]^) ?=>³ Unit | ?=>³ refers to a root capability associated with the result type of (using erased x$1: CT[Ex2]^): (erased x$2: CT[Ex1]^) ?=>³ Unit | ^ refers to the universal root capability diff --git a/tests/neg-custom-args/captures/existential-mapping.check b/tests/neg-custom-args/captures/existential-mapping.check index 37b85fe0edd2..54f2b1f6a1a0 100644 --- a/tests/neg-custom-args/captures/existential-mapping.check +++ b/tests/neg-custom-args/captures/existential-mapping.check @@ -22,7 +22,7 @@ | Required: C^ -> C | | where: ^ refers to the universal root capability - | ^² refers to a fresh root capability created in value x2 + | ^² refers to a fresh root capability in the type of value x2 | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:15:30 -------------------------- @@ -42,7 +42,7 @@ | Required: A^ -> C^ -> C | | where: ^ refers to the universal root capability - | ^² refers to a fresh root capability created in value x4 + | ^² refers to a fresh root capability in the type of value x4 | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:21:30 -------------------------- @@ -61,8 +61,8 @@ | Found: (x6 : A^ -> (x: C^) => C^²) | Required: A^ -> (x: C^) =>² C | - | where: => refers to a fresh root capability created in value x6 - | =>² refers to a fresh root capability created in method Test + | where: => refers to a fresh root capability in the type of value x6 + | =>² refers to a fresh root capability in the type of value _$6 | ^ refers to the universal root capability | ^² refers to a root capability associated with the result type of (x: C^): C^² | @@ -73,8 +73,8 @@ | Found: (y1 : (x: C^) => C^²) | Required: (x: C^) =>² C | - | where: => refers to a fresh root capability created in value y1 - | =>² refers to a fresh root capability created in method Test + | where: => refers to a fresh root capability in the type of value y1 + | =>² refers to a fresh root capability in the type of value _$7 | ^ refers to the universal root capability | ^² refers to a root capability associated with the result type of (x: C^): C^² | @@ -85,10 +85,10 @@ | Found: (y2 : C^ => C^²) | Required: C^ =>² C | - | where: => refers to a fresh root capability created in value y2 - | =>² refers to a fresh root capability created in method Test + | where: => refers to a fresh root capability in the type of value y2 + | =>² refers to a fresh root capability in the type of value _$8 | ^ refers to the universal root capability - | ^² refers to a fresh root capability created in value y2 + | ^² refers to a fresh root capability in the type of value y2 | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:33:30 -------------------------- @@ -97,10 +97,10 @@ | Found: (y3 : A^ => (x: C^) =>² C^²) | Required: A^ =>³ (x: C^) =>⁴ C | - | where: => refers to a fresh root capability created in value y3 - | =>² refers to a fresh root capability created in value y3 - | =>³ refers to a fresh root capability created in method Test - | =>⁴ refers to a fresh root capability created in method Test + | where: => refers to a fresh root capability in the type of value y3 + | =>² refers to a fresh root capability in the type of value y3 + | =>³ refers to a fresh root capability in the type of value _$9 + | =>⁴ refers to a fresh root capability in the type of value _$9 | ^ refers to the universal root capability | ^² refers to a root capability associated with the result type of (x: C^): C^² | @@ -111,12 +111,12 @@ | Found: (y4 : A^ => C^ =>² C^²) | Required: A^ =>³ C^ =>⁴ C | - | where: => refers to a fresh root capability created in value y4 - | =>² refers to a fresh root capability created in value y4 - | =>³ refers to a fresh root capability created in method Test - | =>⁴ refers to a fresh root capability created in method Test + | where: => refers to a fresh root capability in the type of value y4 + | =>² refers to a fresh root capability in the type of value y4 + | =>³ refers to a fresh root capability in the type of value _$10 + | =>⁴ refers to a fresh root capability in the type of value _$10 | ^ refers to the universal root capability - | ^² refers to a fresh root capability created in value y4 + | ^² refers to a fresh root capability in the type of value y4 | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/existential-mapping.scala:39:30 -------------------------- @@ -125,8 +125,8 @@ | Found: (y5 : A^ => (x: C^) -> C^²) | Required: A^ =>² (x: C^) -> C | - | where: => refers to a fresh root capability created in value y5 - | =>² refers to a fresh root capability created in method Test + | where: => refers to a fresh root capability in the type of value y5 + | =>² refers to a fresh root capability in the type of value _$11 | ^ refers to the universal root capability | ^² refers to a root capability associated with the result type of (x: C^): C^² | @@ -137,10 +137,10 @@ | Found: (y6 : A^ => (x: C^) =>² C^²) | Required: A^ =>³ (x: C^) =>⁴ C | - | where: => refers to a fresh root capability created in value y6 - | =>² refers to a fresh root capability created in value y6 - | =>³ refers to a fresh root capability created in method Test - | =>⁴ refers to a fresh root capability created in method Test + | where: => refers to a fresh root capability in the type of value y6 + | =>² refers to a fresh root capability in the type of value y6 + | =>³ refers to a fresh root capability in the type of value _$12 + | =>⁴ refers to a fresh root capability in the type of value _$12 | ^ refers to the universal root capability | ^² refers to a root capability associated with the result type of (x: C^): C^² | diff --git a/tests/neg-custom-args/captures/filevar-expanded.check b/tests/neg-custom-args/captures/filevar-expanded.check index 7678c3314c5d..516d03b5ed7c 100644 --- a/tests/neg-custom-args/captures/filevar-expanded.check +++ b/tests/neg-custom-args/captures/filevar-expanded.check @@ -1,17 +1,17 @@ -- Error: tests/neg-custom-args/captures/filevar-expanded.scala:34:13 -------------------------------------------------- 34 | withFile(io3): f => // error: separation failure | ^^^ - | Separation failure: argument of type (io3 : test2.IO^) - | to method withFile: [T](io2: test2.IO^)(op: (f: test2.File^{io2}) => T): T - | corresponds to capture-polymorphic formal parameter io2 of type test2.IO^² - | and hides capabilities {io3}. - | Some of these overlap with the captures of the second argument with type (f: test2.File^{io3}) ->{io3} Unit. + |Separation failure: argument of type (io3 : test2.IO^) + |to method withFile: [T](io2: test2.IO^)(op: (f: test2.File^{io2}) => T): T + |corresponds to capture-polymorphic formal parameter io2 of type test2.IO^² + |and hides capabilities {io3}. + |Some of these overlap with the captures of the second argument with type (f: test2.File^{io3}) ->{io3} Unit. | - | Hidden set of current argument : {io3} - | Hidden footprint of current argument : {io3} - | Capture set of second argument : {io3} - | Footprint set of second argument : {io3} - | The two sets overlap at : {io3} + | Hidden set of current argument : {io3} + | Hidden footprint of current argument : {io3} + | Capture set of second argument : {io3} + | Footprint set of second argument : {io3} + | The two sets overlap at : {io3} | - | where: ^ refers to a fresh root capability created in method test - | ^² refers to a fresh root capability created in method test + |where: ^ refers to a fresh root capability in the type of parameter io3 + | ^² refers to a fresh root capability created in method test when checking argument to parameter io2 of method withFile diff --git a/tests/neg-custom-args/captures/filevar.check b/tests/neg-custom-args/captures/filevar.check index 499084a2b386..0a619637a2ce 100644 --- a/tests/neg-custom-args/captures/filevar.check +++ b/tests/neg-custom-args/captures/filevar.check @@ -4,7 +4,7 @@ | Mutable variable file cannot have type File^ since | that type captures the root capability `cap`. | - | where: ^ refers to a fresh root capability created in variable file + | where: ^ refers to a fresh root capability in the type of variable file -- Warning: tests/neg-custom-args/captures/filevar.scala:11:55 --------------------------------------------------------- 11 |def withFile[T](op: (l: caps.Capability) ?-> (f: File^{l}) => T): T = | ^ diff --git a/tests/neg-custom-args/captures/heal-tparam-cs.check b/tests/neg-custom-args/captures/heal-tparam-cs.check index c9dfdec1d378..10b56d5d6304 100644 --- a/tests/neg-custom-args/captures/heal-tparam-cs.check +++ b/tests/neg-custom-args/captures/heal-tparam-cs.check @@ -1,15 +1,15 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/heal-tparam-cs.scala:10:23 ------------------------------- 10 | val test1 = localCap { c => // error | ^ - | Found: (c: Capp^) ->? box () ->{c} Unit - | Required: (c: Capp^) => box () ->? Unit + |Found: (c: Capp^) ->? box () ->{c} Unit + |Required: (c: Capp^) => box () ->? Unit | - | where: => refers to a fresh root capability created in value test1 - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in value test1 when checking argument to parameter op of method localCap + | ^ refers to the universal root capability | | - | Note that reference c.type - | cannot be included in outer capture set ? + |Note that reference c.type + |cannot be included in outer capture set ? 11 | () => { c.use() } 12 | } | diff --git a/tests/neg-custom-args/captures/i15116.check b/tests/neg-custom-args/captures/i15116.check index 511212769289..a9cd9df7b38f 100644 --- a/tests/neg-custom-args/captures/i15116.check +++ b/tests/neg-custom-args/captures/i15116.check @@ -4,7 +4,7 @@ | Found: Foo{val m: (Bar.this.m² : String^)}^{Bar.this.m²} | Required: Foo | - | where: ^ refers to a fresh root capability created in class Bar + | where: ^ refers to a fresh root capability in the type of value m | m is a value in class Foo | m² is a value in class Bar | @@ -22,7 +22,7 @@ | Found: Foo{val m: (Baz.this.m² : String^)}^{Baz.this.m²} | Required: Foo | - | where: ^ refers to a fresh root capability created in trait Baz + | where: ^ refers to a fresh root capability in the type of value m | m is a value in class Foo | m² is a value in trait Baz | @@ -40,7 +40,7 @@ | Found: Foo{val m: (Bar1.this.m² : String^)}^{Bar1.this.m²} | Required: Foo | - | where: ^ refers to a fresh root capability created in class Bar1 + | where: ^ refers to a fresh root capability in the type of value m | m is a value in class Foo | m² is a value in class Bar1 | @@ -58,7 +58,7 @@ | Found: Foo{val m: (Baz2.this.m² : String^)}^{Baz2.this.m²} | Required: Foo | - | where: ^ refers to a fresh root capability created in trait Baz2 + | where: ^ refers to a fresh root capability in the type of value m | m is a value in class Foo | m² is a value in trait Baz2 | diff --git a/tests/neg-custom-args/captures/i15772.check b/tests/neg-custom-args/captures/i15772.check index 59182d9fcf13..151ee9599c7c 100644 --- a/tests/neg-custom-args/captures/i15772.check +++ b/tests/neg-custom-args/captures/i15772.check @@ -22,7 +22,7 @@ | Found: box C^ | Required: box C{val arg: C^?}^? | - | where: ^ refers to a fresh root capability created in method c + | where: ^ refers to a fresh root capability in the result type of method c | | | Note that the universal capability `cap` diff --git a/tests/neg-custom-args/captures/i16226.check b/tests/neg-custom-args/captures/i16226.check index 90d23c6e73d4..f3b8666c0eb7 100644 --- a/tests/neg-custom-args/captures/i16226.check +++ b/tests/neg-custom-args/captures/i16226.check @@ -6,8 +6,8 @@ | |where: => refers to the universal root capability | =>² refers to a root capability associated with the result type of (ref1: LazyRef[box A^?]{val elem: () => A^?}^{io}, f1: A^? => B^?): LazyRef[box B^?]{val elem: () =>² B^?}^{f1, ref1} - | =>³ refers to a fresh root capability created in method mapc - | ^ refers to a fresh root capability created in method mapc + | =>³ refers to a fresh root capability in the result type of method mapc + | ^ refers to a fresh root capability in the result type of method mapc | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i16226.scala:15:4 ---------------------------------------- @@ -18,7 +18,7 @@ | |where: => refers to the universal root capability | =>² refers to a root capability associated with the result type of (ref1: LazyRef[box A^?]{val elem: () => A^?}^{io}, f1: A^? => B^?): LazyRef[box B^?]{val elem: () =>² B^?}^{f1, ref1} - | =>³ refers to a fresh root capability created in method mapd + | =>³ refers to a fresh root capability in the result type of method mapd | ^ refers to a root capability associated with the result type of (ref: LazyRef[A]^{io}, f: A => B): LazyRef[B]^ | | diff --git a/tests/neg-custom-args/captures/i19330.check b/tests/neg-custom-args/captures/i19330.check index eb02397ba193..5a7dc525af4d 100644 --- a/tests/neg-custom-args/captures/i19330.check +++ b/tests/neg-custom-args/captures/i19330.check @@ -16,8 +16,8 @@ | Found: bar.T | Required: () => Logger^ | - | where: => refers to a fresh root capability created in value bad - | ^ refers to a fresh root capability created in value bad + | where: => refers to a fresh root capability in the type of value bad + | ^ refers to a fresh root capability in the type of value bad | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/i19330.scala:16:14 ------------------------------------------------------------ diff --git a/tests/neg-custom-args/captures/i21313.check b/tests/neg-custom-args/captures/i21313.check index 245e38db8e51..2a90a1f7aa47 100644 --- a/tests/neg-custom-args/captures/i21313.check +++ b/tests/neg-custom-args/captures/i21313.check @@ -5,9 +5,9 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21313.scala:15:12 --------------------------------------- 15 | ac1.await(src2) // error | ^^^^ - | Found: (src2 : Source[Int, scala.caps.CapSet^{ac2}]^{}) - | Required: Source[Int, scala.caps.CapSet^{ac1}]^ + |Found: (src2 : Source[Int, scala.caps.CapSet^{ac2}]^{}) + |Required: Source[Int, scala.caps.CapSet^{ac1}]^ | - | where: ^ refers to a fresh root capability created in method test + |where: ^ refers to a fresh root capability created in method test when checking argument to parameter src of method await | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i21401.check b/tests/neg-custom-args/captures/i21401.check index 9f026ae33324..cc2cc230b478 100644 --- a/tests/neg-custom-args/captures/i21401.check +++ b/tests/neg-custom-args/captures/i21401.check @@ -15,12 +15,12 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21401.scala:15:23 --------------------------------------- 15 | val a = usingIO[IO^](x => x) // error // error | ^^^^^^ - | Found: (x: IO^) ->? box IO^{x} - | Required: (x: IO^) => box IO^² + |Found: (x: IO^) ->? box IO^{x} + |Required: (x: IO^) => box IO^² | - | where: => refers to a fresh root capability created in value a - | ^ refers to the universal root capability - | ^² refers to a fresh root capability created in value a + |where: => refers to a fresh root capability created in value a when checking argument to parameter op of method usingIO + | ^ refers to the universal root capability + | ^² refers to a fresh root capability created in value a when checking argument to parameter op of method usingIO | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/i21401.scala:16:66 ------------------------------------------------------------ diff --git a/tests/neg-custom-args/captures/i21614.check b/tests/neg-custom-args/captures/i21614.check index 7f2a0e5f5810..a24677f0f9d1 100644 --- a/tests/neg-custom-args/captures/i21614.check +++ b/tests/neg-custom-args/captures/i21614.check @@ -1,10 +1,10 @@ -- [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) - | Required: File^ + |Found: (f : F) + |Required: File^ | - | where: ^ refers to a fresh root capability created in anonymous fucntion of type (f²: F): Logger + |where: ^ refers to a fresh root capability created in anonymous function of type (f²: F): Logger when checking argument to parameter f of constructor Logger | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21614.scala:15:12 --------------------------------------- @@ -13,7 +13,7 @@ |Found: (_$1: box File^{files*}) ->{files*} box Logger{val f: File^{_$1}}^{cap.rd, _$1} |Required: (_$1: box File^{files*}) => box Logger{val f: File^?}^? | - |where: => refers to a fresh root capability created in method mkLoggers2 + |where: => refers to a fresh root capability created in method mkLoggers2 when checking argument to parameter f of method map | cap is a root capability associated with the result type of (_$1: box File^{files*}): box Logger{val f: File^{_$1}}^{cap.rd, _$1} | | diff --git a/tests/neg-custom-args/captures/i21920.check b/tests/neg-custom-args/captures/i21920.check index 72f92bd6d531..a8dd42b44f1d 100644 --- a/tests/neg-custom-args/captures/i21920.check +++ b/tests/neg-custom-args/captures/i21920.check @@ -1,14 +1,14 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21920.scala:34:35 --------------------------------------- 34 | val cell: Cell[File] = File.open(f => Cell(() => Seq(f))) // error | ^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (f: File^?) ->? box Cell[box File^?]{val head: () ->? IterableOnce[box File^?]^?}^? - | Required: (f: File^) => box Cell[box File^?]{val head: () ->? IterableOnce[box File^?]^?}^? + |Found: (f: File^?) ->? box Cell[box File^?]{val head: () ->? IterableOnce[box File^?]^?}^? + |Required: (f: File^) => box Cell[box File^?]{val head: () ->? IterableOnce[box File^?]^?}^? | - | where: => refers to a fresh root capability created in value cell - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in value cell when checking argument to parameter f of method open + | ^ refers to the universal root capability | | - | Note that the universal capability `cap` - | 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/lazylists-exceptions.check b/tests/neg-custom-args/captures/lazylists-exceptions.check index 7f942ed489e4..a16c852750a3 100644 --- a/tests/neg-custom-args/captures/lazylists-exceptions.check +++ b/tests/neg-custom-args/captures/lazylists-exceptions.check @@ -5,7 +5,7 @@ | that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. | - | where: cap is a fresh root capability created in method problem + | where: cap is a fresh root capability in the type of given instance canThrow$1 37 | tabulate(10) { i => 38 | if i > 9 then throw Ex1() 39 | i * i diff --git a/tests/neg-custom-args/captures/lazyref.check b/tests/neg-custom-args/captures/lazyref.check index 55a6d4d2575f..9c2f1a2d1a50 100644 --- a/tests/neg-custom-args/captures/lazyref.check +++ b/tests/neg-custom-args/captures/lazyref.check @@ -33,7 +33,7 @@ | of value get with type () => T. | This type hides capabilities {LazyRef.this.elem} | - | where: => refers to a fresh root capability created in value get + | where: => refers to a fresh root capability in the type of value get -- Error: tests/neg-custom-args/captures/lazyref.scala:23:13 ----------------------------------------------------------- 23 | val ref3 = ref1.map(g) // error: separation failure | ^^^^ @@ -41,7 +41,7 @@ | of value ref2 with type LazyRef[Int]{val elem: () => Int}^{cap2, ref1}. | This type hides capabilities {cap1} | - | where: => refers to a fresh root capability created in class LazyRef + | where: => refers to a fresh root capability in the type of value elem -- Error: tests/neg-custom-args/captures/lazyref.scala:26:9 ------------------------------------------------------------ 26 | if cap1 == cap2 // error: separation failure // error: separation failure | ^^^^ @@ -49,7 +49,7 @@ | of value ref3 with type LazyRef[Int]{val elem: () => Int}^{cap2, ref1}. | This type hides capabilities {ref2*, cap1} | - | where: => refers to a fresh root capability created in class LazyRef + | where: => refers to a fresh root capability in the type of value elem -- Error: tests/neg-custom-args/captures/lazyref.scala:26:17 ----------------------------------------------------------- 26 | if cap1 == cap2 // error: separation failure // error: separation failure | ^^^^ @@ -57,7 +57,7 @@ | of value ref3 with type LazyRef[Int]{val elem: () => Int}^{cap2, ref1}. | This type hides capabilities {ref2*, cap1} | - | where: => refers to a fresh root capability created in class LazyRef + | where: => refers to a fresh root capability in the type of value elem -- Error: tests/neg-custom-args/captures/lazyref.scala:27:11 ----------------------------------------------------------- 27 | then ref1 // error: separation failure | ^^^^ @@ -65,7 +65,7 @@ | of value ref3 with type LazyRef[Int]{val elem: () => Int}^{cap2, ref1}. | This type hides capabilities {ref2*, cap1} | - | where: => refers to a fresh root capability created in class LazyRef + | where: => refers to a fresh root capability in the type of value elem -- Error: tests/neg-custom-args/captures/lazyref.scala:28:11 ----------------------------------------------------------- 28 | else ref2) // error: separation failure | ^^^^ @@ -73,7 +73,7 @@ | of value ref3 with type LazyRef[Int]{val elem: () => Int}^{cap2, ref1}. | This type hides capabilities {ref2*, cap1} | - | where: => refers to a fresh root capability created in class LazyRef + | where: => refers to a fresh root capability in the type of value elem -- Error: tests/neg-custom-args/captures/lazyref.scala:29:9 ------------------------------------------------------------ 29 | .map(g) // error: separation failure | ^ @@ -81,4 +81,4 @@ | of value ref3 with type LazyRef[Int]{val elem: () => Int}^{cap2, ref1}. | This type hides capabilities {ref2*, cap1} | - | where: => refers to a fresh root capability created in class LazyRef + | where: => refers to a fresh root capability in the type of value elem diff --git a/tests/neg-custom-args/captures/leaking-iterators.check b/tests/neg-custom-args/captures/leaking-iterators.check index 57540903ab82..f2731166e53d 100644 --- a/tests/neg-custom-args/captures/leaking-iterators.check +++ b/tests/neg-custom-args/captures/leaking-iterators.check @@ -1,15 +1,15 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/leaking-iterators.scala:56:16 ---------------------------- 56 | usingLogFile: log => // error | ^ - | Found: (log: java.io.FileOutputStream^) ->? box cctest.Iterator[Int]^{log} - | Required: (log: java.io.FileOutputStream^) => box cctest.Iterator[Int]^? + |Found: (log: java.io.FileOutputStream^) ->? box cctest.Iterator[Int]^{log} + |Required: (log: java.io.FileOutputStream^) => box cctest.Iterator[Int]^? | - | where: => refers to a fresh root capability created in method test - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in method test when checking argument to parameter op of method usingLogFile + | ^ refers to the universal root capability | | - | Note that reference log.type - | cannot be included in outer capture set ? + |Note that reference log.type + |cannot be included in outer capture set ? 57 | xs.iterator.map: x => 58 | log.write(x) 59 | x * x diff --git a/tests/neg-custom-args/captures/linear-buffer-2.check b/tests/neg-custom-args/captures/linear-buffer-2.check index 1038a98a1de1..0d9f6b500770 100644 --- a/tests/neg-custom-args/captures/linear-buffer-2.check +++ b/tests/neg-custom-args/captures/linear-buffer-2.check @@ -5,7 +5,7 @@ | of value buf1 with type Buffer[Int]^. | This type hides capabilities {buf} | - | where: ^ refers to a fresh root capability created in value buf1 + | where: ^ refers to a fresh root capability in the type of value buf1 -- Error: tests/neg-custom-args/captures/linear-buffer-2.scala:20:13 --------------------------------------------------- 20 | val buf3 = buf1.append(4) // error | ^^^^ @@ -13,7 +13,7 @@ | @consume parameter or was used as a prefix to a @consume method on line 18 | and therefore is no longer available. | - | where: ^ refers to a fresh root capability created in value buf1 + | where: ^ refers to a fresh root capability in the type of value buf1 -- Error: tests/neg-custom-args/captures/linear-buffer-2.scala:28:13 --------------------------------------------------- 28 | val buf3 = buf1.append(4) // error | ^^^^ @@ -21,7 +21,7 @@ | @consume parameter or was used as a prefix to a @consume method on line 25 | and therefore is no longer available. | - | where: ^ refers to a fresh root capability created in value buf1 + | where: ^ refers to a fresh root capability in the type of value buf1 -- Error: tests/neg-custom-args/captures/linear-buffer-2.scala:38:13 --------------------------------------------------- 38 | val buf3 = buf1.append(4) // error | ^^^^ @@ -29,11 +29,11 @@ | @consume parameter or was used as a prefix to a @consume method on line 33 | and therefore is no longer available. | - | where: ^ refers to a fresh root capability created in value buf1 + | where: ^ refers to a fresh root capability in the type of value buf1 -- 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. | - | where: ^ refers to a fresh root capability created in method Test5 + | where: ^ refers to a fresh root capability in the type of parameter buf diff --git a/tests/neg-custom-args/captures/linear-buffer.check b/tests/neg-custom-args/captures/linear-buffer.check index 153c23321091..2acaa2ad5fc2 100644 --- a/tests/neg-custom-args/captures/linear-buffer.check +++ b/tests/neg-custom-args/captures/linear-buffer.check @@ -20,7 +20,7 @@ | @consume parameter or was used as a prefix to a @consume method on line 17 | and therefore is no longer available. | - | where: ^ refers to a fresh root capability created in method Test + | where: ^ refers to a fresh root capability in the type of parameter buf -- Error: tests/neg-custom-args/captures/linear-buffer.scala:26:17 ----------------------------------------------------- 26 | val buf3 = app(buf1, 4) // error | ^^^^ @@ -28,7 +28,7 @@ | @consume parameter or was used as a prefix to a @consume method on line 24 | and therefore is no longer available. | - | where: ^ refers to a fresh root capability created in value buf1 + | where: ^ refers to a fresh root capability in the type of value buf1 -- Error: tests/neg-custom-args/captures/linear-buffer.scala:34:17 ----------------------------------------------------- 34 | val buf3 = app(buf1, 4) // error | ^^^^ @@ -36,7 +36,7 @@ | @consume parameter or was used as a prefix to a @consume method on line 31 | and therefore is no longer available. | - | where: ^ refers to a fresh root capability created in value buf1 + | where: ^ refers to a fresh root capability in the type of value buf1 -- Error: tests/neg-custom-args/captures/linear-buffer.scala:44:17 ----------------------------------------------------- 44 | val buf3 = app(buf1, 4) // error | ^^^^ @@ -44,11 +44,11 @@ | @consume parameter or was used as a prefix to a @consume method on line 39 | and therefore is no longer available. | - | where: ^ refers to a fresh root capability created in value buf1 + | where: ^ refers to a fresh root capability in the type of value buf1 -- 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. | - | where: ^ refers to a fresh root capability created in method Test5 + | where: ^ refers to a fresh root capability in the type of parameter buf diff --git a/tests/neg-custom-args/captures/outer-var.check b/tests/neg-custom-args/captures/outer-var.check index ef364c7f5395..fc6662fd7795 100644 --- a/tests/neg-custom-args/captures/outer-var.check +++ b/tests/neg-custom-args/captures/outer-var.check @@ -4,7 +4,7 @@ | Found: (q : () => Unit) | Required: () ->{p, q²} Unit | - | where: => refers to a fresh root capability created in method inner + | where: => refers to a fresh root capability in the type of parameter q | q is a parameter in method inner | q² is a parameter in method test | @@ -33,7 +33,7 @@ | Found: (q : () => Unit) | Required: () ->{p} Unit | - | where: => refers to a fresh root capability created in method inner + | where: => refers to a fresh root capability in the type of parameter q | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/outer-var.scala:17:57 --------------------------------------------------------- diff --git a/tests/neg-custom-args/captures/reaches.check b/tests/neg-custom-args/captures/reaches.check index 837571a98473..f99059f8f1af 100644 --- a/tests/neg-custom-args/captures/reaches.check +++ b/tests/neg-custom-args/captures/reaches.check @@ -1,22 +1,22 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:22:13 -------------------------------------- 22 | usingFile: f => // error | ^ - | Found: (f: File^?) ->? Unit - | Required: (f: File^) => Unit + |Found: (f: File^?) ->? Unit + |Required: (f: File^) => Unit | - | where: => refers to a fresh root capability created in method runAll0 - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in method runAll0 when checking argument to parameter f of method usingFile + | ^ refers to the universal root capability 23 | cur = (() => f.write()) :: Nil | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:32:13 -------------------------------------- 32 | usingFile: f => // error | ^ - | Found: (f: File^?) ->? Unit - | Required: (f: File^) => Unit + |Found: (f: File^?) ->? Unit + |Required: (f: File^) => Unit | - | where: => refers to a fresh root capability created in method runAll1 - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in method runAll1 when checking argument to parameter f of method usingFile + | ^ refers to the universal root capability 33 | cur.set: 34 | (() => f.write()) :: Nil | @@ -27,7 +27,7 @@ | Mutable variable cur cannot have type List[box () => Unit] since | the part box () => Unit of that type captures the root capability `cap`. | - | where: => refers to a fresh root capability created in variable cur + | where: => refers to a fresh root capability in the type of variable cur -- Error: tests/neg-custom-args/captures/reaches.scala:44:16 ----------------------------------------------------------- 44 | val cur = Ref[List[Proc]](xs) // error | ^^^^^^^^^^ @@ -42,7 +42,7 @@ | Required: () =>² Unit | | where: => refers to the universal root capability - | =>² refers to a fresh root capability created in value next + | =>² refers to a fresh root capability in the type of value next | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:48:20 -------------------------------------- @@ -69,17 +69,17 @@ | Required: (x: File^) -> File^² | | where: ^ refers to the universal root capability - | ^² refers to a fresh root capability created in value id + | ^² refers to a fresh root capability in the type of value id | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:62:38 -------------------------------------- 62 | val leaked = usingFile[File^{id*}]: f => // error // error | ^ - | Found: (f: File^?) ->? box File^? - | Required: (f: File^) => box File^{id*} + |Found: (f: File^?) ->? box File^? + |Required: (f: File^) => box File^{id*} | - | where: => refers to a fresh root capability created in value leaked - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in value leaked when checking argument to parameter f of method usingFile + | ^ refers to the universal root capability 63 | val f1: File^{id*} = id(f) 64 | f1 | @@ -101,11 +101,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:70:38 -------------------------------------- 70 | val leaked = usingFile[File^{id*}]: f => // error // error | ^ - | Found: (f: File^?) ->? box File^? - | Required: (f: File^) => box File^{id*} + |Found: (f: File^?) ->? box File^? + |Required: (f: File^) => box File^{id*} | - | where: => refers to a fresh root capability created in value leaked - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in value leaked when checking argument to parameter f of method usingFile + | ^ refers to the universal root capability 71 | val f1: File^{id*} = id(f) 72 | f1 | @@ -118,11 +118,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:91:10 -------------------------------------- 91 | ps.map((x, y) => compose1(x, y)) // error | ^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (x$1: (box A^ ->? A^?, box A^ ->? A^?)^?) ->? box A^? ->? A^? - | Required: (x$1: (box A ->{ps*} A, box A ->{ps*} A)) => box A^? ->? A^? + |Found: (x$1: (box A^ ->? A^?, box A^ ->? A^?)^?) ->? box A^? ->? A^? + |Required: (x$1: (box A ->{ps*} A, box A ->{ps*} A)) => box A^? ->? A^? | - | where: => refers to a fresh root capability created in method mapCompose2 - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in method mapCompose2 when checking argument to parameter f of method map + | ^ refers to the universal root capability | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/reaches.scala:62:31 ----------------------------------------------------------- diff --git a/tests/neg-custom-args/captures/reaches2.check b/tests/neg-custom-args/captures/reaches2.check index 13d305189c3e..e6009b95d581 100644 --- a/tests/neg-custom-args/captures/reaches2.check +++ b/tests/neg-custom-args/captures/reaches2.check @@ -23,4 +23,4 @@ | Footprint set of second argument : {y, ps*} | The two sets overlap at : {ps*} | - |where: => refers to a fresh root capability created in anonymous fucntion of type (x$1: (box A^? ->{ps*} A^?, box A^? ->{ps*} A^?)^?): A^? ->{ps*} A^? + |where: => refers to a fresh root capability created in anonymous function of type (x$1: (box A^? ->{ps*} A^?, box A^? ->{ps*} A^?)^?): A^? ->{ps*} A^? when checking argument to parameter f of method compose1 diff --git a/tests/neg-custom-args/captures/real-try.check b/tests/neg-custom-args/captures/real-try.check index c430772a2a0c..4dd51e13f270 100644 --- a/tests/neg-custom-args/captures/real-try.check +++ b/tests/neg-custom-args/captures/real-try.check @@ -11,7 +11,7 @@ | that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. | - | where: cap is a fresh root capability created in method test + | where: cap is a fresh root capability in the type of given instance canThrow$1 15 | () => foo(1) 16 | catch 17 | case _: Ex1 => ??? @@ -23,7 +23,7 @@ | that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. | - | where: cap is a fresh root capability created in value x + | where: cap is a fresh root capability in the type of given instance canThrow$2 21 | () => foo(1) 22 | catch 23 | case _: Ex1 => ??? @@ -35,7 +35,7 @@ | that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. | - | where: cap is a fresh root capability created in value y + | where: cap is a fresh root capability in the type of given instance canThrow$3 27 | () => Cell(foo(1)) 28 | catch 29 | case _: Ex1 => ??? @@ -47,7 +47,7 @@ | the part box () ->{cap.rd} 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. | - | where: cap is a fresh root capability created in value b + | where: cap is a fresh root capability in the type of given instance canThrow$4 33 | Cell(() => foo(1)) 34 | catch 35 | case _: Ex1 => ??? diff --git a/tests/neg-custom-args/captures/sep-box.check b/tests/neg-custom-args/captures/sep-box.check index b1752f0ee582..c3f0bed0df4e 100644 --- a/tests/neg-custom-args/captures/sep-box.check +++ b/tests/neg-custom-args/captures/sep-box.check @@ -1,16 +1,16 @@ -- Error: tests/neg-custom-args/captures/sep-box.scala:41:9 ------------------------------------------------------------ 41 | par(h1.value, h2.value) // error | ^^^^^^^^ - | Separation failure: argument of type Ref^{h1.value*} - | to method par: (x: Ref^, y: Ref^): Unit - | corresponds to capture-polymorphic formal parameter x of type Ref^ - | and hides capabilities {h1.value*}. - | Some of these overlap with the captures of the second argument with type Ref^{h2.value*}. + |Separation failure: argument of type Ref^{h1.value*} + |to method par: (x: Ref^, y: Ref^): Unit + |corresponds to capture-polymorphic formal parameter x of type Ref^ + |and hides capabilities {h1.value*}. + |Some of these overlap with the captures of the second argument with type Ref^{h2.value*}. | - | Hidden set of current argument : {h1.value*} - | Hidden footprint of current argument : {h1.value*, xs*} - | Capture set of second argument : {h2.value*} - | Footprint set of second argument : {h2.value*, xs*} - | The two sets overlap at : {xs*} + | Hidden set of current argument : {h1.value*} + | Hidden footprint of current argument : {h1.value*, xs*} + | Capture set of second argument : {h2.value*} + | Footprint set of second argument : {h2.value*, xs*} + | The two sets overlap at : {xs*} | - | where: ^ refers to a fresh root capability created in method test + |where: ^ refers to a fresh root capability created in method test when checking argument to parameter x of method par diff --git a/tests/neg-custom-args/captures/sep-compose.check b/tests/neg-custom-args/captures/sep-compose.check index fb651c9b2904..b53bc3036d4e 100644 --- a/tests/neg-custom-args/captures/sep-compose.check +++ b/tests/neg-custom-args/captures/sep-compose.check @@ -1,128 +1,128 @@ -- Error: tests/neg-custom-args/captures/sep-compose.scala:32:7 -------------------------------------------------------- 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 x of type () => Unit - | and hides capabilities {f}. - | Some of these overlap with the captures of the second argument with type (f : () ->{a} Unit). - | - | Hidden set of current argument : {f} - | Hidden footprint of current argument : {f, a, io} - | Capture set of second argument : {f} - | Footprint set of second argument : {f, a, io} - | The two sets overlap at : {f, a, io} - | - | where: => refers to a fresh root capability created in method test + |Separation failure: argument of type (f : () ->{a} Unit) + |to method seq3: (x: () => Unit)(y: () ->{a, cap} Unit): Unit + |corresponds to capture-polymorphic formal parameter x of type () => Unit + |and hides capabilities {f}. + |Some of these overlap with the captures of the second argument with type (f : () ->{a} Unit). + | + | Hidden set of current argument : {f} + | Hidden footprint of current argument : {f, a, io} + | Capture set of second argument : {f} + | Footprint set of second argument : {f, a, io} + | The two sets overlap at : {f, a, io} + | + |where: => refers to a fresh root capability created in method test when checking argument to parameter x of method seq3 -- 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 hides capabilities {f}. - | Some of these overlap with the captures of the first argument with type (f : () ->{a} Unit). - | - | Hidden set of current argument : {f} - | Hidden footprint of current argument : {f, a, io} - | Capture set of first argument : {f} - | Footprint set of first argument : {f, a, io} - | The two sets overlap at : {f, a, io} - | - | where: => refers to a fresh root capability created in method test + |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 hides capabilities {f}. + |Some of these overlap with the captures of the first argument with type (f : () ->{a} Unit). + | + | Hidden set of current argument : {f} + | Hidden footprint of current argument : {f, a, io} + | Capture set of first argument : {f} + | Footprint set of first argument : {f, a, io} + | The two sets overlap at : {f, a, io} + | + |where: => refers to a fresh root capability created in method test when checking argument to parameter y of method seq4 -- Error: tests/neg-custom-args/captures/sep-compose.scala:34:7 -------------------------------------------------------- 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 x of type () => Unit - | and hides capabilities {f}. - | Some of these overlap with the captures of the second argument with type (f : () ->{a} Unit). - | - | Hidden set of current argument : {f} - | Hidden footprint of current argument : {f, a, io} - | Capture set of second argument : {f} - | Footprint set of second argument : {f, a, io} - | The two sets overlap at : {f, a, io} - | - | where: => refers to a fresh root capability created in method test + |Separation failure: argument of type (f : () ->{a} Unit) + |to method seq5: (x: () => Unit)(y: () => Unit): Unit + |corresponds to capture-polymorphic formal parameter x of type () => Unit + |and hides capabilities {f}. + |Some of these overlap with the captures of the second argument with type (f : () ->{a} Unit). + | + | Hidden set of current argument : {f} + | Hidden footprint of current argument : {f, a, io} + | Capture set of second argument : {f} + | Footprint set of second argument : {f, a, io} + | The two sets overlap at : {f, a, io} + | + |where: => refers to a fresh root capability created in method test when checking argument to parameter x of method seq5 -- Error: tests/neg-custom-args/captures/sep-compose.scala:35:7 -------------------------------------------------------- 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 x of type () => Unit - | and hides capabilities {f}. - | Some of these overlap with the captures of the second argument with type (f : () ->{a} Unit). - | - | Hidden set of current argument : {f} - | Hidden footprint of current argument : {f, a, io} - | Capture set of second argument : {f} - | Footprint set of second argument : {f, a, io} - | The two sets overlap at : {f, a, io} - | - | where: => refers to a fresh root capability created in method test + |Separation failure: argument of type (f : () ->{a} Unit) + |to method seq6: (x: () => Unit, y: () ->{a, cap} Unit): Unit + |corresponds to capture-polymorphic formal parameter x of type () => Unit + |and hides capabilities {f}. + |Some of these overlap with the captures of the second argument with type (f : () ->{a} Unit). + | + | Hidden set of current argument : {f} + | Hidden footprint of current argument : {f, a, io} + | Capture set of second argument : {f} + | Footprint set of second argument : {f, a, io} + | The two sets overlap at : {f, a, io} + | + |where: => refers to a fresh root capability created in method test when checking argument to parameter x of method seq6 -- 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 hides capabilities {f}. - | Some of these overlap with the captures of the first argument with type (f : () ->{a} Unit). - | - | Hidden set of current argument : {f} - | Hidden footprint of current argument : {f, a, io} - | Capture set of first argument : {f} - | Footprint set of first argument : {f, a, io} - | The two sets overlap at : {f, a, io} - | - | where: => refers to a fresh root capability created in method test + |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 hides capabilities {f}. + |Some of these overlap with the captures of the first argument with type (f : () ->{a} Unit). + | + | Hidden set of current argument : {f} + | Hidden footprint of current argument : {f, a, io} + | Capture set of first argument : {f} + | Footprint set of first argument : {f, a, io} + | The two sets overlap at : {f, a, io} + | + |where: => refers to a fresh root capability created in method test when checking argument to parameter y of method seq7 -- 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 hides capabilities {f}. - | Some of these overlap with the captures of the second argument with type (f : () ->{a} Unit). - | - | Hidden set of current argument : {f} - | Hidden footprint of current argument : {f, a, io} - | Capture set of second argument : {f} - | Footprint set of second argument : {f, a, io} - | The two sets overlap at : {f, a, io} - | - | where: => refers to a fresh root capability created in method test + |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 hides capabilities {f}. + |Some of these overlap with the captures of the second argument with type (f : () ->{a} Unit). + | + | Hidden set of current argument : {f} + | Hidden footprint of current argument : {f, a, io} + | Capture set of second argument : {f} + | Footprint set of second argument : {f, a, io} + | The two sets overlap at : {f, a, io} + | + |where: => refers to a fresh root capability created in method test when checking argument to parameter x of method seq8 -- 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 (() => Unit) ->{f} Unit - | corresponds to capture-polymorphic formal parameter v1 of type () => Unit - | and hides capabilities {f}. - | Some of these overlap with the captures of the function prefix. - | - | Hidden set of current argument : {f} - | Hidden footprint of current argument : {f, a, io} - | Capture set of function prefix : {p1} - | Footprint set of function prefix : {p1, f, a, io} - | The two sets overlap at : {f, a, io} - | - | where: => refers to a fresh root capability created in method test + |Separation failure: argument of type (f : () ->{a} Unit) + |to a function of type (() => Unit) ->{f} Unit + |corresponds to capture-polymorphic formal parameter v1 of type () => Unit + |and hides capabilities {f}. + |Some of these overlap with the captures of the function prefix. + | + | Hidden set of current argument : {f} + | Hidden footprint of current argument : {f, a, io} + | Capture set of function prefix : {p1} + | Footprint set of function prefix : {p1, f, a, io} + | The two sets overlap at : {f, a, io} + | + |where: => refers to a fresh root capability created in method test when checking argument to parameter v1 of method apply -- 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 hides capabilities {f}. - | Some of these overlap with the captures of the second argument with type (x : () ->{a} Unit). - | - | Hidden set of current argument : {f} - | Hidden footprint of current argument : {f, a, io} - | Capture set of second argument : {x} - | Footprint set of second argument : {x, a, io} - | The two sets overlap at : {a, io} - | - | where: => refers to a fresh root capability created in anonymous fucntion of type (x²: () ->{a} Unit): Unit + |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 hides capabilities {f}. + |Some of these overlap with the captures of the second argument with type (x : () ->{a} Unit). + | + | Hidden set of current argument : {f} + | Hidden footprint of current argument : {f, a, io} + | Capture set of second argument : {x} + | Footprint set of second argument : {x, a, io} + | The two sets overlap at : {a, io} + | + |where: => refers to a fresh root capability created in anonymous function of type (x²: () ->{a} Unit): Unit when checking argument to parameter x of method seq8 diff --git a/tests/neg-custom-args/captures/sep-consume.check b/tests/neg-custom-args/captures/sep-consume.check index 1add76a54b50..200b5b458070 100644 --- a/tests/neg-custom-args/captures/sep-consume.check +++ b/tests/neg-custom-args/captures/sep-consume.check @@ -5,7 +5,7 @@ | @consume parameter or was used as a prefix to a @consume method on line 18 | and therefore is no longer available. | - | where: ^ refers to a fresh root capability created in method test2 + | where: ^ refers to a fresh root capability in the type of parameter x -- Error: tests/neg-custom-args/captures/sep-consume.scala:21:16 ------------------------------------------------------- 21 | par(rx, () => x.put(42)) // error | ^ @@ -13,7 +13,7 @@ | @consume parameter or was used as a prefix to a @consume method on line 18 | and therefore is no longer available. | - | where: ^ refers to a fresh root capability created in method test2 + | where: ^ refers to a fresh root capability in the type of parameter x -- Error: tests/neg-custom-args/captures/sep-consume.scala:26:16 ------------------------------------------------------- 26 | def foo = bad(f) // error | ^ diff --git a/tests/neg-custom-args/captures/sep-counter.check b/tests/neg-custom-args/captures/sep-counter.check index cfee7e97b914..ee38eba5eab9 100644 --- a/tests/neg-custom-args/captures/sep-counter.check +++ b/tests/neg-custom-args/captures/sep-counter.check @@ -6,6 +6,6 @@ | Another part, box Ref^², captures capabilities {cap}. | The two sets overlap at cap of value c. | - | where: ^ refers to a fresh root capability created in method mkCounter - | ^² refers to a fresh root capability created in method mkCounter - | cap is a fresh root capability created in value c + | where: ^ refers to a fresh root capability in the result type of method mkCounter + | ^² refers to a fresh root capability in the result type of method mkCounter + | cap is a fresh root capability created in value c when constructing mutable Ref diff --git a/tests/neg-custom-args/captures/sep-curried.check b/tests/neg-custom-args/captures/sep-curried.check index 2ce6cf6bb8b6..1cdce6b2bb99 100644 --- a/tests/neg-custom-args/captures/sep-curried.check +++ b/tests/neg-custom-args/captures/sep-curried.check @@ -10,85 +10,85 @@ -- Error: tests/neg-custom-args/captures/sep-curried.scala:16:6 -------------------------------------------------------- 16 | foo(a)(a) // error | ^ - | Separation failure: argument of type (a : Ref[Int]^) - | to method foo: (x: Ref[Int]^)(y: Ref[Int]^{a}): Unit - | corresponds to capture-polymorphic formal parameter x of type Ref[Int]^² - | and hides capabilities {a}. - | Some of these overlap with the captures of the second argument with type (a : Ref[Int]^). + |Separation failure: argument of type (a : Ref[Int]^) + |to method foo: (x: Ref[Int]^)(y: Ref[Int]^{a}): Unit + |corresponds to capture-polymorphic formal parameter x of type Ref[Int]^² + |and hides capabilities {a}. + |Some of these overlap with the captures of the second argument with type (a : Ref[Int]^). | - | Hidden set of current argument : {a} - | Hidden footprint of current argument : {a} - | Capture set of second argument : {a} - | Footprint set of second argument : {a} - | The two sets overlap at : {a} + | Hidden set of current argument : {a} + | Hidden footprint of current argument : {a} + | Capture set of second argument : {a} + | Footprint set of second argument : {a} + | The two sets overlap at : {a} | - | where: ^ refers to a fresh root capability created in value a - | ^² refers to a fresh root capability created in method test0 + |where: ^ refers to a fresh root capability in the type of value a + | ^² refers to a fresh root capability created in method test0 when checking argument to parameter x of method foo -- Error: tests/neg-custom-args/captures/sep-curried.scala:22:44 ------------------------------------------------------- 22 | val f: (y: Ref[Int]^{a}) ->{a} Unit = foo(a) // error | ^ - | Separation failure: argument of type (a : Ref[Int]^) - | to a function of type (x: Ref[Int]^) -> (y: Ref[Int]^{a}) ->{x} Unit - | corresponds to capture-polymorphic formal parameter x of type Ref[Int]^² - | and hides capabilities {a}. - | Some of these overlap with the captures of the function result with type (y: Ref[Int]^{a}) ->{a} Unit. + |Separation failure: argument of type (a : Ref[Int]^) + |to a function of type (x: Ref[Int]^) -> (y: Ref[Int]^{a}) ->{x} Unit + |corresponds to capture-polymorphic formal parameter x of type Ref[Int]^² + |and hides capabilities {a}. + |Some of these overlap with the captures of the function result with type (y: Ref[Int]^{a}) ->{a} Unit. | - | Hidden set of current argument : {a} - | Hidden footprint of current argument : {a} - | Capture set of function result : {a} - | Footprint set of function result : {a} - | The two sets overlap at : {a} + | Hidden set of current argument : {a} + | Hidden footprint of current argument : {a} + | Capture set of function result : {a} + | Footprint set of function result : {a} + | The two sets overlap at : {a} | - | where: ^ refers to a fresh root capability created in value a - | ^² refers to a fresh root capability created in value f + |where: ^ refers to a fresh root capability in the type of value a + | ^² refers to a fresh root capability created in value f when checking argument to parameter x of method apply -- Error: tests/neg-custom-args/captures/sep-curried.scala:29:6 -------------------------------------------------------- 29 | foo(a)(a) // error | ^ - | Separation failure: argument of type (a : Ref[Int]^) - | to a function of type (x: Ref[Int]^) -> (y: Ref[Int]^{a}) ->{x} Unit - | corresponds to capture-polymorphic formal parameter x of type Ref[Int]^² - | and hides capabilities {a}. - | Some of these overlap with the captures of the function result with type (y: Ref[Int]^{a}) ->{a} Unit. + |Separation failure: argument of type (a : Ref[Int]^) + |to a function of type (x: Ref[Int]^) -> (y: Ref[Int]^{a}) ->{x} Unit + |corresponds to capture-polymorphic formal parameter x of type Ref[Int]^² + |and hides capabilities {a}. + |Some of these overlap with the captures of the function result with type (y: Ref[Int]^{a}) ->{a} Unit. | - | Hidden set of current argument : {a} - | Hidden footprint of current argument : {a} - | Capture set of function result : {a} - | Footprint set of function result : {a} - | The two sets overlap at : {a} + | Hidden set of current argument : {a} + | Hidden footprint of current argument : {a} + | Capture set of function result : {a} + | Footprint set of function result : {a} + | The two sets overlap at : {a} | - | where: ^ refers to a fresh root capability created in value a - | ^² refers to a fresh root capability created in method test2 + |where: ^ refers to a fresh root capability in the type of value a + | ^² refers to a fresh root capability created in method test2 when checking argument to parameter x of method apply -- Error: tests/neg-custom-args/captures/sep-curried.scala:35:9 -------------------------------------------------------- 35 | foo(a)(a) // error | ^ - | Separation failure: argument of type (a : Ref[Int]^) - | to a function of type (y: Ref[Int]^) ->{a} Unit - | corresponds to capture-polymorphic formal parameter y of type Ref[Int]^² - | and hides capabilities {a}. - | Some of these overlap with the captures of the function prefix. + |Separation failure: argument of type (a : Ref[Int]^) + |to a function of type (y: Ref[Int]^) ->{a} Unit + |corresponds to capture-polymorphic formal parameter y of type Ref[Int]^² + |and hides capabilities {a}. + |Some of these overlap with the captures of the function prefix. | - | Hidden set of current argument : {a} - | Hidden footprint of current argument : {a} - | Capture set of function prefix : {a} - | Footprint set of function prefix : {a} - | The two sets overlap at : {a} + | Hidden set of current argument : {a} + | Hidden footprint of current argument : {a} + | Capture set of function prefix : {a} + | Footprint set of function prefix : {a} + | The two sets overlap at : {a} | - | where: ^ refers to a fresh root capability created in value a - | ^² refers to a fresh root capability created in method test3 + |where: ^ refers to a fresh root capability in the type of value a + | ^² refers to a fresh root capability created in method test3 when checking argument to parameter y of method apply -- Error: tests/neg-custom-args/captures/sep-curried.scala:42:4 -------------------------------------------------------- 42 | f(a) // error | ^ - | Separation failure: argument of type (a : Ref[Int]^) - | to a function of type (y: Ref[Int]^) ->{a} Unit - | corresponds to capture-polymorphic formal parameter y of type Ref[Int]^² - | and hides capabilities {a}. - | Some of these overlap with the captures of the function prefix. + |Separation failure: argument of type (a : Ref[Int]^) + |to a function of type (y: Ref[Int]^) ->{a} Unit + |corresponds to capture-polymorphic formal parameter y of type Ref[Int]^² + |and hides capabilities {a}. + |Some of these overlap with the captures of the function prefix. | - | Hidden set of current argument : {a} - | Hidden footprint of current argument : {a} - | Capture set of function prefix : {f} - | Footprint set of function prefix : {f, a} - | The two sets overlap at : {a} + | Hidden set of current argument : {a} + | Hidden footprint of current argument : {a} + | Capture set of function prefix : {f} + | Footprint set of function prefix : {f, a} + | The two sets overlap at : {a} | - | where: ^ refers to a fresh root capability created in value a - | ^² refers to a fresh root capability created in method test4 + |where: ^ refers to a fresh root capability in the type of value a + | ^² refers to a fresh root capability created in method test4 when checking argument to parameter y of method apply diff --git a/tests/neg-custom-args/captures/sep-list.check b/tests/neg-custom-args/captures/sep-list.check index c2854f7801c3..5ecde5d6d2bd 100644 --- a/tests/neg-custom-args/captures/sep-list.check +++ b/tests/neg-custom-args/captures/sep-list.check @@ -1,16 +1,16 @@ -- Error: tests/neg-custom-args/captures/sep-list.scala:39:6 ----------------------------------------------------------- 39 | par(h1, h2) // error | ^^ - | Separation failure: argument of type (h1 : Ref^{xs*}) - | to method par: (x: Ref^, y: Ref^): Unit - | corresponds to capture-polymorphic formal parameter x of type Ref^ - | and hides capabilities {h1}. - | Some of these overlap with the captures of the second argument with type (h2 : Ref^{xs*}). + |Separation failure: argument of type (h1 : Ref^{xs*}) + |to method par: (x: Ref^, y: Ref^): Unit + |corresponds to capture-polymorphic formal parameter x of type Ref^ + |and hides capabilities {h1}. + |Some of these overlap with the captures of the second argument with type (h2 : Ref^{xs*}). | - | Hidden set of current argument : {h1} - | Hidden footprint of current argument : {h1, xs*} - | Capture set of second argument : {h2} - | Footprint set of second argument : {h2, xs*} - | The two sets overlap at : {xs*} + | Hidden set of current argument : {h1} + | Hidden footprint of current argument : {h1, xs*} + | Capture set of second argument : {h2} + | Footprint set of second argument : {h2, xs*} + | The two sets overlap at : {xs*} | - | where: ^ refers to a fresh root capability created in method test + |where: ^ refers to a fresh root capability created in method test when checking argument to parameter x of method par diff --git a/tests/neg-custom-args/captures/sep-pairs.check b/tests/neg-custom-args/captures/sep-pairs.check index dc59a1ede338..08e8b6e90287 100644 --- a/tests/neg-custom-args/captures/sep-pairs.check +++ b/tests/neg-custom-args/captures/sep-pairs.check @@ -4,7 +4,7 @@ | Found: box Ref^ | Required: box Ref^? | - | where: ^ refers to a fresh root capability created in method twoRefsBad + | where: ^ refers to a fresh root capability created in method twoRefsBad when constructing mutable Ref | | | Note that the universal capability `cap` @@ -14,14 +14,14 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/sep-pairs.scala:32:17 ------------------------------------ 32 | Pair(Ref(), Ref()) // error // error: universal capability cannot be included in capture set | ^^^^^ - | Found: box Ref^ - | Required: box Ref^? + | Found: box Ref^ + | Required: box Ref^? | - | where: ^ refers to a fresh root capability created in method twoRefsBad + | where: ^ refers to a fresh root capability created in method twoRefsBad when constructing mutable Ref | | - | Note that the universal capability `cap` - | 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` -- Error: tests/neg-custom-args/captures/sep-pairs.scala:15:10 --------------------------------------------------------- @@ -32,8 +32,8 @@ | Another part, box Ref^², captures capabilities {r0}. | The two sets overlap at {r0}. | - | where: ^ refers to a fresh root capability created in value r1 - | ^² refers to a fresh root capability created in value r1 + | where: ^ refers to a fresh root capability in the type of value r1 + | ^² refers to a fresh root capability in the type of value r1 -- Error: tests/neg-custom-args/captures/sep-pairs.scala:13:9 ---------------------------------------------------------- 13 |def bad: Pair[Ref^, Ref^] = // error: overlap at r1*, r0 | ^^^^^^^^^^^^^^^^ @@ -42,10 +42,10 @@ | Another part, box Ref^², captures capabilities {cap, cap², r1*, r0}. | The two sets overlap at {r1*, r0}. | - | where: ^ refers to a fresh root capability created in method bad - | ^² refers to a fresh root capability created in method bad - | cap is a fresh root capability created in value r1 - | cap² is a fresh root capability created in value r1 + | where: ^ refers to a fresh root capability in the result type of method bad + | ^² refers to a fresh root capability in the result type of method bad + | cap is a fresh root capability in the type of value r1 + | cap² is a fresh root capability in the type of value r1 -- Error: tests/neg-custom-args/captures/sep-pairs.scala:44:18 --------------------------------------------------------- 44 | val sameToPair: Pair[Ref^, Ref^] = Pair(fstSame, sndSame) // error | ^^^^^^^^^^^^^^^^ @@ -54,5 +54,5 @@ | Another part, box Ref^², captures capabilities {sndSame}. | The two sets overlap at cap of value same. | - | where: ^ refers to a fresh root capability created in value sameToPair - | ^² refers to a fresh root capability created in value sameToPair + | where: ^ refers to a fresh root capability in the type of value sameToPair + | ^² refers to a fresh root capability in the type of value sameToPair diff --git a/tests/neg-custom-args/captures/sep-use.check b/tests/neg-custom-args/captures/sep-use.check index c52d1598be30..5d8636007fdc 100644 --- a/tests/neg-custom-args/captures/sep-use.check +++ b/tests/neg-custom-args/captures/sep-use.check @@ -5,7 +5,7 @@ | of value x with type () => Unit. | This type hides capabilities {io} | - | where: => refers to a fresh root capability created in value x + | where: => refers to a fresh root capability in the type of value x -- Error: tests/neg-custom-args/captures/sep-use.scala:12:12 ----------------------------------------------------------- 12 | def x: () => Unit = () => println(io) // error | ^^^^^^^^^^ @@ -17,7 +17,7 @@ | of method x with result type () => Unit. | This type hides capabilities {io} | - | where: => refers to a fresh root capability created in method x + | where: => refers to a fresh root capability in the result type of method x -- Error: tests/neg-custom-args/captures/sep-use.scala:18:10 ----------------------------------------------------------- 18 | def xx: (y: Int) => Unit = _ => println(io) // error | ^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ | of method xx with result type (y: Int) => Unit. | This type hides capabilities {io} | - | where: => refers to a fresh root capability created in method xx + | where: => refers to a fresh root capability in the result type of method xx -- Error: tests/neg-custom-args/captures/sep-use.scala:24:19 ----------------------------------------------------------- 24 | def xxx(y: Int): Object^ = io // error | ^^^^^^^ @@ -41,4 +41,4 @@ | of method xxx with result type Object^. | This type hides capabilities {io} | - | where: ^ refers to a fresh root capability created in method xxx + | where: ^ refers to a fresh root capability in the result type of method xxx diff --git a/tests/neg-custom-args/captures/sep-use2.check b/tests/neg-custom-args/captures/sep-use2.check index d3a22850e4f9..ea990204e194 100644 --- a/tests/neg-custom-args/captures/sep-use2.check +++ b/tests/neg-custom-args/captures/sep-use2.check @@ -9,7 +9,7 @@ | of method cc with result type Object^. | This type hides capabilities {c} | - | where: ^ refers to a fresh root capability created in method cc + | where: ^ refers to a fresh root capability in the result type of method cc -- Error: tests/neg-custom-args/captures/sep-use2.scala:12:10 ---------------------------------------------------------- 12 | val x4: Object^ = // error | ^^^^^^^ @@ -26,25 +26,25 @@ | of method cc with result type Object^. | This type hides capabilities {c} | - | where: ^ refers to a fresh root capability created in method cc + | where: ^ refers to a fresh root capability in the result type of method cc -- Error: tests/neg-custom-args/captures/sep-use2.scala:18:8 ----------------------------------------------------------- 18 | { f(cc) } // error // error | ^^ - | Separation failure: argument of type (cc : -> Object^) - | to a function of type (x: Object^) ->{c} Object^ - | corresponds to capture-polymorphic formal parameter x of type Object^² - | and hides capabilities {cap, c}. - | Some of these overlap with the captures of the function prefix. + |Separation failure: argument of type (cc : -> Object^) + |to a function of type (x: Object^) ->{c} Object^ + |corresponds to capture-polymorphic formal parameter x of type Object^² + |and hides capabilities {cap, c}. + |Some of these overlap with the captures of the function prefix. | - | Hidden set of current argument : {cap, c} - | Hidden footprint of current argument : {c} - | Capture set of function prefix : {f} - | Footprint set of function prefix : {f, c} - | The two sets overlap at : {c} + | Hidden set of current argument : {cap, c} + | Hidden footprint of current argument : {c} + | Capture set of function prefix : {f} + | Footprint set of function prefix : {f, c} + | The two sets overlap at : {c} | - | where: ^ refers to a fresh root capability created in method cc - | ^² refers to a fresh root capability created in value x1 - | cap is a fresh root capability created in method cc + |where: ^ refers to a fresh root capability in the result type of method cc + | ^² refers to a fresh root capability created in value x1 when checking argument to parameter x of method apply + | cap is a fresh root capability in the result type of method cc -- Error: tests/neg-custom-args/captures/sep-use2.scala:20:6 ----------------------------------------------------------- 20 | { f(c) } // error // error | ^ @@ -52,7 +52,7 @@ | of method cc with result type Object^. | This type hides capabilities {c} | - | where: ^ refers to a fresh root capability created in method cc + | where: ^ refers to a fresh root capability in the result type of method cc -- Error: tests/neg-custom-args/captures/sep-use2.scala:20:8 ----------------------------------------------------------- 20 | { f(c) } // error // error | ^ @@ -60,21 +60,21 @@ | of method cc with result type Object^. | This type hides capabilities {c} | - | where: ^ refers to a fresh root capability created in method cc + | where: ^ refers to a fresh root capability in the result type of method cc -- Error: tests/neg-custom-args/captures/sep-use2.scala:24:8 ----------------------------------------------------------- 24 | { f(c) } // error | ^ - | Separation failure: argument of type (c : Object^) - | to a function of type Object^ ->{c} Object^ - | corresponds to capture-polymorphic formal parameter v1 of type Object^² - | and hides capabilities {c}. - | Some of these overlap with the captures of the function prefix. + |Separation failure: argument of type (c : Object^) + |to a function of type Object^ ->{c} Object^ + |corresponds to capture-polymorphic formal parameter v1 of type Object^² + |and hides capabilities {c}. + |Some of these overlap with the captures of the function prefix. | - | Hidden set of current argument : {c} - | Hidden footprint of current argument : {c} - | Capture set of function prefix : {f} - | Footprint set of function prefix : {f, c} - | The two sets overlap at : {c} + | Hidden set of current argument : {c} + | Hidden footprint of current argument : {c} + | Capture set of function prefix : {f} + | Footprint set of function prefix : {f, c} + | The two sets overlap at : {c} | - | where: ^ refers to a fresh root capability created in method test3 - | ^² refers to a fresh root capability created in value x4 + |where: ^ refers to a fresh root capability in the type of parameter c + | ^² refers to a fresh root capability created in value x4 when checking argument to parameter v1 of method apply diff --git a/tests/neg-custom-args/captures/sepchecks2.check b/tests/neg-custom-args/captures/sepchecks2.check index 1a9adf7c5d1a..bb1c2bcaa0bd 100644 --- a/tests/neg-custom-args/captures/sepchecks2.check +++ b/tests/neg-custom-args/captures/sepchecks2.check @@ -5,23 +5,23 @@ | of value xs with type List[box () => Unit]. | This type hides capabilities {c} | - | where: => refers to a fresh root capability created in value xs + | where: => refers to a fresh root capability in the type of value xs -- Error: tests/neg-custom-args/captures/sepchecks2.scala:13:7 --------------------------------------------------------- 13 | foo((() => println(c)) :: Nil, c) // error | ^^^^^^^^^^^^^^^^^^^^^^^^ - | Separation failure: argument of type List[box () ->{c} Unit] - | to method foo: (xs: List[box () => Unit], y: Object^): Nothing - | corresponds to capture-polymorphic formal parameter xs of type List[box () => Unit] - | and hides capabilities {c}. - | Some of these overlap with the captures of the second argument with type (c : Object^). + |Separation failure: argument of type List[box () ->{c} Unit] + |to method foo: (xs: List[box () => Unit], y: Object^): Nothing + |corresponds to capture-polymorphic formal parameter xs of type List[box () => Unit] + |and hides capabilities {c}. + |Some of these overlap with the captures of the second argument with type (c : Object^). | - | Hidden set of current argument : {c} - | Hidden footprint of current argument : {c} - | Capture set of second argument : {c} - | Footprint set of second argument : {c} - | The two sets overlap at : {c} + | Hidden set of current argument : {c} + | Hidden footprint of current argument : {c} + | Capture set of second argument : {c} + | Footprint set of second argument : {c} + | The two sets overlap at : {c} | - | where: => refers to a fresh root capability created in method Test2 + |where: => refers to a fresh root capability created in method Test2 when checking argument to parameter xs of method foo -- Error: tests/neg-custom-args/captures/sepchecks2.scala:14:10 -------------------------------------------------------- 14 | val x1: (Object^, Object^) = (c, c) // error | ^^^^^^^^^^^^^^^^^^ @@ -30,8 +30,8 @@ | Another part, box Object^², captures capabilities {c}. | The two sets overlap at {c}. | - | where: ^ refers to a fresh root capability created in value x1 - | ^² refers to a fresh root capability created in value x1 + | where: ^ refers to a fresh root capability in the type of value x1 + | ^² refers to a fresh root capability in the type of value x1 -- Error: tests/neg-custom-args/captures/sepchecks2.scala:15:10 -------------------------------------------------------- 15 | val x2: (Object^, Object^{d}) = (d, d) // error | ^^^^^^^^^^^^^^^^^^^^^ @@ -40,17 +40,17 @@ | Another part, box Object^{d}, captures capabilities {d}. | The two sets overlap at {d}. | - | where: ^ refers to a fresh root capability created in value x2 + | where: ^ refers to a fresh root capability in the type of value x2 -- 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 capabilities {c}. - | Another part, box Object^², captures capabilities {c}. - | The two sets overlap at {c}. + |Separation failure in the argument's adapted type (box Object^, box Object^²). + |One part, box Object^, hides capabilities {c}. + |Another part, box Object^², captures capabilities {c}. + |The two sets overlap at {c}. | - | where: ^ refers to a fresh root capability created in method Test6 - | ^² refers to a fresh root capability created in method Test6 + |where: ^ refers to a fresh root capability created in method Test6 when checking argument to parameter x of method bar + | ^² refers to a fresh root capability created in method Test6 when checking argument to parameter x of method bar -- Error: tests/neg-custom-args/captures/sepchecks2.scala:30:9 --------------------------------------------------------- 30 | val x: (Object^, Object^{c}) = (d, c) // error | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/neg-custom-args/captures/sepchecks4.check b/tests/neg-custom-args/captures/sepchecks4.check index 0b5c9ce73ccf..7b587363d1cd 100644 --- a/tests/neg-custom-args/captures/sepchecks4.check +++ b/tests/neg-custom-args/captures/sepchecks4.check @@ -11,16 +11,16 @@ -- Error: tests/neg-custom-args/captures/sepchecks4.scala:12:6 --------------------------------------------------------- 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 op1 of type () => Unit - | and hides capabilities {io}. - | Some of these overlap with the captures of the second argument with type () ->{io} Unit. + |Separation failure: argument of type () ->{io} Unit + |to method par: (op1: () => Unit)(op2: () => Unit): Unit + |corresponds to capture-polymorphic formal parameter op1 of type () => Unit + |and hides capabilities {io}. + |Some of these overlap with the captures of the second argument with type () ->{io} Unit. | - | Hidden set of current argument : {io} - | Hidden footprint of current argument : {io} - | Capture set of second argument : {io} - | Footprint set of second argument : {io} - | The two sets overlap at : {io} + | Hidden set of current argument : {io} + | Hidden footprint of current argument : {io} + | Capture set of second argument : {io} + | Footprint set of second argument : {io} + | The two sets overlap at : {io} | - | where: => refers to a fresh root capability created in method test + |where: => refers to a fresh root capability created in method test when checking argument to parameter op1 of method par diff --git a/tests/neg-custom-args/captures/sepchecks5.check b/tests/neg-custom-args/captures/sepchecks5.check index fb8595199852..63ec3bc73431 100644 --- a/tests/neg-custom-args/captures/sepchecks5.check +++ b/tests/neg-custom-args/captures/sepchecks5.check @@ -15,4 +15,4 @@ | @consume parameter or was used as a prefix to a @consume method on line 19 | and therefore is no longer available. | - | where: ^ refers to a fresh root capability created in method test + | where: ^ refers to a fresh root capability in the type of parameter io diff --git a/tests/neg-custom-args/captures/simple-using.check b/tests/neg-custom-args/captures/simple-using.check index 5280705d51dc..6e32260d3322 100644 --- a/tests/neg-custom-args/captures/simple-using.check +++ b/tests/neg-custom-args/captures/simple-using.check @@ -1,14 +1,14 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/simple-using.scala:8:15 ---------------------------------- 8 | usingLogFile { f => () => f.write(2) } // error | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (f: java.io.FileOutputStream^) ->? box () ->{f} Unit - | Required: (f: java.io.FileOutputStream^) => box () ->? Unit + |Found: (f: java.io.FileOutputStream^) ->? box () ->{f} Unit + |Required: (f: java.io.FileOutputStream^) => box () ->? Unit | - | where: => refers to a fresh root capability created in method test - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in method test when checking argument to parameter op of method usingLogFile + | ^ refers to the universal root capability | | - | Note that reference f.type - | cannot be included in outer capture set ? + |Note that reference f.type + |cannot be included in outer capture set ? | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/try.check b/tests/neg-custom-args/captures/try.check index ddac93881450..7f420872eb25 100644 --- a/tests/neg-custom-args/captures/try.check +++ b/tests/neg-custom-args/captures/try.check @@ -8,12 +8,12 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:23:49 ------------------------------------------ 23 | val a = handle[Exception, CanThrow[Exception]] { // error // error | ^ - | Found: (x: CT[Exception]^) ->? box CT[Exception]^{x} - | Required: (x: CT[Exception]^) => box CT[Exception]^² + |Found: (x: CT[Exception]^) ->? box CT[Exception]^{x} + |Required: (x: CT[Exception]^) => box CT[Exception]^² | - | where: => refers to a fresh root capability created in value a - | ^ refers to the universal root capability - | ^² refers to a fresh root capability created in value a + |where: => refers to a fresh root capability created in value a when checking argument to parameter op of method handle + | ^ refers to the universal root capability + | ^² refers to a fresh root capability created in value a when checking argument to parameter op of method handle 24 | (x: CanThrow[Exception]) => x 25 | }{ | @@ -21,11 +21,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:29:43 ------------------------------------------ 29 | val b = handle[Exception, () -> Nothing] { // error | ^ - | Found: (x: CT[Exception]^) ->? () ->{x} Nothing - | Required: (x: CT[Exception]^) => () -> Nothing + |Found: (x: CT[Exception]^) ->? () ->{x} Nothing + |Required: (x: CT[Exception]^) => () -> Nothing | - | where: => refers to a fresh root capability created in value b - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in value b when checking argument to parameter op of method handle + | ^ refers to the universal root capability 30 | (x: CanThrow[Exception]) => () => raise(new Exception)(using x) 31 | } { | @@ -33,15 +33,15 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:35:18 ------------------------------------------ 35 | val xx = handle { // error | ^ - | Found: (x: CT[Exception]^) ->? box () ->{x} Int - | Required: (x: CT[Exception]^) => box () ->? Int + |Found: (x: CT[Exception]^) ->? box () ->{x} Int + |Required: (x: CT[Exception]^) => box () ->? Int | - | where: => refers to a fresh root capability created in value xx - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in value xx when checking argument to parameter op of method handle + | ^ refers to the universal root capability | | - | Note that reference x.type - | cannot be included in outer capture set ? + |Note that reference x.type + |cannot be included in outer capture set ? 36 | (x: CanThrow[Exception]) => 37 | () => 38 | raise(new Exception)(using x) @@ -52,15 +52,15 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:47:31 ------------------------------------------ 47 |val global: () -> Int = handle { // error | ^ - | Found: (x: CT[Exception]^) ->? box () ->{x} Int - | Required: (x: CT[Exception]^) => box () ->? Int + |Found: (x: CT[Exception]^) ->? box () ->{x} Int + |Required: (x: CT[Exception]^) => box () ->? Int | - | where: => refers to a fresh root capability created in value global - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in value global when checking argument to parameter op of method handle + | ^ refers to the universal root capability | | - | Note that reference x.type - | cannot be included in outer capture set ? + |Note that reference x.type + |cannot be included in outer capture set ? 48 | (x: CanThrow[Exception]) => 49 | () => 50 | raise(new Exception)(using x) diff --git a/tests/neg-custom-args/captures/unsound-reach-4.check b/tests/neg-custom-args/captures/unsound-reach-4.check index 29e317b90396..850388bf1c33 100644 --- a/tests/neg-custom-args/captures/unsound-reach-4.check +++ b/tests/neg-custom-args/captures/unsound-reach-4.check @@ -11,7 +11,7 @@ | Found: Bar^? | Required: Foo[box File^] | - | where: ^ refers to a fresh root capability created in value backdoor + | where: ^ refers to a fresh root capability in the type of value backdoor | | longer explanation available when compiling with `-explain` -- [E164] Declaration Error: tests/neg-custom-args/captures/unsound-reach-4.scala:17:6 --------------------------------- diff --git a/tests/neg-custom-args/captures/unsound-reach.check b/tests/neg-custom-args/captures/unsound-reach.check index f5e0249da9af..f8ddb725598d 100644 --- a/tests/neg-custom-args/captures/unsound-reach.check +++ b/tests/neg-custom-args/captures/unsound-reach.check @@ -18,6 +18,6 @@ | Found: Bar^? | Required: Foo[box File^] | - | where: ^ refers to a fresh root capability created in value backdoor + | where: ^ refers to a fresh root capability in the type of value backdoor | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/usingLogFile.check b/tests/neg-custom-args/captures/usingLogFile.check index 5c14890efc52..86ddcb17312d 100644 --- a/tests/neg-custom-args/captures/usingLogFile.check +++ b/tests/neg-custom-args/captures/usingLogFile.check @@ -1,56 +1,56 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/usingLogFile.scala:22:27 --------------------------------- 22 | val later = usingLogFile { f => () => f.write(0) } // error | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (f: java.io.FileOutputStream^) ->? box () ->{f} Unit - | Required: (f: java.io.FileOutputStream^) => box () ->? Unit + |Found: (f: java.io.FileOutputStream^) ->? box () ->{f} Unit + |Required: (f: java.io.FileOutputStream^) => box () ->? Unit | - | where: => refers to a fresh root capability created in value later - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in value later when checking argument to parameter op of method usingLogFile + | ^ refers to the universal root capability | | - | Note that reference f.type - | cannot be included in outer capture set ? + |Note that reference f.type + |cannot be included in outer capture set ? | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/usingLogFile.scala:27:36 --------------------------------- 27 | private val later2 = usingLogFile { f => Cell(() => f.write(0)) } // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (f: java.io.FileOutputStream^) ->? box Test2.Cell[box () ->{f} Unit]^? - | Required: (f: java.io.FileOutputStream^) => box Test2.Cell[box () ->? Unit]^? + |Found: (f: java.io.FileOutputStream^) ->? box Test2.Cell[box () ->{f} Unit]^? + |Required: (f: java.io.FileOutputStream^) => box Test2.Cell[box () ->? Unit]^? | - | where: => refers to a fresh root capability created in value later2 - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in value later2 when checking argument to parameter op of method usingLogFile + | ^ refers to the universal root capability | | - | Note that reference f.type - | cannot be included in outer capture set ? + |Note that reference f.type + |cannot be included in outer capture set ? | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/usingLogFile.scala:43:33 --------------------------------- 43 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (f: java.io.OutputStream^) ->? box Int ->{f} Unit - | Required: (f: java.io.OutputStream^) => box Int ->? Unit + |Found: (f: java.io.OutputStream^) ->? box Int ->{f} Unit + |Required: (f: java.io.OutputStream^) => box Int ->? Unit | - | where: => refers to a fresh root capability created in value later - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in value later when checking argument to parameter op of method usingFile + | ^ refers to the universal root capability | | - | Note that reference f.type - | cannot be included in outer capture set ? + |Note that reference f.type + |cannot be included in outer capture set ? | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/usingLogFile.scala:52:6 ---------------------------------- 52 | usingLogger(_, l => () => l.log("test"))) // error after checking mapping scheme | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (_$1: java.io.OutputStream^) ->? box () ->{_$1} Unit - | Required: (_$1: java.io.OutputStream^) => box () ->? Unit + |Found: (_$1: java.io.OutputStream^) ->? box () ->{_$1} Unit + |Required: (_$1: java.io.OutputStream^) => box () ->? Unit | - | where: => refers to a fresh root capability created in value later - | ^ refers to the universal root capability + |where: => refers to a fresh root capability created in value later when checking argument to parameter op of method usingFile + | ^ refers to the universal root capability | | - | Note that reference _$1.type - | cannot be included in outer capture set ? + |Note that reference _$1.type + |cannot be included in outer capture set ? | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/widen-reach.check b/tests/neg-custom-args/captures/widen-reach.check index 3448088abc02..9898f1c242b4 100644 --- a/tests/neg-custom-args/captures/widen-reach.check +++ b/tests/neg-custom-args/captures/widen-reach.check @@ -12,7 +12,7 @@ | Required: (x: IO^) -> IO^² | | where: ^ refers to the universal root capability - | ^² refers to a fresh root capability created in value foo + | ^² refers to a fresh root capability in the type of value foo | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/widen-reach.scala:13:26 ------------------------------------------------------- From 6df16c84f9786369a286cd5362e4ab447b3e83fe Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 4 May 2025 11:16:50 +0200 Subject: [PATCH 08/19] Refactor: Instead of purpose string, use Origin enum --- .../src/dotty/tools/dotc/cc/CaptureOps.scala | 4 +- .../src/dotty/tools/dotc/cc/CaptureSet.scala | 8 +-- .../dotty/tools/dotc/cc/CheckCaptures.scala | 27 +++---- compiler/src/dotty/tools/dotc/cc/Setup.scala | 8 +-- .../src/dotty/tools/dotc/cc/Synthetics.scala | 2 +- compiler/src/dotty/tools/dotc/cc/root.scala | 72 ++++++++++++++++--- .../dotty/tools/dotc/reporting/Message.scala | 9 +-- tests/neg-custom-args/captures/i15116.check | 8 +-- 8 files changed, 88 insertions(+), 50 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index ff27573fe716..8690edfe86ee 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -464,7 +464,7 @@ extension (tp: Type) // preceding arguments are known to be always pure t.derivedFunctionOrMethod( args, - apply(root.resultToFresh(res, i"when instantiating $t"))) + apply(root.resultToFresh(res, root.Origin.ResultInstance(t, NoSymbol)))) else t case _ => @@ -798,7 +798,7 @@ abstract class DeepTypeAccumulator[T](using Context) extends TypeAccumulator[T]: this(acc, parent) case t @ FunctionOrMethod(args, res) => if args.forall(_.isAlwaysPure) then - this(acc, root.resultToFresh(res, i" when instantiating $t")) + this(acc, root.resultToFresh(res, root.Origin.ResultInstance(t, NoSymbol))) else acc case _ => foldOver(acc, t) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index c186ff855fb2..1fb8c96ad509 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -423,8 +423,8 @@ object CaptureSet: def universalImpliedByCapability(using Context) = defn.universalCSImpliedByCapability - def fresh(purpose: => String)(using Context): CaptureSet = - root.Fresh(purpose).singletonCaptureSet + def fresh(origin: root.Origin)(using Context): CaptureSet = + root.Fresh(origin).singletonCaptureSet /** The shared capture set `{cap.rd}` */ def shared(using Context): CaptureSet = @@ -685,7 +685,7 @@ object CaptureSet: def solve()(using Context): Unit = CCState.withCapAsRoot: // // OK here since we infer parameter types that get checked later val approx = upperApprox(empty) - .map(root.CapToFresh("").inverse) // Fresh --> cap + .map(root.CapToFresh(root.Origin.Unknown).inverse) // Fresh --> cap .showing(i"solve $this = $result", capt) //println(i"solving var $this $approx ${approx.isConst} deps = ${deps.toList}") val newElems = approx.elems -- elems @@ -1388,7 +1388,7 @@ object CaptureSet: def capturingCase(acc: CaptureSet, parent: Type, refs: CaptureSet) = this(acc, parent) ++ refs def abstractTypeCase(acc: CaptureSet, t: TypeRef, upperBound: Type) = - if includeTypevars && upperBound.isExactlyAny then CaptureSet.fresh(i" when computing deep capture set of $t") + if includeTypevars && upperBound.isExactlyAny then fresh(root.Origin.DeepCS(t)) else this(acc, upperBound) collect(CaptureSet.empty, tp) diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 7db6f91eeea9..99c5e88a3c07 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -714,7 +714,7 @@ 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(root.Fresh(i" when instantiating argument of unsafeAssumePure"))) + val argType0 = recheck(arg, pt.stripCapturing.capturing(root.Fresh(root.Origin.UnsafeAssumePure))) val argType = if argType0.captureSet.isAlwaysEmpty then argType0 else argType0.widen.stripCapturing @@ -730,11 +730,7 @@ class CheckCaptures extends Recheck, SymTransformer: * charge the deep capture set of the actual argument to the environment. */ protected override def recheckArg(arg: Tree, formal: Type, pref: ParamRef, app: Apply)(using Context): Type = - val fn = app.symbol - def purpose = - if fn.exists then i" when checking argument to parameter ${pref.paramName} of $fn" - else "" - val freshenedFormal = root.capToFresh(formal, purpose) + val freshenedFormal = root.capToFresh(formal, root.Origin.Formal(pref, app)) val argType = recheck(arg, freshenedFormal) .showing(i"recheck arg $arg vs $freshenedFormal = $result", capt) if formal.hasAnnotation(defn.UseAnnot) || formal.hasAnnotation(defn.ConsumeAnnot) then @@ -768,11 +764,9 @@ class CheckCaptures extends Recheck, SymTransformer: */ protected override def recheckApplication(tree: Apply, qualType: Type, funType: MethodType, argTypes: List[Type])(using Context): Type = - val fn = tree.symbol - def methDescr = if fn.exists then i"$fn's type " else "" val appType = root.resultToFresh( super.recheckApplication(tree, qualType, funType, argTypes), - i" when instantiating $methDescr$funType") + root.Origin.ResultInstance(funType, tree.symbol)) val qualCaptures = qualType.captureSet val argCaptures = for (argType, formal) <- argTypes.lazyZip(funType.paramInfos) yield @@ -832,9 +826,9 @@ class CheckCaptures extends Recheck, SymTransformer: var refined: Type = core var allCaptures: CaptureSet = if core.derivesFromMutable then - initCs ++ CaptureSet.fresh(i" when constructing mutable $core") + initCs ++ root.Fresh(root.Origin.NewMutable(core)).singletonCaptureSet else if core.derivesFromCapability then - initCs ++ root.Fresh(i" when constructing Capability instance $core").readOnly.singletonCaptureSet + initCs ++ root.Fresh(root.Origin.NewCapability(core)).readOnly.singletonCaptureSet else initCs for (getterName, argType) <- mt.paramNames.lazyZip(argTypes) do val getter = cls.info.member(getterName).suchThat(_.isRefiningParamAccessor).symbol @@ -877,7 +871,7 @@ class CheckCaptures extends Recheck, SymTransformer: def methDescr = if meth.exists then i"$meth's type " else "" disallowCapInTypeArgs(tree.fun, meth, tree.args) val funType = super.recheckTypeApply(tree, pt) - val res = root.resultToFresh(funType, i" when instantiating $methDescr$funType") + val res = root.resultToFresh(funType, root.Origin.ResultInstance(funType, meth)) includeCallCaptures(tree.symbol, res, tree) checkContains(tree) res @@ -943,10 +937,8 @@ class CheckCaptures extends Recheck, SymTransformer: rinfo.instantiate(paramTypes) case _ => resType - val respt = root.resultToFresh(respt0, - i" when instantiating expected result type $respt0 of lambda") - val res = root.resultToFresh(mdef.tpt.nuType, - i" when instantiating result type ${mdef.tpt.nuType} of lambda") + val respt = root.resultToFresh(respt0, root.Origin.LambdaExpected(respt0)) + val res = root.resultToFresh(mdef.tpt.nuType, root.Origin.LambdaActual(mdef.tpt.nuType)) // We need to open existentials here in order not to get vars mixed up in them // We do the proper check with existentials when we are finished with the closure block. capt.println(i"pre-check closure $expr of type $res against $respt") @@ -1647,8 +1639,7 @@ class CheckCaptures extends Recheck, SymTransformer: otherTp.derivedTypeBounds( otherTp.lo, hi.derivedCapturingType(parent, - root.Fresh(i" when instantiating upper bound of member overridden by $member") - .singletonCaptureSet)))) + CaptureSet.fresh(root.Origin.OverriddenType(member)))))) case _ => None case _ => None case _ => None diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index d6cc9dc6b888..196bdff5a56a 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -473,11 +473,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: if sym.isType then stripImpliedCaptureSet(tp2) else tp2 if freshen then - root.capToFresh(tp3, - if sym.is(Method) then i" in the result type of $sym" - else if sym.exists then i" in the type of $sym" - else "") - .tap(addOwnerAsHidden(_, sym)) + root.capToFresh(tp3, root.Origin.InDecl(sym)).tap(addOwnerAsHidden(_, sym)) else tp3 end transformExplicitType @@ -574,7 +570,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: for case arg: TypeTree <- args do if defn.isTypeTestOrCast(fn.symbol) then arg.setNuType( - root.capToFresh(arg.tpe, i" of type argument ${arg.tpe}")) + root.capToFresh(arg.tpe, root.Origin.TypeArg(arg.tpe))) else transformTT(arg, NoSymbol, boxed = true) // type arguments in type applications are boxed diff --git a/compiler/src/dotty/tools/dotc/cc/Synthetics.scala b/compiler/src/dotty/tools/dotc/cc/Synthetics.scala index 0ec4d1e20a17..93215e77be96 100644 --- a/compiler/src/dotty/tools/dotc/cc/Synthetics.scala +++ b/compiler/src/dotty/tools/dotc/cc/Synthetics.scala @@ -117,7 +117,7 @@ object Synthetics: case info: MethodType => val paramInfo :: Nil = info.paramInfos: @unchecked val newParamInfo = CapturingType(paramInfo, - CaptureSet.fresh(i" when instantiating argument of unapply with type $info")) + CaptureSet.fresh(root.Origin.UnapplyInstance(info))) val trackedParam = info.paramRefs.head def newResult(tp: Type): Type = tp match case tp: MethodOrPoly => diff --git a/compiler/src/dotty/tools/dotc/cc/root.scala b/compiler/src/dotty/tools/dotc/cc/root.scala index bb8144bbd052..d41e594b821b 100644 --- a/compiler/src/dotty/tools/dotc/cc/root.scala +++ b/compiler/src/dotty/tools/dotc/cc/root.scala @@ -14,6 +14,7 @@ import NameOps.isImpureFunction import reporting.Message import util.{SimpleIdentitySet, EqHashMap} import util.Spans.NoSpan +import ast.tpd import annotation.constructorOnly /** A module defining three kinds of root capabilities @@ -59,9 +60,58 @@ import annotation.constructorOnly */ object root: + enum Origin: + case InDecl(sym: Symbol) + case TypeArg(tp: Type) + case UnsafeAssumePure + case Formal(pref: ParamRef, app: tpd.Apply) + case ResultInstance(methType: Type, meth: Symbol) + case UnapplyInstance(info: MethodType) + case NewMutable(tp: Type) + case NewCapability(tp: Type) + case LambdaExpected(respt: Type) + case LambdaActual(restp: Type) + case OverriddenType(member: Symbol) + case DeepCS(ref: TypeRef) + case Unknown + + def explanation(using Context): String = this match + case InDecl(sym: Symbol) => + if sym.is(Method) then i" in the result type of $sym" + else if sym.exists then i" in the type of $sym" + else "" + case TypeArg(tp: Type) => + i" of type argument $tp" + case UnsafeAssumePure => + " when instantiating argument of unsafeAssumePure" + case Formal(pref, app) => + val meth = app.symbol + if meth.exists + then i" when checking argument to parameter ${pref.paramName} of $meth" + else "" + case ResultInstance(mt, meth) => + val methDescr = if meth.exists then i"$meth's type " else "" + i" when instantiating $methDescr$mt" + case UnapplyInstance(info) => + i" when instantiating argument of unapply with type $info" + case NewMutable(tp) => + i" when constructing mutable $tp" + case NewCapability(tp) => + i" when constructing Capability instance $tp" + case LambdaExpected(respt) => + i" when instantiating expected result type $respt of lambda" + case LambdaActual(restp: Type) => + i" when instantiating result type $restp of lambda" + case OverriddenType(member: Symbol) => + i" when instantiating upper bound of member overridden by $member" + case DeepCS(ref: TypeRef) => + i" when computing deep capture set of $ref" + case Unknown => + "" + enum Kind: case Result(binder: MethodType) - case Fresh(hidden: CaptureSet.HiddenSet)(val purpose: () => String) + case Fresh(hidden: CaptureSet.HiddenSet)(val origin: Origin) case Global override def equals(other: Any): Boolean = @@ -129,10 +179,10 @@ object root: /** Constructor and extractor methods for "fresh" capabilities */ object Fresh: - def apply(using Context)(purpose: => String, owner: Symbol = ctx.owner): CaptureRef = + def apply(using Context)(origin: Origin, owner: Symbol = ctx.owner): CaptureRef = if ccConfig.useSepChecks then val hiddenSet = CaptureSet.HiddenSet(owner) - val res = AnnotatedType(cap, Annot(Kind.Fresh(hiddenSet)(() => purpose))) + val res = AnnotatedType(cap, Annot(Kind.Fresh(hiddenSet)(origin))) hiddenSet.owningCap = res //assert(hiddenSet.id != 3) res @@ -167,14 +217,14 @@ object root: /** Map each occurrence of cap to a different Fresh instance * Exception: CapSet^ stays as it is. */ - class CapToFresh(purpose: => String)(using Context) extends BiTypeMap, FollowAliasesMap: + class CapToFresh(origin: Origin)(using Context) extends BiTypeMap, FollowAliasesMap: thisMap => override def apply(t: Type) = if variance <= 0 then t else t match case t: CaptureRef if t.isCap => - Fresh(purpose) + Fresh(origin) case t @ CapturingType(parent: TypeRef, _) if parent.symbol == defn.Caps_CapSet => t case t @ CapturingType(_, _) => @@ -212,15 +262,15 @@ object root: end CapToFresh /** Maps cap to fresh */ - def capToFresh(tp: Type, purpose: => String)(using Context): Type = - if ccConfig.useSepChecks then CapToFresh(purpose)(tp) else tp + def capToFresh(tp: Type, origin: Origin)(using Context): Type = + if ccConfig.useSepChecks then CapToFresh(origin)(tp) else tp /** Maps fresh to cap */ def freshToCap(tp: Type)(using Context): Type = - if ccConfig.useSepChecks then CapToFresh("").inverse(tp) else tp + if ccConfig.useSepChecks then CapToFresh(Origin.Unknown).inverse(tp) else tp /** Map top-level free existential variables one-to-one to Fresh instances */ - def resultToFresh(tp: Type, purpose: => String)(using Context): Type = + def resultToFresh(tp: Type, origin: Origin)(using Context): Type = val subst = new TypeMap: val seen = EqHashMap[Annotation, CaptureRef]() var localBinders: SimpleIdentitySet[MethodType] = SimpleIdentitySet.empty @@ -228,7 +278,7 @@ object root: def apply(t: Type): Type = t match case t @ Result(binder) => if localBinders.contains(binder) then t // keep bound references - else seen.getOrElseUpdate(t.annot, Fresh(purpose)) // map free references to Fresh() + else seen.getOrElseUpdate(t.annot, Fresh(origin)) // map free references to Fresh() case t: MethodType => // skip parameters val saved = localBinders @@ -294,7 +344,7 @@ object root: val (k, v) = it.next if v.annot eq t.annot then ref = k if ref == null then - ref = Fresh("") + ref = Fresh(Origin.Unknown) seen(ref) = t ref case _ => mapOver(t) diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index cccd5b0284bc..5a9f867f006e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -185,10 +185,11 @@ object Message: else ref match case ref @ root.Fresh(hidden) => val (kind: root.Kind.Fresh) = ref.rootAnnot.kind: @unchecked - val purpose = kind.purpose() - val descr = - if purpose.startsWith(" in the ") then purpose - else i" created in ${ownerStr(hidden.owner)}$purpose" + val descr = kind.origin match + case origin @ root.Origin.InDecl(sym) if sym.exists => + origin.explanation + case origin => + i" created in ${ownerStr(hidden.owner)}${origin.explanation}" i"a fresh root capability$descr" case root.Result(binder) => i"a root capability associated with the result type of $binder" s"$relation $descr" diff --git a/tests/neg-custom-args/captures/i15116.check b/tests/neg-custom-args/captures/i15116.check index a9cd9df7b38f..50784d393162 100644 --- a/tests/neg-custom-args/captures/i15116.check +++ b/tests/neg-custom-args/captures/i15116.check @@ -4,7 +4,7 @@ | Found: Foo{val m: (Bar.this.m² : String^)}^{Bar.this.m²} | Required: Foo | - | where: ^ refers to a fresh root capability in the type of value m + | where: ^ refers to a fresh root capability in the type of value m² | m is a value in class Foo | m² is a value in class Bar | @@ -22,7 +22,7 @@ | Found: Foo{val m: (Baz.this.m² : String^)}^{Baz.this.m²} | Required: Foo | - | where: ^ refers to a fresh root capability in the type of value m + | where: ^ refers to a fresh root capability in the type of value m² | m is a value in class Foo | m² is a value in trait Baz | @@ -40,7 +40,7 @@ | Found: Foo{val m: (Bar1.this.m² : String^)}^{Bar1.this.m²} | Required: Foo | - | where: ^ refers to a fresh root capability in the type of value m + | where: ^ refers to a fresh root capability in the type of value m² | m is a value in class Foo | m² is a value in class Bar1 | @@ -58,7 +58,7 @@ | Found: Foo{val m: (Baz2.this.m² : String^)}^{Baz2.this.m²} | Required: Foo | - | where: ^ refers to a fresh root capability in the type of value m + | where: ^ refers to a fresh root capability in the type of value m² | m is a value in class Foo | m² is a value in trait Baz2 | From b005179551b60147e53a3d51a10569630ae0a2a7 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 4 May 2025 19:01:41 +0200 Subject: [PATCH 09/19] Implement level checking for Fresh roots --- .../src/dotty/tools/dotc/cc/CaptureRef.scala | 24 +++++++++++++++++++ .../tools/dotc/printing/PlainPrinter.scala | 2 +- tests/pos-custom-args/captures/skolems2.scala | 11 +++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala b/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala index 5dfacd8a5a35..388970ffc854 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala @@ -122,6 +122,30 @@ trait CaptureRef extends TypeProxy, ValueType: final def isExclusive(using Context): Boolean = !isReadOnly && (isRootCapability || captureSetOfInfo.isExclusive) + final def ccOwner(using Context): Symbol = this match + case root.Fresh(hidden) => + hidden.owner + case ref: TermRef => + if ref.isCap then NoSymbol + else ref.prefix match + case prefix: CaptureRef => prefix.ccOwner + case _ => ref.symbol + case ref: ThisType => + ref.cls + case QualifiedCapability(ref1) => + ref1.ccOwner + case _ => + NoSymbol + + final def adjustedOwner(using Context): Symbol = + def adjust(owner: Symbol): Symbol = + if !owner.exists + || owner.isClass && (!owner.is(Flags.Module) || owner.isStatic) + || owner.is(Flags.Method, butNot = Flags.Accessor) && !owner.isConstructor + then owner + else adjust(owner.owner) + adjust(ccOwner) + // With the support of paths, 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 diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 6f9d3a28f725..01658e2f3e9b 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -467,7 +467,7 @@ class PlainPrinter(_ctx: Context) extends Printer { vbleText ~ hashStr(binder) ~ Str(idStr).provided(showUniqueIds) case tp @ root.Fresh(hidden) => val idStr = if showUniqueIds then s"#${tp.rootAnnot.id}" else "" - if ccVerbose then s"" + if ccVerbose then s"" else if ccVerbose then "fresh" else "cap" case tp => toText(tp) diff --git a/tests/pos-custom-args/captures/skolems2.scala b/tests/pos-custom-args/captures/skolems2.scala index a9ff6e258317..53fbe935e362 100644 --- a/tests/pos-custom-args/captures/skolems2.scala +++ b/tests/pos-custom-args/captures/skolems2.scala @@ -13,6 +13,17 @@ def Test(@consume c: Object^, @consume f: Object^ => Object^) = val x4: Object^ = { unsafeAssumeSeparate(f)(cc) } +def Test2(@consume c: Object^, @consume f: Object^ => Object^) = + def cc(): Object^ = unsafeAssumeSeparate(c) + val x1 = + { f(cc()) } + val x2 = + f(cc()) + val x3: Object^ = + f(cc()) + val x4: Object^ = + { unsafeAssumeSeparate(f)(cc()) } + From c9683ac031b8a90b57c5d424a224f8c2ea4002ee Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 5 May 2025 16:41:51 +0200 Subject: [PATCH 10/19] Disambiguate and explain override error messages --- .../tools/dotc/printing/Formatting.scala | 14 ++ .../dotty/tools/dotc/reporting/Message.scala | 137 ++++++++++-------- .../dotty/tools/dotc/reporting/messages.scala | 8 +- tests/neg-custom-args/captures/lazylist.check | 2 + .../captures/sep-lazyListState.check | 5 + .../captures/sep-lazyListState.scala | 14 ++ .../captures/unsound-reach-4.check | 5 +- .../captures/widen-reach.check | 5 +- tests/neg/abstract-givens.check | 5 +- 9 files changed, 128 insertions(+), 67 deletions(-) create mode 100644 tests/neg-custom-args/captures/sep-lazyListState.check create mode 100644 tests/neg-custom-args/captures/sep-lazyListState.scala diff --git a/compiler/src/dotty/tools/dotc/printing/Formatting.scala b/compiler/src/dotty/tools/dotc/printing/Formatting.scala index 741b997d9926..10365c1a3d95 100644 --- a/compiler/src/dotty/tools/dotc/printing/Formatting.scala +++ b/compiler/src/dotty/tools/dotc/printing/Formatting.scala @@ -13,6 +13,17 @@ import Highlighting.* object Formatting { + /** Essentially, a function Context => T, which can be created with `delay` */ + abstract class Delay[T]: + def apply(c: Context): T + + /** Delay a Context => T computation so that it is generated from the embedded + * context of a string formatter instead of the enclosing context. This is needed + * to make disambiguation work for such embedded computatons. + */ + def delay[T](fn: Context ?=> T): Delay[T] = new Delay[T]: + def apply(c: Context) = fn(using c) + object ShownDef: /** Represents a value that has been "shown" and can be consumed by StringFormatter. * Not just a string because it may be a Seq that StringFormatter will intersperse with the trailing separator. @@ -76,6 +87,9 @@ object Formatting { given [X: Show]: Show[Seq[X]] with def show(x: Seq[X]) = CtxShow(x.map(toStr)) + given [X: Show]: Show[Delay[X]] = new Show: + def show(x: Delay[X]) = CtxShow(c ?=> x(c)) + given Show[Seq[Nothing]] with def show(x: Seq[Nothing]) = CtxShow(x) diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 5a9f867f006e..a75117a4cab0 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -41,6 +41,17 @@ object Message: i"\n$what can be rewritten automatically under -rewrite $optionStr." else "" + enum Disambiguation: + case All + case AllExcept(strs: List[String]) + case None + + def recordOK(str: String): Boolean = this match + case All => true + case AllExcept(strs) => !strs.contains(str) + case None => false + end Disambiguation + private type Recorded = Symbol | ParamRef | SkolemType | CaptureRef private case class SeenKey(str: String, isType: Boolean) @@ -49,7 +60,7 @@ object Message: * adds superscripts for disambiguations, and can explain recorded symbols * in ` where` clause */ - private class Seen(disambiguate: Boolean): + private class Seen(disambiguate: Disambiguation): /** The set of lambdas that were opened at some point during printing. */ private val openedLambdas = new collection.mutable.HashSet[LambdaType] @@ -63,12 +74,12 @@ object Message: var nonSensical = false /** If false, stop all recordings */ - private var recordOK = disambiguate + private var disambi = disambiguate /** Clear all entries and stop further entries to be added */ def disable() = seen.clear() - recordOK = false + disambi = Disambiguation.None /** Record an entry `entry` with given String representation `str` and a * type/term namespace identified by `isType`. @@ -77,59 +88,61 @@ object Message: * and following recordings get consecutive superscripts starting with 2. * @return The possibly superscripted version of `str`. */ - def record(str: String, isType: Boolean, entry: Recorded)(using Context): String = if !recordOK then str else - //println(s"recording $str, $isType, $entry") - - /** If `e1` is an alias of another class of the same name, return the other - * class symbol instead. This normalization avoids recording e.g. scala.List - * and scala.collection.immutable.List as two different types - */ - def followAlias(e1: Recorded): Recorded = e1 match { - case e1: Symbol if e1.isAliasType => - val underlying = e1.typeRef.underlyingClassRef(refinementOK = false).typeSymbol - if (underlying.name == e1.name) underlying else e1.namedType.dealias.typeSymbol - case _ => e1 - } - val key = SeenKey(str, isType) - val existing = seen(key) - lazy val dealiased = followAlias(entry) - - /** All lambda parameters with the same name are given the same superscript as - * long as their corresponding binder has been printed. - * See tests/neg/lambda-rename.scala for test cases. - */ - def sameSuperscript(cur: Recorded, existing: Recorded) = - (cur eq existing) || - (cur, existing).match - case (cur: ParamRef, existing: ParamRef) => - (cur.paramName eq existing.paramName) && - openedLambdas.contains(cur.binder) && - openedLambdas.contains(existing.binder) - case _ => - false - - // The length of alts corresponds to the number of superscripts we need to print. - var alts = existing.dropWhile(alt => !sameSuperscript(dealiased, followAlias(alt))) - if alts.isEmpty then - alts = entry :: existing - seen(key) = alts - - val suffix = alts.length match { - case 1 => "" - case n => n.toString.toCharArray.map { - case '0' => '⁰' - case '1' => '¹' - case '2' => '²' - case '3' => '³' - case '4' => '⁴' - case '5' => '⁵' - case '6' => '⁶' - case '7' => '⁷' - case '8' => '⁸' - case '9' => '⁹' - }.mkString - } - str + suffix + def record(str: String, isType: Boolean, entry: Recorded)(using Context): String = + if disambi.recordOK(str) then + //println(s"recording $str, $isType, $entry") + + /** If `e1` is an alias of another class of the same name, return the other + * class symbol instead. This normalization avoids recording e.g. scala.List + * and scala.collection.immutable.List as two different types + */ + def followAlias(e1: Recorded): Recorded = e1 match { + case e1: Symbol if e1.isAliasType => + val underlying = e1.typeRef.underlyingClassRef(refinementOK = false).typeSymbol + if (underlying.name == e1.name) underlying else e1.namedType.dealias.typeSymbol + case _ => e1 + } + val key = SeenKey(str, isType) + val existing = seen(key) + lazy val dealiased = followAlias(entry) + + /** All lambda parameters with the same name are given the same superscript as + * long as their corresponding binder has been printed. + * See tests/neg/lambda-rename.scala for test cases. + */ + def sameSuperscript(cur: Recorded, existing: Recorded) = + (cur eq existing) || + (cur, existing).match + case (cur: ParamRef, existing: ParamRef) => + (cur.paramName eq existing.paramName) && + openedLambdas.contains(cur.binder) && + openedLambdas.contains(existing.binder) + case _ => + false + + // The length of alts corresponds to the number of superscripts we need to print. + var alts = existing.dropWhile(alt => !sameSuperscript(dealiased, followAlias(alt))) + if alts.isEmpty then + alts = entry :: existing + seen(key) = alts + + val suffix = alts.length match { + case 1 => "" + case n => n.toString.toCharArray.map { + case '0' => '⁰' + case '1' => '¹' + case '2' => '²' + case '3' => '³' + case '4' => '⁴' + case '5' => '⁵' + case '6' => '⁶' + case '7' => '⁷' + case '8' => '⁸' + case '9' => '⁹' + }.mkString + } + str + suffix + else str end record /** Create explanation for single `Recorded` type or symbol */ @@ -394,13 +407,15 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => */ def isNonSensical: Boolean = { message; myIsNonSensical } - private var disambiguate: Boolean = true + private var disambiguate: Disambiguation = Disambiguation.All - def withoutDisambiguation(): this.type = - disambiguate = false + def withDisambiguation(disambi: Disambiguation): this.type = + disambiguate = disambi this - private def inMessageContext(disambiguate: Boolean)(op: Context ?=> String): String = + def withoutDisambiguation(): this.type = withDisambiguation(Disambiguation.None) + + private def inMessageContext(disambiguate: Disambiguation)(op: Context ?=> String): String = if ctx eq NoContext then op else val msgContext = ctx.printer match @@ -419,7 +434,7 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => /** The explanation to report. tags are filtered out */ @threadUnsafe lazy val explanation: String = - inMessageContext(disambiguate = false)(explain) + inMessageContext(disambiguate = Disambiguation.None)(explain) /** The implicit `Context` in messages is a large thing that we don't want * persisted. This method gets around that by duplicating the message, diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 0db766d4e337..aa2332f98c10 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -23,7 +23,7 @@ import typer.Implicits.* import typer.Inferencing import scala.util.control.NonFatal import StdNames.nme -import printing.Formatting.hl +import Formatting.{hl, delay} import ast.Trees.* import ast.untpd import ast.tpd @@ -37,6 +37,7 @@ import scala.jdk.CollectionConverters.* import dotty.tools.dotc.util.SourceFile import dotty.tools.dotc.config.SourceVersion import DidYouMean.* +import Message.Disambiguation /** Messages * ======== @@ -1172,6 +1173,7 @@ class OverrideError( member: Symbol, other: Symbol, memberTp: Type, otherTp: Type)(using Context) extends DeclarationMsg(OverrideErrorID), NoDisambiguation: + withDisambiguation(Disambiguation.AllExcept(List(member.name.toString))) def msg(using Context) = val isConcreteOverAbstract = (other.owner isSubClass member.owner) && other.is(Deferred) && !member.is(Deferred) @@ -1181,8 +1183,8 @@ extends DeclarationMsg(OverrideErrorID), NoDisambiguation: |(Note that ${err.infoStringWithLocation(other, base)} is abstract, |and is therefore overridden by concrete ${err.infoStringWithLocation(member, base)})""" else "" - i"""error overriding ${err.infoStringWithLocation(other, base)}; - | ${err.infoString(member, base, showLocation = member.owner != base.typeSymbol)} $core$addendum""" + i"""error overriding ${delay(err.infoStringWithLocation(other, base))}; + | ${delay(err.infoString(member, base, showLocation = member.owner != base.typeSymbol))} $core$addendum""" override def canExplain = memberTp.exists && otherTp.exists def explain(using Context) = diff --git a/tests/neg-custom-args/captures/lazylist.check b/tests/neg-custom-args/captures/lazylist.check index acdbfaaca3e0..47f54e508e76 100644 --- a/tests/neg-custom-args/captures/lazylist.check +++ b/tests/neg-custom-args/captures/lazylist.check @@ -39,4 +39,6 @@ | error overriding method tail in class LazyList of type -> lazylists.LazyList[Nothing]; | method tail of type -> lazylists.LazyList[Nothing]^ has incompatible type | + | where: ^ refers to a fresh root capability in the result type of method tail + | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/sep-lazyListState.check b/tests/neg-custom-args/captures/sep-lazyListState.check new file mode 100644 index 000000000000..519d143983bf --- /dev/null +++ b/tests/neg-custom-args/captures/sep-lazyListState.check @@ -0,0 +1,5 @@ +-- Error: tests/neg-custom-args/captures/sep-lazyListState.scala:13:14 ------------------------------------------------- +13 | def tail: LazyListIterable[A]^ = tl // error + | ^^^^^^^^^^^^^^^^^^^^ + | Separation failure: method tail's result type LazyListIterable[A]^ hides non-local this of class class Cons. + | The access must be in a @consume method to allow this. diff --git a/tests/neg-custom-args/captures/sep-lazyListState.scala b/tests/neg-custom-args/captures/sep-lazyListState.scala new file mode 100644 index 000000000000..4ef39eb4912d --- /dev/null +++ b/tests/neg-custom-args/captures/sep-lazyListState.scala @@ -0,0 +1,14 @@ +class LazyListIterable[+A] + +private sealed trait State[+A]: + def head: A + def tail: LazyListIterable[A]^ + +private object State: + object Empty extends State[Nothing]: + def head: Nothing = throw new NoSuchElementException("head of empty lazy list") + def tail: LazyListIterable[Nothing] = throw new UnsupportedOperationException("tail of empty lazy list") + + final class Cons[A](val head: A, tl: LazyListIterable[A]^) extends State[A]: + def tail: LazyListIterable[A]^ = tl // error + diff --git a/tests/neg-custom-args/captures/unsound-reach-4.check b/tests/neg-custom-args/captures/unsound-reach-4.check index 850388bf1c33..0e9acbea1afa 100644 --- a/tests/neg-custom-args/captures/unsound-reach-4.check +++ b/tests/neg-custom-args/captures/unsound-reach-4.check @@ -18,6 +18,9 @@ 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^): File^ has incompatible type + | method use of type (x: File^): File^² has incompatible type + | + | where: ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: File^): File^² | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/widen-reach.check b/tests/neg-custom-args/captures/widen-reach.check index 9898f1c242b4..a8148e02bad1 100644 --- a/tests/neg-custom-args/captures/widen-reach.check +++ b/tests/neg-custom-args/captures/widen-reach.check @@ -29,6 +29,9 @@ 9 | val foo: IO^ -> IO^ = x => x // error // error | ^ | error overriding value foo in trait Foo of type IO^ -> box IO^; - | value foo of type IO^ -> IO^ has incompatible type + | value foo of type IO^ -> IO^² has incompatible type + | + | where: ^ refers to the universal root capability + | ^² refers to a fresh root capability in the type of value foo | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/abstract-givens.check b/tests/neg/abstract-givens.check index 1430c5b6e950..51f50db266c2 100644 --- a/tests/neg/abstract-givens.check +++ b/tests/neg/abstract-givens.check @@ -10,7 +10,10 @@ -- [E164] Declaration Error: tests/neg/abstract-givens.scala:9:8 ------------------------------------------------------- 9 | given z[T](using T): Seq[T] = List(summon[T]) // error | ^ - | error overriding given instance z in trait T of type [T](using x$1: T): List[T]; + | error overriding given instance z in trait T² of type [T](using x$1: T): List[T]; | given instance z of type [T](using x$1: T): Seq[T] has incompatible type | + | where: T is a type variable + | T² is a trait in the empty package + | | longer explanation available when compiling with `-explain` From b3531fc6e12f52719b593f9eb14bdcd7f8122796 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 5 May 2025 17:19:04 +0200 Subject: [PATCH 11/19] Fix root treatment of parameterless methods - Map cap to Result in their original types (was Fresh before) - Map Result to Fresh on access --- .../src/dotty/tools/dotc/cc/CaptureSet.scala | 3 ++- .../dotty/tools/dotc/cc/CheckCaptures.scala | 14 +++++++++++-- compiler/src/dotty/tools/dotc/cc/Setup.scala | 6 +++--- compiler/src/dotty/tools/dotc/cc/root.scala | 16 ++++++++------- .../src/dotty/tools/dotc/core/Types.scala | 5 +++-- .../immutable/LazyListIterable.scala | 14 +++++++++---- tests/neg-custom-args/captures/i15772.check | 10 +++++----- .../captures/lazyListState.check | 10 ++++++++++ .../captures/lazyListState.scala | 13 ++++++++++++ tests/neg-custom-args/captures/lazylist.check | 4 ++-- .../captures/linear-buffer.check | 5 ----- .../captures/linear-buffer.scala | 2 +- tests/neg-custom-args/captures/sep-use2.check | 20 +------------------ tests/neg-custom-args/captures/sep-use2.scala | 2 +- .../captures/unsound-reach-6.check | 2 +- 15 files changed, 73 insertions(+), 53 deletions(-) create mode 100644 tests/neg-custom-args/captures/lazyListState.check create mode 100644 tests/neg-custom-args/captures/lazyListState.scala diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index 1fb8c96ad509..265350708ec8 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -1162,7 +1162,8 @@ object CaptureSet: def unify(root1: root.Result, root2: root.Result)(using Context): Boolean = (root1, root2) match case (root1 @ root.Result(binder1), root2 @ root.Result(binder2)) - if (binder1 eq binder2) + if ((binder1 eq binder2) + || binder1.isInstanceOf[ExprType] && binder2.isInstanceOf[ExprType]) && (root1.rootAnnot.originalBinder ne root2.rootAnnot.originalBinder) && eqResultMap(root1) == null && eqResultMap(root2) == null diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 99c5e88a3c07..01a37eba4f1e 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -556,6 +556,16 @@ class CheckCaptures extends Recheck, SymTransformer: if sym.exists && curEnv.isOpen then markFree(capturedVars(sym).filter(isRetained), tree) + /** If `tp` (possibly after widening singletons) is an ExprType + * of a parameterless method, map Result instances in it to Fresh instances + */ + def mapResultRoots(tp: Type, sym: Symbol)(using Context): Type = + tp.widenSingleton match + case tp: ExprType if sym.is(Method) => + root.resultToFresh(tp, root.Origin.ResultInstance(tp, sym)) + case _ => + tp + /** Under the sealed policy, disallow the root capability in type arguments. * Type arguments come either from a TypeApply node or from an AppliedType * which represents a trait parent in a template. @@ -618,7 +628,7 @@ class CheckCaptures extends Recheck, SymTransformer: if pathRef.derivesFrom(defn.Caps_Mutable) && pt.isValueType && !pt.isMutableType then pathRef = pathRef.readOnly markFree(sym, pathRef, tree) - super.recheckIdent(tree, pt) + mapResultRoots(super.recheckIdent(tree, pt), tree.symbol) /** 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 @@ -665,7 +675,7 @@ class CheckCaptures extends Recheck, SymTransformer: |since its capture set ${qualType.captureSet} is read-only""", tree.srcPos) - val selType = recheckSelection(tree, qualType, name, disambiguate) + val selType = mapResultRoots(recheckSelection(tree, qualType, name, disambiguate), tree.symbol) val selWiden = selType.widen // Don't apply the rule diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 196bdff5a56a..808cb29e8859 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -323,7 +323,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: try val tp1 = mapInferred(refine = true)(tp) - val tp2 = root.toResultInResults(_ => assert(false))(tp1) + val tp2 = root.toResultInResults(NoSymbol, _ => assert(false))(tp1) if tp2 ne tp then capt.println(i"expanded inferred in ${ctx.owner}: $tp --> $tp1 --> $tp2") tp2 catch case ex: AssertionError => @@ -458,7 +458,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: def transform(tp: Type): Type = val tp1 = toCapturing(tp) - val tp2 = root.toResultInResults(fail, toCapturing.keepFunAliases)(tp1) + val tp2 = root.toResultInResults(sym, fail, toCapturing.keepFunAliases)(tp1) val snd = if toCapturing.keepFunAliases then "" else " 2nd time" if tp2 ne tp then capt.println(i"expanded explicit$snd in ${ctx.owner}: $tp --> $tp1 --> $tp2") tp2 @@ -648,7 +648,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: val paramSymss = sym.paramSymss def newInfo(using Context) = // will be run in this or next phase - root.toResultInResults(report.error(_, tree.srcPos)): + root.toResultInResults(sym, report.error(_, tree.srcPos)): if sym.is(Method) then paramsToCap(methodType(paramSymss, localReturnType)) else tree.tpt.nuType diff --git a/compiler/src/dotty/tools/dotc/cc/root.scala b/compiler/src/dotty/tools/dotc/cc/root.scala index d41e594b821b..613745add1f2 100644 --- a/compiler/src/dotty/tools/dotc/cc/root.scala +++ b/compiler/src/dotty/tools/dotc/cc/root.scala @@ -110,7 +110,7 @@ object root: "" enum Kind: - case Result(binder: MethodType) + case Result(binder: MethodicType) case Fresh(hidden: CaptureSet.HiddenSet)(val origin: Origin) case Global @@ -134,14 +134,14 @@ object root: ccs.rootId += 1 ccs.rootId - //assert(id != 4) + //assert(id != 4, kind) override def symbol(using Context) = defn.RootCapabilityAnnot override def tree(using Context) = New(symbol.typeRef, Nil) override def derivedAnnotation(tree: Tree)(using Context): Annotation = this private var myOriginalKind = kind - def originalBinder: MethodType = myOriginalKind.asInstanceOf[Kind.Result].binder + def originalBinder: MethodicType = myOriginalKind.asInstanceOf[Kind.Result].binder def derivedAnnotation(binder: MethodType)(using Context): Annotation = kind match case Kind.Result(b) if b ne binder => @@ -198,13 +198,13 @@ object root: type Result = AnnotatedType object Result: - def apply(binder: MethodType)(using Context): Result = + def apply(binder: MethodicType)(using Context): Result = val hiddenSet = CaptureSet.HiddenSet(NoSymbol) val res = AnnotatedType(cap, Annot(Kind.Result(binder))) hiddenSet.owningCap = res res - def unapply(tp: Result)(using Context): Option[MethodType] = tp.annot match + def unapply(tp: Result)(using Context): Option[MethodicType] = tp.annot match case Annot(Kind.Result(binder)) => Some(binder) case _ => None end Result @@ -298,7 +298,7 @@ object root: * variable bound by `mt`. * Stop at function or method types since these have been mapped before. */ - def toResult(tp: Type, mt: MethodType, fail: Message => Unit)(using Context): Type = + def toResult(tp: Type, mt: MethodicType, fail: Message => Unit)(using Context): Type = abstract class CapMap extends BiTypeMap: override def mapOver(t: Type): Type = t match @@ -356,7 +356,7 @@ object root: end toResult /** Map global roots in function results to result roots */ - def toResultInResults(fail: Message => Unit, keepAliases: Boolean = false)(using Context): TypeMap = new TypeMap with FollowAliasesMap: + def toResultInResults(sym: Symbol, fail: Message => Unit, keepAliases: Boolean = false)(using Context): TypeMap = new TypeMap with FollowAliasesMap: def apply(t: Type): Type = t match case defn.RefinedFunctionOf(mt) => val mt1 = apply(mt) @@ -369,6 +369,8 @@ object root: t.derivedCapturingType(this(parent), refs) case t: (LazyRef | TypeVar) => mapConserveSuper(t) + case t: ExprType if sym.is(Method, butNot = Accessor) => + t.derivedExprType(toResult(t.resType, t, fail)) case _ => try if keepAliases then mapOver(t) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 2cd66464da3c..93cdb075f5ab 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3721,7 +3721,8 @@ object Types extends TypeUtils { // is that most poly types are cyclic via poly params, // and therefore two different poly types would never be equal. - trait MethodicType extends TermType + trait MethodicType extends TermType: + def resType: Type /** A by-name parameter type of the form `=> T`, or the type of a method with no parameter list. */ abstract case class ExprType(resType: Type) @@ -4279,7 +4280,7 @@ object Types extends TypeUtils { ps.get(elemName) match case Some(elemRef) => assert(elemRef eq elem, i"bad $mt") case _ => - case root.Result(binder) if binder ne mt => + case root.Result(binder: MethodType) if binder ne mt => assert(binder.paramNames.toList != mt.paramNames.toList, i"bad $mt") case _ => checkRefs(refs) diff --git a/scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala b/scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala index 45186e7c13b2..726b011c6929 100644 --- a/scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala +++ b/scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala @@ -25,7 +25,7 @@ import scala.runtime.Statics import language.experimental.captureChecking import annotation.unchecked.uncheckedCaptures import caps.cap -import caps.unsafe.{unsafeAssumeSeparate, untrackedCaptures} +import caps.unsafe.{unsafeAssumeSeparate, unsafeAssumePure, untrackedCaptures} /** This class implements an immutable linked list. We call it "lazy" * because it computes its elements only when they are needed. @@ -989,7 +989,12 @@ object LazyListIterable extends IterableFactory[LazyListIterable] { private sealed trait State[+A] extends Serializable { def head: A - def tail: LazyListIterable[A]^ + def tail: LazyListIterable[A]/*^*/ + // should be ^ but this fails checking. The problem is that + // the ^ in LazyListIterable[A]^ is treated as a result reference. + // But then it cannot be subsumed by + // val tail: LazyListIterable[A]^ + // in class State.Cons. } private object State { @@ -1000,14 +1005,15 @@ object LazyListIterable extends IterableFactory[LazyListIterable] { } @SerialVersionUID(3L) - final class Cons[A](val head: A, val tail: LazyListIterable[A]^) extends State[A] + final class Cons[A](val head: A, val tail: LazyListIterable[A]/*^*/) extends State[A] } /** Creates a new LazyListIterable. */ @inline private def newLL[A](state: => State[A]^): LazyListIterable[A]^{state} = new LazyListIterable[A](() => state) /** Creates a new State.Cons. */ - @inline private def sCons[A](hd: A, tl: LazyListIterable[A]^): State[A]^{tl} = new State.Cons[A](hd, tl) + @inline private def sCons[A](hd: A, tl: LazyListIterable[A]^): State[A]^{tl} = + new State.Cons[A](hd, tl.unsafeAssumePure) private val anyToMarker: Any => Any = _ => Statics.pfMarker diff --git a/tests/neg-custom-args/captures/i15772.check b/tests/neg-custom-args/captures/i15772.check index 151ee9599c7c..145bfd7faae8 100644 --- a/tests/neg-custom-args/captures/i15772.check +++ b/tests/neg-custom-args/captures/i15772.check @@ -19,14 +19,14 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:35:34 --------------------------------------- 35 | val boxed2 : Observe[C]^ = box2(c) // error | ^ - | Found: box C^ - | Required: box C{val arg: C^?}^? + |Found: box C^ + |Required: box C{val arg: C^?}^? | - | where: ^ refers to a fresh root capability in the result type of method c + |where: ^ refers to a fresh root capability created in value boxed2 when instantiating method c's type -> C^{cap} | | - | Note that the universal capability `cap` - | 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` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:46:2 ---------------------------------------- diff --git a/tests/neg-custom-args/captures/lazyListState.check b/tests/neg-custom-args/captures/lazyListState.check new file mode 100644 index 000000000000..bb84f3d7f006 --- /dev/null +++ b/tests/neg-custom-args/captures/lazyListState.check @@ -0,0 +1,10 @@ +-- [E164] Declaration Error: tests/neg-custom-args/captures/lazyListState.scala:12:39 ---------------------------------- +12 | final class Cons[A](val head: A, val tail: LazyListIterable[A]^) extends State[A] // error + | ^ + | error overriding method tail in trait State of type -> LazyListIterable[A]^{cap}; + | value tail of type LazyListIterable[A]^ has incompatible type + | + | where: ^ refers to a fresh root capability in the type of value tail + | cap is a root capability associated with the result type of -> LazyListIterable[A²]^² + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/lazyListState.scala b/tests/neg-custom-args/captures/lazyListState.scala new file mode 100644 index 000000000000..43e8ba19bb2f --- /dev/null +++ b/tests/neg-custom-args/captures/lazyListState.scala @@ -0,0 +1,13 @@ +class LazyListIterable[+A] + +private sealed trait State[+A]: + def head: A + def tail: LazyListIterable[A]^ + +private object State: + object Empty extends State[Nothing]: + def head: Nothing = throw new NoSuchElementException("head of empty lazy list") + def tail: LazyListIterable[Nothing] = throw new UnsupportedOperationException("tail of empty lazy list") + + final class Cons[A](val head: A, val tail: LazyListIterable[A]^) extends State[A] // error + diff --git a/tests/neg-custom-args/captures/lazylist.check b/tests/neg-custom-args/captures/lazylist.check index 47f54e508e76..455d72e9fd37 100644 --- a/tests/neg-custom-args/captures/lazylist.check +++ b/tests/neg-custom-args/captures/lazylist.check @@ -37,8 +37,8 @@ 22 | def tail: LazyList[Nothing]^ = ??? // error overriding | ^ | error overriding method tail in class LazyList of type -> lazylists.LazyList[Nothing]; - | method tail of type -> lazylists.LazyList[Nothing]^ has incompatible type + | method tail of type -> lazylists.LazyList[Nothing]^{cap} has incompatible type | - | where: ^ refers to a fresh root capability in the result type of method tail + | where: cap is a root capability associated with the result type of -> lazylists.LazyList[Nothing]^ | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/linear-buffer.check b/tests/neg-custom-args/captures/linear-buffer.check index 2acaa2ad5fc2..86e6aaaa81a3 100644 --- a/tests/neg-custom-args/captures/linear-buffer.check +++ b/tests/neg-custom-args/captures/linear-buffer.check @@ -8,11 +8,6 @@ | ^^^^^^^^^^^^^ | 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 | ^^^ diff --git a/tests/neg-custom-args/captures/linear-buffer.scala b/tests/neg-custom-args/captures/linear-buffer.scala index 97315c1aa0fb..a72f69a22dbd 100644 --- a/tests/neg-custom-args/captures/linear-buffer.scala +++ b/tests/neg-custom-args/captures/linear-buffer.scala @@ -3,7 +3,7 @@ import language.experimental.captureChecking class BadBuffer[T] extends Mutable: mut def append(x: T): BadBuffer[T]^ = this // error - def foo = // error + def foo = def bar: BadBuffer[T]^ = this // error bar diff --git a/tests/neg-custom-args/captures/sep-use2.check b/tests/neg-custom-args/captures/sep-use2.check index ea990204e194..7dfdfaf1e498 100644 --- a/tests/neg-custom-args/captures/sep-use2.check +++ b/tests/neg-custom-args/captures/sep-use2.check @@ -20,31 +20,13 @@ | ^^^^^^^ | Separation failure: method cc's result type Object^ hides non-local parameter c -- Error: tests/neg-custom-args/captures/sep-use2.scala:18:6 ----------------------------------------------------------- -18 | { f(cc) } // error // error +18 | { f(cc) } // error | ^ | Separation failure: Illegal access to {c} which is hidden by the previous definition | of method cc with result type Object^. | This type hides capabilities {c} | | where: ^ refers to a fresh root capability in the result type of method cc --- Error: tests/neg-custom-args/captures/sep-use2.scala:18:8 ----------------------------------------------------------- -18 | { f(cc) } // error // error - | ^^ - |Separation failure: argument of type (cc : -> Object^) - |to a function of type (x: Object^) ->{c} Object^ - |corresponds to capture-polymorphic formal parameter x of type Object^² - |and hides capabilities {cap, c}. - |Some of these overlap with the captures of the function prefix. - | - | Hidden set of current argument : {cap, c} - | Hidden footprint of current argument : {c} - | Capture set of function prefix : {f} - | Footprint set of function prefix : {f, c} - | The two sets overlap at : {c} - | - |where: ^ refers to a fresh root capability in the result type of method cc - | ^² refers to a fresh root capability created in value x1 when checking argument to parameter x of method apply - | cap is a fresh root capability in the result type of method cc -- Error: tests/neg-custom-args/captures/sep-use2.scala:20:6 ----------------------------------------------------------- 20 | { f(c) } // error // error | ^ diff --git a/tests/neg-custom-args/captures/sep-use2.scala b/tests/neg-custom-args/captures/sep-use2.scala index a31fb098a719..fcebc5f5b5ed 100644 --- a/tests/neg-custom-args/captures/sep-use2.scala +++ b/tests/neg-custom-args/captures/sep-use2.scala @@ -15,7 +15,7 @@ def test1(@consume c: Object^, f: (x: Object^) => Object^) = def test2(@consume c: Object^, f: (x: Object^) ->{c} Object^) = def cc: Object^ = c // error val x1 = - { f(cc) } // error // error + { f(cc) } // error val x4: Object^ = // ^ hides just c, since the Object^ in the result of `f` is existential { f(c) } // error // error diff --git a/tests/neg-custom-args/captures/unsound-reach-6.check b/tests/neg-custom-args/captures/unsound-reach-6.check index ed81271efa90..2afe9ba0e1dd 100644 --- a/tests/neg-custom-args/captures/unsound-reach-6.check +++ b/tests/neg-custom-args/captures/unsound-reach-6.check @@ -25,4 +25,4 @@ -- 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 + |Separation failure: argument to @consume parameter with type List[box () ->{io} Unit] refers to non-local parameter io From fce0fd4d58960c26c9fcabc7b32b04f49753a9b1 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 5 May 2025 19:48:29 +0200 Subject: [PATCH 12/19] Turn level checking on under useFreshLevels config option Turn level checking on under the useFreshLevels config option (which is on by default) --- .../src/dotty/tools/dotc/cc/CCState.scala | 18 +++ .../src/dotty/tools/dotc/cc/CaptureRef.scala | 20 ++- .../src/dotty/tools/dotc/cc/CaptureSet.scala | 9 +- .../src/dotty/tools/dotc/cc/ccConfig.scala | 4 + .../dotc/transform/OverridingPairs.scala | 4 +- tests/neg-custom-args/captures/i21401.check | 14 ++- tests/neg-custom-args/captures/i21401.scala | 2 +- tests/neg-custom-args/captures/reaches.check | 21 ++-- tests/neg-custom-args/captures/reaches.scala | 4 +- .../captures/scoped-caps.check | 117 ++++++++++++++++++ .../captures/scoped-caps.scala | 2 +- 11 files changed, 194 insertions(+), 21 deletions(-) create mode 100644 tests/neg-custom-args/captures/scoped-caps.check diff --git a/compiler/src/dotty/tools/dotc/cc/CCState.scala b/compiler/src/dotty/tools/dotc/cc/CCState.scala index c92c9faa6fe6..eb3c6dbbdf6b 100644 --- a/compiler/src/dotty/tools/dotc/cc/CCState.scala +++ b/compiler/src/dotty/tools/dotc/cc/CCState.scala @@ -118,6 +118,8 @@ class CCState: private var capIsRoot: Boolean = false + private var treatFreshAsEqual: Boolean = false + object CCState: opaque type Level = Int @@ -162,4 +164,20 @@ object CCState: /** Is `caps.cap` a root capability that is allowed to subsume other capabilities? */ def capIsRoot(using Context): Boolean = ccState.capIsRoot + /** Run `op` under the assumption that all root.Fresh instances are equal. + * Needed to make override checking of types containing fresh work. + * Asserted in override checking, tested in maxSubsumes. + * Is this sound? Test case is neg-custom-args/captures/leaked-curried.scala. + */ + inline def withTreatFreshAsEqual[T](op: => T)(using Context): T = + if isCaptureCheckingOrSetup then + val ccs = ccState + val saved = ccs.treatFreshAsEqual + ccs.treatFreshAsEqual = true + try op finally ccs.treatFreshAsEqual = saved + else op + + /** Should all root.Fresh instances be treated equal? */ + def treatFreshAsEqual(using Context): Boolean = ccState.treatFreshAsEqual + end CCState diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala b/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala index 388970ffc854..33bf0b08f10b 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala @@ -14,7 +14,9 @@ import compiletime.uninitialized import StdNames.nme import CaptureSet.VarState import Annotations.Annotation +import Flags.* import config.Printers.capt +import CCState.{Level, undefinedLevel} object CaptureRef: opaque type Validity = Int @@ -106,6 +108,10 @@ trait CaptureRef extends TypeProxy, ValueType: case root.Fresh(_) => true case _ => false + final def isResultRoot(using Context): Boolean = this match + case root.Result(_) => true + case _ => false + /** Is this reference the generic root capability `cap` or a Fresh instance? */ final def isCapOrFresh(using Context): Boolean = isCap || isFresh @@ -287,13 +293,21 @@ trait CaptureRef extends TypeProxy, ValueType: case _ => false (this eq y) || this.match - case root.Fresh(hidden) => - vs.ifNotSeen(this)(hidden.elems.exists(_.subsumes(y))) - || !y.stripReadOnly.isCap + case x @ root.Fresh(hidden) => + def levelOK = + if ccConfig.useFreshLevels then + val yOwner = y.adjustedOwner + yOwner.isStatic || x.ccOwner.isContainedIn(yOwner) + else + !y.stripReadOnly.isCap && !yIsExistential && !y.isInstanceOf[ParamRef] + + vs.ifNotSeen(this)(hidden.elems.exists(_.subsumes(y))) + || levelOK && canAddHidden && vs.addHidden(hidden, y) + || ccConfig.useFreshLevels && CCState.treatFreshAsEqual && y.stripReadOnly.isFresh case x @ root.Result(binder) => val result = y match case y @ root.Result(_) => vs.unify(x, y) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index 265350708ec8..a13d06443332 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -1158,12 +1158,19 @@ object CaptureSet: * substBinder when comparing two method types. In that case we can unify * the two roots1, provided none of the two roots have already been unified * themselves. So unification must be 1-1. + * + * Note, see (**) below: We also allow unifications of results that have different ExprType + * binders. This is necessary because ExprTypes don't get updated with SubstBindingMaps. + * It's sound since ExprTypes always appear alone and at the top-level, so there is + * no problem with confusing results at different levels. + * See pos-customargs/captures/overrides.scala for a test case. */ def unify(root1: root.Result, root2: root.Result)(using Context): Boolean = (root1, root2) match case (root1 @ root.Result(binder1), root2 @ root.Result(binder2)) if ((binder1 eq binder2) - || binder1.isInstanceOf[ExprType] && binder2.isInstanceOf[ExprType]) + || binder1.isInstanceOf[ExprType] && binder2.isInstanceOf[ExprType] // (**) + ) && (root1.rootAnnot.originalBinder ne root2.rootAnnot.originalBinder) && eqResultMap(root1) == null && eqResultMap(root2) == null diff --git a/compiler/src/dotty/tools/dotc/cc/ccConfig.scala b/compiler/src/dotty/tools/dotc/cc/ccConfig.scala index 4c06f4a0843d..1a9f63b42a69 100644 --- a/compiler/src/dotty/tools/dotc/cc/ccConfig.scala +++ b/compiler/src/dotty/tools/dotc/cc/ccConfig.scala @@ -50,6 +50,10 @@ object ccConfig: def useSepChecks(using Context): Boolean = Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.7`) + /** If true, do level checking for root.Fresh instances */ + def useFreshLevels(using Context): Boolean = + Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.7`) + /** Not used currently. Handy for trying out new features */ def newScheme(using Context): Boolean = Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.8`) diff --git a/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala b/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala index 20b0c8534920..c51222790145 100644 --- a/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala +++ b/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala @@ -225,6 +225,8 @@ object OverridingPairs: ) else member.name.is(DefaultGetterName) // default getters are not checked for compatibility - || memberTp.overrides(otherTp, member.matchNullaryLoosely || other.matchNullaryLoosely || fallBack) + || + CCState.withTreatFreshAsEqual: + memberTp.overrides(otherTp, member.matchNullaryLoosely || other.matchNullaryLoosely || fallBack) end OverridingPairs diff --git a/tests/neg-custom-args/captures/i21401.check b/tests/neg-custom-args/captures/i21401.check index cc2cc230b478..2f0443e90ed9 100644 --- a/tests/neg-custom-args/captures/i21401.check +++ b/tests/neg-custom-args/captures/i21401.check @@ -31,16 +31,26 @@ | | where: ^ refers to the universal root capability -- Error: tests/neg-custom-args/captures/i21401.scala:17:29 ------------------------------------------------------------ -17 | val x: Boxed[IO^] = leaked[Boxed[IO^], Boxed[IO^] -> Boxed[IO^]](x => x) // error // error +17 | val x: Boxed[IO^] = leaked[Boxed[IO^], Boxed[IO^] -> Boxed[IO^]](x => x) // error // error // error | ^^^^^^^^^^ | Type variable R of value leaked cannot be instantiated to Boxed[box IO^] since | the part box IO^ of that type captures the root capability `cap`. | | where: ^ refers to the universal root capability -- Error: tests/neg-custom-args/captures/i21401.scala:17:52 ------------------------------------------------------------ -17 | val x: Boxed[IO^] = leaked[Boxed[IO^], Boxed[IO^] -> Boxed[IO^]](x => x) // error // error +17 | val x: Boxed[IO^] = leaked[Boxed[IO^], Boxed[IO^] -> Boxed[IO^]](x => x) // error // error // error | ^^^^^^^^^^^^^^^^^^^^^^^^ | Type variable X of value leaked cannot be instantiated to Boxed[box IO^] -> Boxed[box IO^] since | the part box IO^ of that type captures the root capability `cap`. | | where: ^ refers to the universal root capability +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21401.scala:17:67 --------------------------------------- +17 | val x: Boxed[IO^] = leaked[Boxed[IO^], Boxed[IO^] -> Boxed[IO^]](x => x) // error // error // error + | ^^^^^^ + | Found: (x: Boxed[box IO^]^?) ->? Boxed[box IO^{x*}]^? + | Required: (x: Boxed[box IO^]) -> Boxed[box IO^²] + | + | where: ^ refers to the universal root capability + | ^² refers to a fresh root capability created in value x² + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i21401.scala b/tests/neg-custom-args/captures/i21401.scala index bd10a97952a2..af82fb88002d 100644 --- a/tests/neg-custom-args/captures/i21401.scala +++ b/tests/neg-custom-args/captures/i21401.scala @@ -14,6 +14,6 @@ def mkRes(x: IO^): Res = def test2() = val a = usingIO[IO^](x => x) // error // error val leaked: [R, X <: Boxed[IO^] -> R] -> (op: X) -> R = usingIO[Res](mkRes) // error - val x: Boxed[IO^] = leaked[Boxed[IO^], Boxed[IO^] -> Boxed[IO^]](x => x) // error // error + val x: Boxed[IO^] = leaked[Boxed[IO^], Boxed[IO^] -> Boxed[IO^]](x => x) // error // error // error val y: IO^{x*} = x.unbox // was error y.println("boom") diff --git a/tests/neg-custom-args/captures/reaches.check b/tests/neg-custom-args/captures/reaches.check index f99059f8f1af..095ae91182b5 100644 --- a/tests/neg-custom-args/captures/reaches.check +++ b/tests/neg-custom-args/captures/reaches.check @@ -72,16 +72,13 @@ | ^² refers to a fresh root capability in the type of value id | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:62:38 -------------------------------------- -62 | val leaked = usingFile[File^{id*}]: f => // error // error - | ^ - |Found: (f: File^?) ->? box File^? - |Required: (f: File^) => box File^{id*} +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:63:27 -------------------------------------- +63 | val f1: File^{id*} = id(f) // error // error + | ^^^^^ + | Found: File^ + | Required: File^{id*} | - |where: => refers to a fresh root capability created in value leaked when checking argument to parameter f of method usingFile - | ^ refers to the universal root capability -63 | val f1: File^{id*} = id(f) -64 | f1 + | where: ^ refers to a fresh root capability in the type of value id | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:67:32 -------------------------------------- @@ -126,9 +123,13 @@ | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/reaches.scala:62:31 ----------------------------------------------------------- -62 | val leaked = usingFile[File^{id*}]: f => // error // error +62 | 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 // error + | ^^^ + | id* cannot be tracked since its deep capture set is empty -- Error: tests/neg-custom-args/captures/reaches.scala:70:31 ----------------------------------------------------------- 70 | val leaked = usingFile[File^{id*}]: f => // error // error | ^^^ diff --git a/tests/neg-custom-args/captures/reaches.scala b/tests/neg-custom-args/captures/reaches.scala index 709f818a02a8..cf794da701a6 100644 --- a/tests/neg-custom-args/captures/reaches.scala +++ b/tests/neg-custom-args/captures/reaches.scala @@ -59,8 +59,8 @@ def attack2 = val id: File^ -> File^ = x => x // error // val id: File^ -> File^{fresh} - val leaked = usingFile[File^{id*}]: f => // error // error - val f1: File^{id*} = id(f) + val leaked = usingFile[File^{id*}]: f => // error + val f1: File^{id*} = id(f) // error // error f1 def attack3 = diff --git a/tests/neg-custom-args/captures/scoped-caps.check b/tests/neg-custom-args/captures/scoped-caps.check new file mode 100644 index 000000000000..23b5cf70962f --- /dev/null +++ b/tests/neg-custom-args/captures/scoped-caps.check @@ -0,0 +1,117 @@ +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:7:20 ----------------------------------- +7 | val g: A^ -> B^ = f // error + | ^ + | Found: (f : (x: A^) -> B^²) + | Required: A^ -> B^³ + | + | where: ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: A^): B^² + | ^³ refers to a fresh root capability in the type of value g + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:9:25 ----------------------------------- +9 | val _: (x: A^) -> B^ = g // error + | ^ + | Found: (g : A^ -> B^²) + | Required: (x: A^) -> B^³ + | + | where: ^ refers to the universal root capability + | ^² refers to a fresh root capability in the type of value g + | ^³ refers to a root capability associated with the result type of (x: A^): B^³ + | + | + | Note that the existential capture root in B^ + | cannot subsume the capability cap + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:10:20 ---------------------------------- +10 | val _: A^ -> B^ = f // error + | ^ + | Found: (f : (x: A^) -> B^²) + | Required: A^ -> B^³ + | + | where: ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: A^): B^² + | ^³ refers to a fresh root capability in the type of value _$3 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:12:20 ---------------------------------- +12 | val _: A^ -> B^ = x => g(x) // error, since g is pure, g(x): B^{x} , which does not match B^{fresh} + | ^^^^^^^^^ + | Found: (x: A^) ->? B^{x} + | Required: (x: A^) -> B^² + | + | where: ^ refers to the universal root capability + | ^² refers to a fresh root capability in the type of value _$5 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:13:25 ---------------------------------- +13 | val _: (x: A^) -> B^ = x => f(x) // error: existential in B cannot subsume `x` since `x` is not shared + | ^^^^^^^^^ + | Found: (x: A^) ->? B^{x} + | Required: (x: A^) -> B^² + | + | where: ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: A^): B^² + | + | + | Note that the existential capture root in B^ + | cannot subsume the capability x.type since that capability is not a SharedCapability + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:16:24 ---------------------------------- +16 | val _: (x: S) -> B^ = h // error: direct conversion fails + | ^ + | Found: (h : S -> B^) + | Required: (x: S) -> B^² + | + | where: ^ refers to a fresh root capability in the type of value h + | ^² refers to a root capability associated with the result type of (x: S): B^² + | + | + | Note that the existential capture root in B^ + | cannot subsume the capability cap + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:22:19 ---------------------------------- +22 | val _: S -> B^ = j // error + | ^ + | Found: (j : (x: S) -> B^) + | Required: S -> B^² + | + | where: ^ refers to a root capability associated with the result type of (x: S): B^ + | ^² refers to a fresh root capability in the type of value _$11 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:23:19 ---------------------------------- +23 | val _: S -> B^ = x => j(x) // error + | ^^^^^^^^^ + | Found: (x: S) ->? B^{x} + | Required: (x: S) -> B^ + | + | where: ^ refers to a fresh root capability in the type of value _$12 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:26:20 ---------------------------------- +26 | val _: A^ => B^ = x => g2(x) // error: g2(x): B^{g2, x}, and the `x` cannot be subsumed by fresh + | ^^^^^^^^^^ + | Found: (x: A^) ->{g2} B^² + | Required: (x: A^) => B^³ + | + | where: => refers to a fresh root capability in the type of value _$13 + | ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: A^): B^² + | ^³ refers to a fresh root capability in the type of value _$13 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:28:24 ---------------------------------- +28 | val _: A^{io} => B^ = x => g3(x) // error, fails level checking of fresh instances + | ^^^^^^^^^^ + | Found: (x: A^{io}) ->{g3} B^ + | Required: (x: A^{io}) => B^² + | + | where: => refers to a fresh root capability in the type of value _$14 + | ^ refers to a root capability associated with the result type of (x: A^{io}): B^ + | ^² refers to a fresh root capability in the type of value _$14 + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/scoped-caps.scala b/tests/neg-custom-args/captures/scoped-caps.scala index b4705b3d6a62..f109c4178634 100644 --- a/tests/neg-custom-args/captures/scoped-caps.scala +++ b/tests/neg-custom-args/captures/scoped-caps.scala @@ -25,4 +25,4 @@ def test(io: Object^): Unit = val g2: A^ => B^ = ??? val _: A^ => B^ = x => g2(x) // error: g2(x): B^{g2, x}, and the `x` cannot be subsumed by fresh val g3: A^ => B^ = ??? - val _: A^{io} => B^ = x => g3(x) // ok, now g3(x): B^{g3, x}, which is widened to B^{g3, io} + val _: A^{io} => B^ = x => g3(x) // error, fails level checking of fresh instances From 33069c10d0a79dcd1632aa1558f14c52f2c55893 Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 6 May 2025 16:00:23 +0200 Subject: [PATCH 13/19] Tweak printing in MessagePrinter Fall back to previous scheme if there is no recording in Seen. That way, we see what we expect when turning on Ycc-verbose and other printing options. --- compiler/src/dotty/tools/dotc/reporting/Message.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index a75117a4cab0..d098df628a54 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -76,6 +76,8 @@ object Message: /** If false, stop all recordings */ private var disambi = disambiguate + def isActive = disambi != Disambiguation.None + /** Clear all entries and stop further entries to be added */ def disable() = seen.clear() @@ -266,20 +268,20 @@ object Message: case _ => super.toTextRef(tp) override def toTextCaptureRef(tp: Type): Text = tp match - case tp: CaptureRef if tp.isRootCapability && !tp.isReadOnly => + case tp: CaptureRef if tp.isRootCapability && !tp.isReadOnly && seen.isActive => seen.record("cap", isType = false, tp) case _ => super.toTextCaptureRef(tp) override def toTextCapturing(parent: Type, refs: GeneralCaptureSet, boxText: Text) = refs match case refs: CaptureSet - if isUniversalCaptureSet(refs) && !defn.isFunctionType(parent) && !printDebug => + if isUniversalCaptureSet(refs) && !defn.isFunctionType(parent) && !printDebug && seen.isActive => boxText ~ toTextLocal(parent) ~ seen.record("^", isType = true, refs.elems.nth(0)) case _ => super.toTextCapturing(parent, refs, boxText) override def funMiddleText(isContextual: Boolean, isPure: Boolean, refs: GeneralCaptureSet | Null): Text = refs match - case refs: CaptureSet if isUniversalCaptureSet(refs) => + case refs: CaptureSet if isUniversalCaptureSet(refs) && seen.isActive => seen.record(arrow(isContextual, isPure = false), isType = true, refs.elems.nth(0)) case _ => super.funMiddleText(isContextual, isPure, refs) From 23bb53488ca80ef9a666858531561cb7568ce55f Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 6 May 2025 23:31:54 +0200 Subject: [PATCH 14/19] Fix levelOK when root.Result variables are added to capture set variables. The check was not working properly before, which meant that problems went undetected. To make things work again, we need two additional fixes - A more detailed treatment of result mapping for inferred types. In these, we map root.Fresh to root.Result only if the original inferred type was dependent. AppliedType functions are also mapped to RefinedType functions in setup, but this should not count for fresh to result mappings. - A fix in the Fresh to Result mapping of parameterless defs. --- .../src/dotty/tools/dotc/cc/CaptureSet.scala | 74 +++++++++++-------- compiler/src/dotty/tools/dotc/cc/Setup.scala | 4 +- compiler/src/dotty/tools/dotc/cc/root.scala | 56 ++++++++------ .../dotty/tools/dotc/core/Definitions.scala | 1 + library/src/scala/caps/package.scala | 8 ++ .../captures/capt-capability.scala | 9 +++ .../captures/class-level-attack.scala | 25 +++++++ .../captures/capt-capability.scala | 11 ++- tests/pos-custom-args/captures/rinaudo.scala | 12 +++ 9 files changed, 140 insertions(+), 60 deletions(-) create mode 100644 tests/neg-custom-args/captures/capt-capability.scala create mode 100644 tests/neg-custom-args/captures/class-level-attack.scala create mode 100644 tests/pos-custom-args/captures/rinaudo.scala diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index a13d06443332..024b7bdf9462 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -183,7 +183,7 @@ sealed abstract class CaptureSet extends Showable: */ def accountsFor(x: CaptureRef)(using ctx: Context)(using vs: VarState = VarState.Separate): Boolean = - def debugInfo(using Context) = i"$this accountsFor $x, which has capture set ${x.captureSetOfInfo}" + def debugInfo(using Context) = i"$this accountsFor $x" def test(using Context) = reporting.trace(debugInfo): elems.exists(_.subsumes(x)) @@ -353,7 +353,14 @@ sealed abstract class CaptureSet extends Showable: /** Invoke handler if this set has (or later aquires) the root capability `cap` */ def disallowRootCapability(handler: () => Context ?=> Unit)(using Context): this.type = - if containsRootCapability then handler() + val hasRoot = + if ccConfig.newScheme then + elems.exists: elem => + val elem1 = elem.stripReadOnly + elem1.isCap || elem1.isResultRoot + else + containsRootCapability + if hasRoot then handler() this /** Invoke handler on the elements to ensure wellformedness of the capture set. @@ -499,7 +506,7 @@ object CaptureSet: ccs.varId += 1 ccs.varId - //assert(id != 40) + //assert(id != 8, this) /** A variable is solved if it is aproximated to a from-then-on constant set. * Interpretation: @@ -529,7 +536,7 @@ object CaptureSet: /** A handler to be invoked if the root reference `cap` is added to this set */ var rootAddedHandler: () => Context ?=> Unit = () => () - private[CaptureSet] var noUniversal = false + private[CaptureSet] var universalOK = true /** A handler to be invoked when new elems are added to this set */ var newElemAddedHandler: CaptureRef => Context ?=> Unit = _ => () @@ -600,33 +607,38 @@ object CaptureSet: case _ => foldOver(b, t) find(false, binder) - // TODO: Also track allowable TermParamRefs and root.Results in capture sets - private def levelOK(elem: CaptureRef)(using Context): Boolean = - if elem.isRootCapability then - !noUniversal - else elem match - case elem @ root.Result(mt) => - !noUniversal && isPartOf(mt.resType) - case elem: TermRef if level.isDefined => - elem.prefix match - case prefix: CaptureRef => - levelOK(prefix) - case _ => - ccState.symLevel(elem.symbol) <= level - case elem: ThisType if level.isDefined => - ccState.symLevel(elem.cls).nextInner <= level - case elem: ParamRef if !this.isInstanceOf[BiMapped] => - isPartOf(elem.binder.resType) - || { - capt.println( - i"""LEVEL ERROR $elem for $this - |elem binder = ${elem.binder}""") + private def levelOK(elem: CaptureRef)(using Context): Boolean = elem match + case elem @ root.Fresh(_) => + if ccConfig.newScheme then + if !level.isDefined || ccState.symLevel(elem.ccOwner) <= level then true + else + println(i"LEVEL ERROR $elem cannot be included in $this of $owner") false - } - case QualifiedCapability(elem1) => - levelOK(elem1) - case _ => - true + else universalOK + case elem @ root.Result(mt) => + universalOK && (this.isInstanceOf[BiMapped] || isPartOf(mt.resType)) + case elem: TermRef if elem.isCap => + universalOK + case elem: TermRef if level.isDefined => + elem.prefix match + case prefix: CaptureRef => + levelOK(prefix) + case _ => + ccState.symLevel(elem.symbol) <= level + case elem: ThisType if level.isDefined => + ccState.symLevel(elem.cls).nextInner <= level + case elem: ParamRef if !this.isInstanceOf[BiMapped] => + isPartOf(elem.binder.resType) + || { + capt.println( + i"""LEVEL ERROR $elem for $this + |elem binder = ${elem.binder}""") + false + } + case QualifiedCapability(elem1) => + levelOK(elem1) + case _ => + true def addDependent(cs: CaptureSet)(using Context, VarState): CompareResult = if (cs eq this) || cs.isUniversal || isConst then @@ -638,7 +650,7 @@ object CaptureSet: CompareResult.Fail(this :: Nil) override def disallowRootCapability(handler: () => Context ?=> Unit)(using Context): this.type = - noUniversal = true + universalOK = false rootAddedHandler = handler super.disallowRootCapability(handler) diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 808cb29e8859..e7f1438701ec 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -211,9 +211,11 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: case AppliedType(`tycon`, args0) => args0.last ne args.last case _ => false if expand then - depFun(args.init, args.last, + val fn = depFun( + args.init, args.last, isContextual = defn.isContextFunctionClass(tycon.classSymbol)) .showing(i"add function refinement $tp ($tycon, ${args.init}, ${args.last}) --> $result", capt) + AnnotatedType(fn, Annotation(defn.InferredDepFunAnnot, util.Spans.NoSpan)) else tp case _ => tp diff --git a/compiler/src/dotty/tools/dotc/cc/root.scala b/compiler/src/dotty/tools/dotc/cc/root.scala index 613745add1f2..d8b4ba14ca2d 100644 --- a/compiler/src/dotty/tools/dotc/cc/root.scala +++ b/compiler/src/dotty/tools/dotc/cc/root.scala @@ -13,7 +13,6 @@ import NameKinds.ExistentialBinderName import NameOps.isImpureFunction import reporting.Message import util.{SimpleIdentitySet, EqHashMap} -import util.Spans.NoSpan import ast.tpd import annotation.constructorOnly @@ -355,29 +354,38 @@ object root: toVar(tp) end toResult - /** Map global roots in function results to result roots */ - def toResultInResults(sym: Symbol, fail: Message => Unit, keepAliases: Boolean = false)(using Context): TypeMap = new TypeMap with FollowAliasesMap: - def apply(t: Type): Type = t match - case defn.RefinedFunctionOf(mt) => - val mt1 = apply(mt) - if mt1 ne mt then mt1.toFunctionType(alwaysDependent = true) - else t - case t: MethodType if variance > 0 && t.marksExistentialScope => - val t1 = mapOver(t).asInstanceOf[MethodType] - t1.derivedLambdaType(resType = toResult(t1.resType, t1, fail)) - case CapturingType(parent, refs) => - t.derivedCapturingType(this(parent), refs) - case t: (LazyRef | TypeVar) => - mapConserveSuper(t) - case t: ExprType if sym.is(Method, butNot = Accessor) => - t.derivedExprType(toResult(t.resType, t, fail)) - case _ => - try - if keepAliases then mapOver(t) - else mapFollowingAliases(t) - catch case ex: AssertionError => - println(i"error while mapping $t") - throw ex + /** Map global roots in function results to result roots. Also, + * map roots in the types of parameterless def methods. + */ + def toResultInResults(sym: Symbol, fail: Message => Unit, keepAliases: Boolean = false)(tp: Type)(using Context): Type = + val m = new TypeMap with FollowAliasesMap: + def apply(t: Type): Type = t match + case AnnotatedType(parent @ defn.RefinedFunctionOf(mt), ann) if ann.symbol == defn.InferredDepFunAnnot => + val mt1 = mapOver(mt).asInstanceOf[MethodType] + if mt1 ne mt then mt1.toFunctionType(alwaysDependent = true) + else parent + case defn.RefinedFunctionOf(mt) => + val mt1 = apply(mt) + if mt1 ne mt then mt1.toFunctionType(alwaysDependent = true) + else t + case t: MethodType if variance > 0 && t.marksExistentialScope => + val t1 = mapOver(t).asInstanceOf[MethodType] + t1.derivedLambdaType(resType = toResult(t1.resType, t1, fail)) + case CapturingType(parent, refs) => + t.derivedCapturingType(this(parent), refs) + case t: (LazyRef | TypeVar) => + mapConserveSuper(t) + case _ => + try + if keepAliases then mapOver(t) + else mapFollowingAliases(t) + catch case ex: AssertionError => + println(i"error while mapping $t") + throw ex + m(tp) match + case tp1: ExprType if sym.is(Method, butNot = Accessor) => + tp1.derivedExprType(toResult(tp1.resType, tp1, fail)) + case tp1 => tp1 end toResultInResults end root \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 58d93f0f4899..ed231c1e9ca9 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1038,6 +1038,7 @@ class Definitions { @tu lazy val DeprecatedInheritanceAnnot: ClassSymbol = requiredClass("scala.deprecatedInheritance") @tu lazy val ImplicitAmbiguousAnnot: ClassSymbol = requiredClass("scala.annotation.implicitAmbiguous") @tu lazy val ImplicitNotFoundAnnot: ClassSymbol = requiredClass("scala.annotation.implicitNotFound") + @tu lazy val InferredDepFunAnnot: ClassSymbol = requiredClass("scala.caps.internal.inferredDepFun") @tu lazy val InlineParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.InlineParam") @tu lazy val IntoAnnot: ClassSymbol = requiredClass("scala.annotation.into") @tu lazy val IntoParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.$into") diff --git a/library/src/scala/caps/package.scala b/library/src/scala/caps/package.scala index 7f8b184c1c95..3016cb4ebe14 100644 --- a/library/src/scala/caps/package.scala +++ b/library/src/scala/caps/package.scala @@ -122,6 +122,14 @@ object internal: */ final class rootCapability extends annotation.StaticAnnotation + /** An annotation used internally to mark a function type that was + * converted to a dependent function type during setup of inferred types. + * Such function types should not map roots to result variables. + */ + final class inferredDepFun extends annotation.StaticAnnotation + +end internal + @experimental object unsafe: /** diff --git a/tests/neg-custom-args/captures/capt-capability.scala b/tests/neg-custom-args/captures/capt-capability.scala new file mode 100644 index 000000000000..7813ad8144b8 --- /dev/null +++ b/tests/neg-custom-args/captures/capt-capability.scala @@ -0,0 +1,9 @@ +import caps.{Capability, SharedCapability} + +def foo() = + val x: SharedCapability = ??? + + val z3 = + if x == null then (y: Unit) => x else (y: Unit) => new Capability() {} // error + + diff --git a/tests/neg-custom-args/captures/class-level-attack.scala b/tests/neg-custom-args/captures/class-level-attack.scala new file mode 100644 index 000000000000..19d681198e8c --- /dev/null +++ b/tests/neg-custom-args/captures/class-level-attack.scala @@ -0,0 +1,25 @@ +import language.experimental.captureChecking +import caps.* + +class IO + +class Ref[X](init: X): + var x = init + def get: X = x + def put(y: X): Unit = x = y + +class C(io: IO^): + val r: Ref[IO^] = Ref[IO^](io) // error: + //Type variable X of constructor Ref cannot be instantiated to box IO^ since + //that type captures the root capability `cap`. + // where: ^ refers to the universal root capability + val r2: Ref[IO^] = Ref(io) // error: + //Error: Ref[IO^{io}] does not conform to Ref[IO^] (since Refs are invariant) + def set(x: IO^) = r.put(x) + +def outer(outerio: IO^) = + val c = C(outerio) + def test(innerio: IO^) = + c.set(innerio) + + diff --git a/tests/pos-custom-args/captures/capt-capability.scala b/tests/pos-custom-args/captures/capt-capability.scala index 03b5cb1bbabf..d82f78263d18 100644 --- a/tests/pos-custom-args/captures/capt-capability.scala +++ b/tests/pos-custom-args/captures/capt-capability.scala @@ -1,4 +1,4 @@ -import caps.Capability +import caps.{Capability, SharedCapability} def f1(c: Capability): () ->{c} c.type = () => c // ok @@ -14,7 +14,7 @@ def f3: Int = x def foo() = - val x: Capability = ??? + val x: SharedCapability = ??? val y: Capability = x val x2: () ->{x} Capability = ??? val y2: () ->{x} Capability = x2 @@ -22,7 +22,10 @@ def foo() = val z1: () => Capability = f1(x) def h[X](a: X)(b: X) = a - val z2 = - if x == null then () => x else () => new Capability() {} + val z2: (y: Unit) ->{x} Capability^ = + if x == null then (y: Unit) => x else (y: Unit) => new Capability() {} + // z2's type cannot be inferred, see neg test + //val z3 = + // if x == null then (y: Unit) => x else (y: Unit) => new Capability() {} val _ = x diff --git a/tests/pos-custom-args/captures/rinaudo.scala b/tests/pos-custom-args/captures/rinaudo.scala new file mode 100644 index 000000000000..7091f8e94d6d --- /dev/null +++ b/tests/pos-custom-args/captures/rinaudo.scala @@ -0,0 +1,12 @@ +import language.experimental.captureChecking +import caps.* + +trait FileSystem extends Capability: + def print(msg: String): Unit + +class Logger(using fs: FileSystem): + def info(msg: String): Unit = fs.print(msg) + +def log(msg: String): FileSystem ?-> Unit = + val l = new Logger + l.info(msg) \ No newline at end of file From f2fbfc0057832850d2f8f611bd375f75c94e4fd9 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 7 May 2025 11:18:42 +0200 Subject: [PATCH 15/19] Don't elide capture sets of capability classes in most cases When printing types deriving from capability, do print their capture sets even if these are implied. This avoids confusing errors where a capability classes has two different implied Fresh instances which are both elided so the types look the same. Exception: When printing a singleton type (x: T) we do elide in T, since the usually the only part that matters is the `x`. But we don't do the elision under -Ycc-verbose or -explain. --- .../tools/dotc/printing/PlainPrinter.scala | 24 ++++++++++----- .../captures/effect-swaps.check | 10 ++++--- .../captures/extending-cap-classes.check | 12 +++++--- .../captures/scoped-caps.check | 29 ++++++++++--------- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 01658e2f3e9b..ceab670ff862 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -31,6 +31,11 @@ class PlainPrinter(_ctx: Context) extends Printer { /** Print Fresh instances as */ protected def ccVerbose = ctx.settings.YccVerbose.value + /** Elide redundant ^ and ^{cap.rd} when printing instances of Capability + * classes. Gets set when singletons are printed as `(x: T)` to reduce verbosity. + */ + private var elideCapabilityCaps = false + private var openRecs: List[RecType] = Nil protected def maxToTextRecursions: Int = 100 @@ -276,12 +281,12 @@ 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 - toTextCapturing(parent, refs, boxText) + if elideCapabilityCaps + && parent.derivesFrom(defn.Caps_Capability) + && refs.containsRootCapability + && refs.isReadOnly + then toText(parent) + else toTextCapturing(parent, refs, boxText) case tp @ RetainingType(parent, refs) => if Feature.ccEnabledSomewhere then toTextCapturing(parent, refs, "") ~ Str("R").provided(printDebug) @@ -367,7 +372,11 @@ class PlainPrinter(_ctx: Context) extends Printer { }.close def toTextSingleton(tp: SingletonType): Text = - "(" ~ toTextRef(tp) ~ " : " ~ toTextGlobal(tp.underlying) ~ ")" + val saved = elideCapabilityCaps + elideCapabilityCaps = !ccVerbose && !ctx.settings.explain.value + // don't elide capability capture sets under -Ycc-verbose or -explain + try "(" ~ toTextRef(tp) ~ " : " ~ toTextGlobal(tp.underlying) ~ ")" + finally elideCapabilityCaps = saved protected def paramsText(lam: LambdaType): Text = { def paramText(ref: ParamRef) = @@ -468,7 +477,6 @@ class PlainPrinter(_ctx: Context) extends Printer { case tp @ root.Fresh(hidden) => val idStr = if showUniqueIds then s"#${tp.rootAnnot.id}" else "" if ccVerbose then s"" - else if ccVerbose then "fresh" else "cap" case tp => toText(tp) diff --git a/tests/neg-custom-args/captures/effect-swaps.check b/tests/neg-custom-args/captures/effect-swaps.check index 350a7271aefb..d30aa0ca3f21 100644 --- a/tests/neg-custom-args/captures/effect-swaps.check +++ b/tests/neg-custom-args/captures/effect-swaps.check @@ -17,11 +17,12 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:69:10 --------------------------------- 69 | Future: fut ?=> // error, type mismatch | ^ - |Found: (contextual$9: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]) ?->{fr, async} + |Found: (contextual$9: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]^{cap.rd}) ?->{fr, async} | box Future[box T^?]^{fr, contextual$9} - |Required: (contextual$9: boundary.Label[Result[box Future[box T^?]^?, box E^?]]) ?=> box Future[box T^?]^? + |Required: (contextual$9: boundary.Label[Result[box Future[box T^?]^?, box E^?]]^{cap.rd}) ?=> box Future[box T^?]^? | |where: ?=> refers to a fresh root capability created in method fail4 when checking argument to parameter body of method make + | cap is the universal root capability | | |Note that reference contextual$9.type @@ -32,10 +33,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/effect-swaps.scala:73:35 --------------------------------- 73 | Result.make[Future[T], E]: lbl ?=> // error: type mismatch | ^ - |Found: (lbl: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]) ?->{fr, async} Future[box T^?]^{fr, lbl} - |Required: (lbl: boundary.Label[Result[Future[T], E]]) ?=> Future[T] + |Found: (lbl: boundary.Label[box Result[box Future[box T^?]^?, box E^?]^?]^{cap.rd}) ?->{fr, async} Future[box T^?]^{fr, lbl} + |Required: (lbl: boundary.Label[Result[Future[T], E]]^{cap.rd}) ?=> Future[T] | |where: ?=> refers to a fresh root capability created in method fail5 when checking argument to parameter body of method make + | cap is the universal root capability 74 | Future: fut ?=> 75 | fr.await.ok | diff --git a/tests/neg-custom-args/captures/extending-cap-classes.check b/tests/neg-custom-args/captures/extending-cap-classes.check index 4a77a638a4d8..fb77546dd8e5 100644 --- a/tests/neg-custom-args/captures/extending-cap-classes.check +++ b/tests/neg-custom-args/captures/extending-cap-classes.check @@ -1,15 +1,19 @@ -- [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 + | Found: C2^{cap.rd} + | Required: C1 + | + | where: cap is a fresh root capability created in value x2 when constructing Capability instance C2 | | 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 + | Found: C3^{cap.rd} + | Required: C1 + | + | where: cap is a fresh root capability created in value x3 when constructing Capability instance C3 | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/extending-cap-classes.scala:13:15 ------------------------ diff --git a/tests/neg-custom-args/captures/scoped-caps.check b/tests/neg-custom-args/captures/scoped-caps.check index 23b5cf70962f..d4470b219dca 100644 --- a/tests/neg-custom-args/captures/scoped-caps.check +++ b/tests/neg-custom-args/captures/scoped-caps.check @@ -62,34 +62,37 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:16:24 ---------------------------------- 16 | val _: (x: S) -> B^ = h // error: direct conversion fails | ^ - | Found: (h : S -> B^) - | Required: (x: S) -> B^² + | Found: (h : S -> B^) + | Required: (x: S^{cap.rd}) -> B^² | - | where: ^ refers to a fresh root capability in the type of value h - | ^² refers to a root capability associated with the result type of (x: S): B^² + | where: ^ refers to a fresh root capability in the type of value h + | ^² refers to a root capability associated with the result type of (x: S^{cap.rd}): B^² + | cap is the universal root capability | | - | Note that the existential capture root in B^ - | cannot subsume the capability cap + | Note that the existential capture root in B^ + | cannot subsume the capability cap | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:22:19 ---------------------------------- 22 | val _: S -> B^ = j // error | ^ - | Found: (j : (x: S) -> B^) - | Required: S -> B^² + | Found: (j : (x: S) -> B^) + | Required: S^{cap.rd} -> B^² | - | where: ^ refers to a root capability associated with the result type of (x: S): B^ - | ^² refers to a fresh root capability in the type of value _$11 + | where: ^ refers to a root capability associated with the result type of (x: S^{cap.rd}): B^ + | ^² refers to a fresh root capability in the type of value _$11 + | cap is the universal root capability | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:23:19 ---------------------------------- 23 | val _: S -> B^ = x => j(x) // error | ^^^^^^^^^ - | Found: (x: S) ->? B^{x} - | Required: (x: S) -> B^ + | Found: (x: S^{cap.rd}) ->? B^{x} + | Required: (x: S^{cap.rd}) -> B^ | - | where: ^ refers to a fresh root capability in the type of value _$12 + | where: ^ refers to a fresh root capability in the type of value _$12 + | cap is the universal root capability | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:26:20 ---------------------------------- From 9f0527dda1c25b5b45a619b9d6f1d1b5f5647071 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 7 May 2025 13:59:50 +0200 Subject: [PATCH 16/19] Tighten level checking We had the wrong condition in maxSubsumes before. We need to test for isStaticOwner instead of isStatic. The previous test made all fresh capabilities in global classes slip through the net. --- .../src/dotty/tools/dotc/cc/CCState.scala | 12 +++++------ .../src/dotty/tools/dotc/cc/CaptureOps.scala | 7 ++++++- .../src/dotty/tools/dotc/cc/CaptureRef.scala | 7 +++---- .../src/dotty/tools/dotc/cc/CaptureSet.scala | 5 +++-- .../dotty/tools/dotc/cc/CheckCaptures.scala | 16 +++++++------- .../src/dotty/tools/dotc/cc/SepCheck.scala | 6 +++++- .../src/dotty/tools/dotc/cc/Synthetics.scala | 8 +++---- compiler/src/dotty/tools/dotc/cc/root.scala | 2 +- .../src/dotty/tools/dotc/core/Types.scala | 17 +++++++++++---- .../dotc/transform/OverridingPairs.scala | 3 +-- tests/neg-custom-args/captures/filevar.check | 19 ++++++++++++++++- tests/neg-custom-args/captures/filevar.scala | 4 ++-- tests/neg-custom-args/captures/reaches.check | 21 +++++++++---------- tests/neg-custom-args/captures/reaches.scala | 4 ++-- .../captures/scoped-caps.check | 18 +++------------- .../captures/scoped-caps.scala | 2 +- 16 files changed, 85 insertions(+), 66 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CCState.scala b/compiler/src/dotty/tools/dotc/cc/CCState.scala index eb3c6dbbdf6b..9fecf1076bf0 100644 --- a/compiler/src/dotty/tools/dotc/cc/CCState.scala +++ b/compiler/src/dotty/tools/dotc/cc/CCState.scala @@ -118,7 +118,7 @@ class CCState: private var capIsRoot: Boolean = false - private var treatFreshAsEqual: Boolean = false + private var ignoreFreshLevels: Boolean = false object CCState: @@ -169,15 +169,15 @@ object CCState: * Asserted in override checking, tested in maxSubsumes. * Is this sound? Test case is neg-custom-args/captures/leaked-curried.scala. */ - inline def withTreatFreshAsEqual[T](op: => T)(using Context): T = + inline def ignoringFreshLevels[T](op: => T)(using Context): T = if isCaptureCheckingOrSetup then val ccs = ccState - val saved = ccs.treatFreshAsEqual - ccs.treatFreshAsEqual = true - try op finally ccs.treatFreshAsEqual = saved + val saved = ccs.ignoreFreshLevels + ccs.ignoreFreshLevels = true + try op finally ccs.ignoreFreshLevels = saved else op /** Should all root.Fresh instances be treated equal? */ - def treatFreshAsEqual(using Context): Boolean = ccState.treatFreshAsEqual + def ignoreFreshLevels(using Context): Boolean = ccState.ignoreFreshLevels end CCState diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index 8690edfe86ee..f53650f425b2 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -4,12 +4,13 @@ package cc import core.* import Types.*, Symbols.*, Contexts.*, Annotations.*, Flags.* -import Names.TermName +import Names.{Name, TermName} import ast.{tpd, untpd} import Decorators.*, NameOps.* import config.Printers.capt import util.Property.Key import tpd.* +import Annotations.Annotation import CaptureSet.VarState /** Attachment key for capturing type trees */ @@ -504,6 +505,10 @@ extension (tp: Type) case tp: ThisType => ccState.symLevel(tp.cls).nextInner case _ => CCState.undefinedLevel + def refinedOverride(name: Name, rinfo: Type)(using Context): Type = + RefinedType(tp, name, + AnnotatedType(rinfo, Annotation(defn.RefineOverrideAnnot, util.Spans.NoSpan))) + extension (tp: MethodType) /** A method marks an existential scope unless it is the prefix of a curried method */ def marksExistentialScope(using Context): Boolean = diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala b/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala index 33bf0b08f10b..f4afc375f9e3 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala @@ -131,7 +131,7 @@ trait CaptureRef extends TypeProxy, ValueType: final def ccOwner(using Context): Symbol = this match case root.Fresh(hidden) => hidden.owner - case ref: TermRef => + case ref: NamedType => if ref.isCap then NoSymbol else ref.prefix match case prefix: CaptureRef => prefix.ccOwner @@ -295,9 +295,9 @@ trait CaptureRef extends TypeProxy, ValueType: || this.match case x @ root.Fresh(hidden) => def levelOK = - if ccConfig.useFreshLevels then + if ccConfig.useFreshLevels && !CCState.ignoreFreshLevels then val yOwner = y.adjustedOwner - yOwner.isStatic || x.ccOwner.isContainedIn(yOwner) + yOwner.isStaticOwner || x.ccOwner.isContainedIn(yOwner) else !y.stripReadOnly.isCap && !yIsExistential @@ -307,7 +307,6 @@ trait CaptureRef extends TypeProxy, ValueType: || levelOK && canAddHidden && vs.addHidden(hidden, y) - || ccConfig.useFreshLevels && CCState.treatFreshAsEqual && y.stripReadOnly.isFresh case x @ root.Result(binder) => val result = y match case y @ root.Result(_) => vs.unify(x, y) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index 024b7bdf9462..0c74d6951242 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -209,8 +209,9 @@ sealed abstract class CaptureSet extends Showable: */ def mightAccountFor(x: CaptureRef)(using Context): Boolean = reporting.trace(i"$this mightAccountFor $x, ${x.captureSetOfInfo}?", show = true): - CCState.withCapAsRoot: // OK here since we opportunistically choose an alternative which gets checked later - elems.exists(_.subsumes(x)(using ctx)(using VarState.ClosedUnrecorded)) + CCState.withCapAsRoot: + CCState.ignoringFreshLevels: // OK here since we opportunistically choose an alternative which gets checked later + elems.exists(_.subsumes(x)(using ctx)(using VarState.ClosedUnrecorded)) || !x.isRootCapability && { val elems = x.captureSetOfInfo.elems diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 01a37eba4f1e..9aff98a95744 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -843,8 +843,7 @@ class CheckCaptures extends Recheck, SymTransformer: 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 = refined.refinedOverride(getterName, argType.unboxed) // Yichen you might want to check this allCaptures ++= argType.captureSet (refined, allCaptures) @@ -1507,9 +1506,7 @@ class CheckCaptures extends Recheck, SymTransformer: val cs = actual.captureSet if covariant then cs ++ leaked else - if // CCState.withCapAsRoot: // Not sure withCapAsRoot is OK here, actually - !leaked.subCaptures(cs).isOK - then + if !leaked.subCaptures(cs).isOK then report.error( em"""$expected cannot be box-converted to ${actual.capturing(leaked)} |since the additional capture set $leaked resulting from box conversion is not allowed in $actual""", tree.srcPos) @@ -1708,7 +1705,8 @@ class CheckCaptures extends Recheck, SymTransformer: def traverse(t: Tree)(using Context) = t match case t: Template => - checkAllOverrides(ctx.owner.asClass, OverridingPairsCheckerCC(_, _, t)) + ignoringFreshLevels: + checkAllOverrides(ctx.owner.asClass, OverridingPairsCheckerCC(_, _, t)) case _ => traverseChildren(t) end checkOverrides @@ -1912,7 +1910,8 @@ class CheckCaptures extends Recheck, SymTransformer: arg.withType(arg.nuType.forceBoxStatus( bounds.hi.isBoxedCapturing | bounds.lo.isBoxedCapturing)) CCState.withCapAsRoot: // OK? We need this since bounds use `cap` instead of `fresh` - checkBounds(normArgs, tl) + CCState.ignoringFreshLevels: + checkBounds(normArgs, tl) if ccConfig.postCheckCapturesets then args.lazyZip(tl.paramNames).foreach(checkTypeParam(_, _, fun.symbol)) case _ => @@ -1932,7 +1931,8 @@ class CheckCaptures extends Recheck, SymTransformer: case tree: New => case tree: TypeTree => CCState.withCapAsRoot: - checkAppliedTypesIn(tree.withType(tree.nuType)) + CCState.ignoringFreshLevels: + checkAppliedTypesIn(tree.withType(tree.nuType)) case _ => traverseChildren(t) checkApplied.traverse(unit) end postCheck diff --git a/compiler/src/dotty/tools/dotc/cc/SepCheck.scala b/compiler/src/dotty/tools/dotc/cc/SepCheck.scala index 6434db0638a3..af3c70a0a491 100644 --- a/compiler/src/dotty/tools/dotc/cc/SepCheck.scala +++ b/compiler/src/dotty/tools/dotc/cc/SepCheck.scala @@ -915,7 +915,11 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: traverseChildren(tree) checkValOrDefDef(tree) case tree: DefDef => - inSection: + if tree.symbol.isInlineMethod then + // We currently skip inline method since these seem to generate + // spurious recheck completions. Test case is i20237.scala + capt.println(i"skipping sep check of inline def ${tree.symbol}") + else inSection: consumed.segment: for params <- tree.paramss; case param: ValDef <- params do pushDef(param, emptyRefs) diff --git a/compiler/src/dotty/tools/dotc/cc/Synthetics.scala b/compiler/src/dotty/tools/dotc/cc/Synthetics.scala index 93215e77be96..0f7a3a3ee022 100644 --- a/compiler/src/dotty/tools/dotc/cc/Synthetics.scala +++ b/compiler/src/dotty/tools/dotc/cc/Synthetics.scala @@ -77,12 +77,11 @@ object Synthetics: case tp: MethodOrPoly => tp.derivedLambdaType(resType = augmentResult(tp.resType)) case _ => - val refined = trackedParams.foldLeft(tp) { (parent, pref) => - RefinedType(parent, pref.paramName, + val refined = trackedParams.foldLeft(tp): (parent, pref) => + parent.refinedOverride(pref.paramName, CapturingType( atPhase(ctx.phase.next)(pref.underlying.stripCapturing), CaptureSet(pref))) - } CapturingType(refined, CaptureSet(trackedParams*)) if trackedParams.isEmpty then info else augmentResult(info).showing(i"augment apply/copy type $info to $result", capt) @@ -116,8 +115,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(root.Origin.UnapplyInstance(info))) + val newParamInfo = CapturingType(paramInfo, CaptureSet.universal) val trackedParam = info.paramRefs.head def newResult(tp: Type): Type = tp match case tp: MethodOrPoly => diff --git a/compiler/src/dotty/tools/dotc/cc/root.scala b/compiler/src/dotty/tools/dotc/cc/root.scala index d8b4ba14ca2d..69e5975c9ad5 100644 --- a/compiler/src/dotty/tools/dotc/cc/root.scala +++ b/compiler/src/dotty/tools/dotc/cc/root.scala @@ -155,7 +155,7 @@ object root: case Annot(kind) => this.kind eq kind case _ => false - /** Special treatment of `SubstBindingMaps` which can change the binder of a + /** Special treatment of `SubstBindingMaps` which can change the binder of * Result instances */ override def mapWith(tm: TypeMap)(using Context) = kind match diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 93cdb075f5ab..76cb2a74587b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -868,10 +868,19 @@ object Types extends TypeUtils { pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, overridingRefinement) 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)) + val joint = try + CCState.ignoringFreshLevels: + pdenot.meet( + new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId), pre, isRefinedMethod), + pre, + safeIntersection = ctx.base.pendingMemberSearches.contains(name)) + pdenot.meet( + new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId), pre, isRefinedMethod), + pre, + safeIntersection = ctx.base.pendingMemberSearches.contains(name)) + catch case ex: AssertionError => + println(i"error while do refined $tp . $name, ${pdenot.info} / $rinfo") + throw ex joint match case joint: SingleDenotation if isRefinedMethod diff --git a/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala b/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala index c51222790145..16c50b9cd474 100644 --- a/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala +++ b/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala @@ -226,7 +226,6 @@ object OverridingPairs: else member.name.is(DefaultGetterName) // default getters are not checked for compatibility || - CCState.withTreatFreshAsEqual: - memberTp.overrides(otherTp, member.matchNullaryLoosely || other.matchNullaryLoosely || fallBack) + memberTp.overrides(otherTp, member.matchNullaryLoosely || other.matchNullaryLoosely || fallBack) end OverridingPairs diff --git a/tests/neg-custom-args/captures/filevar.check b/tests/neg-custom-args/captures/filevar.check index 0a619637a2ce..276f261ec058 100644 --- a/tests/neg-custom-args/captures/filevar.check +++ b/tests/neg-custom-args/captures/filevar.check @@ -1,10 +1,27 @@ -- Error: tests/neg-custom-args/captures/filevar.scala:8:6 ------------------------------------------------------------- -8 | var file: File^ = uninitialized // error, was OK under unsealed +8 | var file: File^ = uninitialized // error, under sealed | ^ | Mutable variable file cannot have type File^ since | that type captures the root capability `cap`. | | where: ^ refers to a fresh root capability in the type of variable file +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/filevar.scala:15:12 -------------------------------------- +15 | withFile: f => // error with level checking, was OK under both schemes before + | ^ + |Found: (l: scala.caps.Capability^{cap.rd}) ?->? File^? ->? Unit + |Required: (l: scala.caps.Capability^{cap.rd}) ?-> (f: File^{l}) => Unit + | + |where: => refers to a root capability associated with the result type of (using l: scala.caps.Capability^{cap.rd}): (f: File^{l}) => Unit + | cap is the universal root capability + | + | + |Note that reference l.type + |cannot be included in outer capture set ? of parameter f +16 | val o = Service() +17 | o.file = f +18 | o.log + | + | longer explanation available when compiling with `-explain` -- Warning: tests/neg-custom-args/captures/filevar.scala:11:55 --------------------------------------------------------- 11 |def withFile[T](op: (l: caps.Capability) ?-> (f: File^{l}) => T): T = | ^ diff --git a/tests/neg-custom-args/captures/filevar.scala b/tests/neg-custom-args/captures/filevar.scala index 5b83f1d29380..0a4d16cc1f4b 100644 --- a/tests/neg-custom-args/captures/filevar.scala +++ b/tests/neg-custom-args/captures/filevar.scala @@ -5,14 +5,14 @@ class File: def write(x: String): Unit = ??? class Service: - var file: File^ = uninitialized // error, was OK under unsealed + var file: File^ = uninitialized // error, under sealed def log = file.write("log") // OK, was error under unsealed def withFile[T](op: (l: caps.Capability) ?-> (f: File^{l}) => T): T = op(using caps.cap)(new File) def test = - withFile: f => + withFile: f => // error with level checking, was OK under both schemes before val o = Service() o.file = f o.log diff --git a/tests/neg-custom-args/captures/reaches.check b/tests/neg-custom-args/captures/reaches.check index 095ae91182b5..f99059f8f1af 100644 --- a/tests/neg-custom-args/captures/reaches.check +++ b/tests/neg-custom-args/captures/reaches.check @@ -72,13 +72,16 @@ | ^² refers to a fresh root capability in the type of value id | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:63:27 -------------------------------------- -63 | val f1: File^{id*} = id(f) // error // error - | ^^^^^ - | Found: File^ - | Required: File^{id*} +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:62:38 -------------------------------------- +62 | val leaked = usingFile[File^{id*}]: f => // error // error + | ^ + |Found: (f: File^?) ->? box File^? + |Required: (f: File^) => box File^{id*} | - | where: ^ refers to a fresh root capability in the type of value id + |where: => refers to a fresh root capability created in value leaked when checking argument to parameter f of method usingFile + | ^ refers to the universal root capability +63 | val f1: File^{id*} = id(f) +64 | f1 | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:67:32 -------------------------------------- @@ -123,13 +126,9 @@ | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/reaches.scala:62:31 ----------------------------------------------------------- -62 | val leaked = usingFile[File^{id*}]: f => // error +62 | val leaked = usingFile[File^{id*}]: f => // error // 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 // error - | ^^^ - | id* cannot be tracked since its deep capture set is empty -- Error: tests/neg-custom-args/captures/reaches.scala:70:31 ----------------------------------------------------------- 70 | val leaked = usingFile[File^{id*}]: f => // error // error | ^^^ diff --git a/tests/neg-custom-args/captures/reaches.scala b/tests/neg-custom-args/captures/reaches.scala index cf794da701a6..709f818a02a8 100644 --- a/tests/neg-custom-args/captures/reaches.scala +++ b/tests/neg-custom-args/captures/reaches.scala @@ -59,8 +59,8 @@ def attack2 = val id: File^ -> File^ = x => x // error // val id: File^ -> File^{fresh} - val leaked = usingFile[File^{id*}]: f => // error - val f1: File^{id*} = id(f) // error // error + val leaked = usingFile[File^{id*}]: f => // error // error + val f1: File^{id*} = id(f) f1 def attack3 = diff --git a/tests/neg-custom-args/captures/scoped-caps.check b/tests/neg-custom-args/captures/scoped-caps.check index d4470b219dca..e35af6b10bb9 100644 --- a/tests/neg-custom-args/captures/scoped-caps.check +++ b/tests/neg-custom-args/captures/scoped-caps.check @@ -98,23 +98,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:26:20 ---------------------------------- 26 | val _: A^ => B^ = x => g2(x) // error: g2(x): B^{g2, x}, and the `x` cannot be subsumed by fresh | ^^^^^^^^^^ - | Found: (x: A^) ->{g2} B^² - | Required: (x: A^) => B^³ + | Found: (x: A^) ->{g2} B^{g2, x} + | Required: (x: A^) => B^² | | where: => refers to a fresh root capability in the type of value _$13 | ^ refers to the universal root capability - | ^² refers to a root capability associated with the result type of (x: A^): B^² - | ^³ refers to a fresh root capability in the type of value _$13 - | - | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps.scala:28:24 ---------------------------------- -28 | val _: A^{io} => B^ = x => g3(x) // error, fails level checking of fresh instances - | ^^^^^^^^^^ - | Found: (x: A^{io}) ->{g3} B^ - | Required: (x: A^{io}) => B^² - | - | where: => refers to a fresh root capability in the type of value _$14 - | ^ refers to a root capability associated with the result type of (x: A^{io}): B^ - | ^² refers to a fresh root capability in the type of value _$14 + | ^² refers to a fresh root capability in the type of value _$13 | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/scoped-caps.scala b/tests/neg-custom-args/captures/scoped-caps.scala index f109c4178634..b4705b3d6a62 100644 --- a/tests/neg-custom-args/captures/scoped-caps.scala +++ b/tests/neg-custom-args/captures/scoped-caps.scala @@ -25,4 +25,4 @@ def test(io: Object^): Unit = val g2: A^ => B^ = ??? val _: A^ => B^ = x => g2(x) // error: g2(x): B^{g2, x}, and the `x` cannot be subsumed by fresh val g3: A^ => B^ = ??? - val _: A^{io} => B^ = x => g3(x) // error, fails level checking of fresh instances + val _: A^{io} => B^ = x => g3(x) // ok, now g3(x): B^{g3, x}, which is widened to B^{g3, io} From 0fcd1ab243297aee6a48197ace4431e13672770f Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 8 May 2025 15:02:21 +0200 Subject: [PATCH 17/19] Allow Fresh instances in disallowRootCapability --- .../src/dotty/tools/dotc/cc/CaptureSet.scala | 64 +++++++------ .../dotty/tools/dotc/cc/CheckCaptures.scala | 28 +++--- compiler/src/dotty/tools/dotc/cc/Setup.scala | 89 ++++++++++++------- .../src/dotty/tools/dotc/cc/ccConfig.scala | 2 +- compiler/src/dotty/tools/dotc/cc/root.scala | 12 ++- .../src/dotty/tools/dotc/core/NameKinds.scala | 1 + .../src/dotty/tools/dotc/core/Types.scala | 6 ++ .../dotty/tools/dotc/transform/Recheck.scala | 8 +- tests/neg-custom-args/captures/filevar.check | 7 -- tests/neg-custom-args/captures/filevar.scala | 2 +- tests/neg-custom-args/captures/i15772.check | 27 +++--- tests/neg-custom-args/captures/i21920.check | 4 +- tests/neg-custom-args/captures/reaches.check | 15 ++-- tests/neg-custom-args/captures/reaches.scala | 4 +- .../captures/sep-pairs-2.scala | 21 +++++ .../neg-custom-args/captures/sep-pairs.check | 30 +------ .../neg-custom-args/captures/sep-pairs.scala | 3 +- tests/neg-custom-args/captures/withFile.scala | 17 +++- 18 files changed, 197 insertions(+), 143 deletions(-) create mode 100644 tests/neg-custom-args/captures/sep-pairs-2.scala diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index 0c74d6951242..58b08b077eba 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -352,16 +352,27 @@ sealed abstract class CaptureSet extends Showable: 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 = - val hasRoot = - if ccConfig.newScheme then - elems.exists: elem => - val elem1 = elem.stripReadOnly - elem1.isCap || elem1.isResultRoot - else - containsRootCapability - if hasRoot then handler() + /** A bad root `elem` is inadmissible as a member of this set. What is a bad roots depends + * on the value of `rootLimit`. + * If the limit is null, all capture roots are good. + * If the limit is NoSymbol, all Fresh roots are good, but cap and Result roots are bad. + * If the limit is some other symbol, cap and Result roots are bad, as well as + * all Fresh roots that are contained (via ccOwner) in `rootLimit`. + */ + protected def isBadRoot(rootLimit: Symbol | Null, elem: CaptureRef)(using Context): Boolean = + if rootLimit == null then false + else + val elem1 = elem.stripReadOnly + elem1.isCap + || elem1.isResultRoot + || elem1.isFresh && elem1.ccOwner.isContainedIn(rootLimit) + + /** Invoke `handler` if this set has (or later aquires) a root capability. + * Excluded are Fresh instances unless their ccOwner is contained in `upto`. + * If `upto` is NoSymbol, all Fresh instances are admitted. + */ + def disallowRootCapability(upto: Symbol)(handler: () => Context ?=> Unit)(using Context): this.type = + if elems.exists(isBadRoot(upto, _)) then handler() this /** Invoke handler on the elements to ensure wellformedness of the capture set. @@ -537,7 +548,10 @@ object CaptureSet: /** A handler to be invoked if the root reference `cap` is added to this set */ var rootAddedHandler: () => Context ?=> Unit = () => () - private[CaptureSet] var universalOK = true + /** The limit deciding which capture roots are bad (i.e. cannot be contained in this set). + * @see isBadRoot for details. + */ + private[CaptureSet] var rootLimit: Symbol | Null = null /** A handler to be invoked when new elems are added to this set */ var newElemAddedHandler: CaptureRef => Context ?=> Unit = _ => () @@ -588,7 +602,7 @@ object CaptureSet: assert(elem.isTrackableRef, elem) assert(!this.isInstanceOf[HiddenSet] || summon[VarState].isSeparating, summon[VarState]) elems += elem - if elem.isRootCapability then + if isBadRoot(rootLimit, elem) then rootAddedHandler() newElemAddedHandler(elem) val normElem = if isMaybeSet then elem else elem.stripMaybe @@ -610,16 +624,16 @@ object CaptureSet: private def levelOK(elem: CaptureRef)(using Context): Boolean = elem match case elem @ root.Fresh(_) => - if ccConfig.newScheme then - if !level.isDefined || ccState.symLevel(elem.ccOwner) <= level then true - else - println(i"LEVEL ERROR $elem cannot be included in $this of $owner") - false - else universalOK + !level.isDefined + || ccState.symLevel(elem.ccOwner) <= level + || { + capt.println(i"LEVEL ERROR $elem cannot be included in $this of $owner") + false + } case elem @ root.Result(mt) => - universalOK && (this.isInstanceOf[BiMapped] || isPartOf(mt.resType)) + rootLimit == null && (this.isInstanceOf[BiMapped] || isPartOf(mt.resType)) case elem: TermRef if elem.isCap => - universalOK + rootLimit == null case elem: TermRef if level.isDefined => elem.prefix match case prefix: CaptureRef => @@ -650,10 +664,10 @@ object CaptureSet: else CompareResult.Fail(this :: Nil) - override def disallowRootCapability(handler: () => Context ?=> Unit)(using Context): this.type = - universalOK = false + override def disallowRootCapability(upto: Symbol)(handler: () => Context ?=> Unit)(using Context): this.type = + rootLimit = upto rootAddedHandler = handler - super.disallowRootCapability(handler) + super.disallowRootCapability(upto)(handler) override def ensureWellformed(handler: CaptureRef => (Context) ?=> Unit)(using Context): this.type = newElemAddedHandler = handler @@ -756,7 +770,7 @@ object CaptureSet: * Test case: Without that tweak, logger.scala would not compile. */ class RefiningVar(owner: Symbol)(using Context) extends Var(owner): - override def disallowRootCapability(handler: () => Context ?=> Unit)(using Context) = this + override def disallowRootCapability(upto: Symbol)(handler: () => Context ?=> Unit)(using Context) = this /** A variable that is derived from some other variable via a map or filter. */ abstract class DerivedVar(owner: Symbol, initialElems: Refs)(using @constructorOnly ctx: Context) @@ -814,7 +828,7 @@ object CaptureSet: .showing(i"propagating new elem $elem backward from $this to $source = $result", captDebug) .andAlso(addNewElem(elem)) catch case ex: AssertionError => - println(i"fail while tryInclude $elem of ${elem.getClass} in $this / ${this.summarize}") + println(i"fail while prop backwards tryInclude $elem of ${elem.getClass} in $this / ${this.summarize}") throw ex /** For a BiTypeMap, supertypes of the mapped type also constrain diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 9aff98a95744..740af2254e66 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -123,7 +123,7 @@ object CheckCaptures: * root capability in its capture set or if it refers to a type parameter that * could possibly be instantiated with cap in a way that's visible at the type. */ - private def disallowRootCapabilitiesIn(tp: Type, carrier: Symbol, what: String, have: String, addendum: String, pos: SrcPos)(using Context) = + private def disallowRootCapabilitiesIn(tp: Type, upto: Symbol, what: String, have: String, addendum: String, pos: SrcPos)(using Context) = val check = new TypeTraverser: private val seen = new EqHashSet[TypeRef] @@ -150,7 +150,7 @@ object CheckCaptures: case CapturingType(parent, refs) => if variance >= 0 then val openScopes = openExistentialScopes - refs.disallowRootCapability: () => + refs.disallowRootCapability(upto): () => def part = if t eq tp then "" else @@ -488,7 +488,7 @@ class CheckCaptures extends Recheck, SymTransformer: case _ => CaptureSet.ofType(c.widen, followResult = false) capt.println(i"Widen reach $c to $underlying in ${env.owner}") - underlying.disallowRootCapability: () => + underlying.disallowRootCapability(NoSymbol): () => report.error(em"Local capability $c in ${env.ownerString} cannot have `cap` as underlying capture set", tree.srcPos) recur(underlying, env, lastEnv) @@ -517,7 +517,7 @@ class CheckCaptures extends Recheck, SymTransformer: // fresh capabilities. We don't need to also follow the hidden set since separation // checking makes ure that locally hidden references need to go to @consume parameters. else - underlying.disallowRootCapability: () => + underlying.disallowRootCapability(ctx.owner): () => report.error(em"Local reach capability $c leaks into capture scope of ${env.ownerString}", tree.srcPos) recur(underlying, env, null) case c: TypeRef if c.isParamPath => @@ -590,7 +590,7 @@ class CheckCaptures extends Recheck, SymTransformer: def where = if sym.exists then i" in an argument of $sym" else "" val (addendum, errTree) = if arg.isInferred - then ("\nThis is often caused by a local capability$where\nleaking as part of its result.", fn) + then (i"\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) disallowRootCapabilitiesIn(arg.nuType, NoSymbol, @@ -988,7 +988,7 @@ class CheckCaptures extends Recheck, SymTransformer: if sym.is(Module) then sym.info // Modules are checked by checking the module class else if sym.is(Mutable) && !sym.hasAnnotation(defn.UncheckedCapturesAnnot) then - val (carrier, addendum) = capturedBy.get(sym) match + val addendum = capturedBy.get(sym) match case Some(encl) => val enclStr = if encl.isAnonymousFunction then @@ -997,11 +997,11 @@ class CheckCaptures extends Recheck, SymTransformer: case _ => "" s"an anonymous function$location" else encl.show - (NoSymbol, i"\n\nNote that $sym does not count as local since it is captured by $enclStr") + i"\n\nNote that $sym does not count as local since it is captured by $enclStr" case _ => - (sym, "") + "" disallowRootCapabilitiesIn( - tree.tpt.nuType, carrier, i"Mutable $sym", "have type", addendum, sym.srcPos) + tree.tpt.nuType, NoSymbol, i"Mutable $sym", "have type", addendum, sym.srcPos) checkInferredResult(super.recheckValDef(tree, sym), tree) finally if !sym.is(Param) then @@ -1200,7 +1200,12 @@ class CheckCaptures extends Recheck, SymTransformer: * result type of a try */ override def recheckTry(tree: Try, pt: Type)(using Context): Type = - val tp = super.recheckTry(tree, pt) + val tryOwner = Setup.firstCanThrowEvidence(tree.expr) match + case Some(vd) => vd.symbol.owner + case None => ctx.owner + val bodyType = inContext(ctx.withOwner(tryOwner)): + recheck(tree.expr, pt) + val tp = recheckTryRest(bodyType, tree.cases, tree.finalizer, pt) if Feature.enabled(Feature.saferExceptions) then disallowRootCapabilitiesIn(tp, ctx.owner, "The result of `try`", "have type", @@ -1272,8 +1277,7 @@ class CheckCaptures extends Recheck, SymTransformer: val msg = note match case CompareResult.LevelError(cs, ref) => if ref.stripReadOnly.isCapOrFresh then - def capStr = if ref.isReadOnly then "cap.rd" else "cap" - i"""the universal capability `$capStr` + i"""the universa capability $ref |cannot be included in capture set $cs""" else val levelStr = ref match diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index e7f1438701ec..7dd9c1e0cd30 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -21,6 +21,7 @@ import CCState.* import dotty.tools.dotc.util.NoSourcePosition import CheckCaptures.CheckerAPI import NamerOps.methodType +import NameKinds.{CanThrowEvidenceName, TryOwnerName} /** Operations accessed from CheckCaptures */ trait SetupAPI: @@ -51,6 +52,15 @@ object Setup: Some((res, exc)) case _ => None + + def firstCanThrowEvidence(body: Tree)(using Context): Option[Tree] = body match + case Block(stats, expr) => + if stats.isEmpty then firstCanThrowEvidence(expr) + else stats.find: + case vd: ValDef => vd.symbol.name.is(CanThrowEvidenceName) + case _ => false + case _ => None + end Setup import Setup.* @@ -480,9 +490,9 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: end transformExplicitType /** Update info of `sym` for CheckCaptures phase only */ - private def updateInfo(sym: Symbol, info: Type)(using Context) = + private def updateInfo(sym: Symbol, info: Type, owner: Symbol)(using Context) = toBeUpdated += sym - sym.updateInfo(thisPhase, info, newFlagsFor(sym)) + sym.updateInfo(thisPhase, info, newFlagsFor(sym), owner) toBeUpdated -= sym /** The info of `sym` at the CheckCaptures phase */ @@ -590,6 +600,17 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: traverse(elems) tpt.setNuType(box(transformInferredType(tpt.tpe))) + case tree @ Try(body, catches, finalizer) => + val tryOwner = firstCanThrowEvidence(body) match + case Some(vd) => + newSymbol(ctx.owner, TryOwnerName.fresh(), + Method | Synthetic, ExprType(defn.NothingType), coord = tree.span) + case _ => + ctx.owner + inContext(ctx.withOwner(tryOwner)): + traverse(body) + catches.foreach(traverse) + traverse(finalizer) case _ => traverseChildren(tree) postProcess(tree) @@ -634,6 +655,8 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: // have a new type installed here (meaning hasRememberedType is true) def signatureChanges = tree.tpt.hasNuType || paramSignatureChanges + def ownerChanges = + ctx.owner.name.is(TryOwnerName) def paramsToCap(mt: Type)(using Context): Type = mt match case mt: MethodType => @@ -644,38 +667,40 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: mt.derivedLambdaType(resType = paramsToCap(mt.resType)) case _ => mt - // If there's a change in the signature, update the info of `sym` - if sym.exists && signatureChanges then + // If there's a change in the signature or owner, update the info of `sym` + if sym.exists && (signatureChanges || ownerChanges) then val updatedInfo = - - val paramSymss = sym.paramSymss - def newInfo(using Context) = // will be run in this or next phase - root.toResultInResults(sym, report.error(_, tree.srcPos)): - if sym.is(Method) then - paramsToCap(methodType(paramSymss, localReturnType)) - else tree.tpt.nuType - if tree.tpt.isInstanceOf[InferredTypeTree] - && !sym.is(Param) && !sym.is(ParamAccessor) - then - val prevInfo = sym.info - new LazyType: - def complete(denot: SymDenotation)(using Context) = - assert(ctx.phase == thisPhase.next, i"$sym") - sym.info = prevInfo // set info provisionally so we can analyze the symbol in recheck - completeDef(tree, sym, this) - sym.info = newInfo - .showing(i"new info of $sym = $result", capt) - else if sym.is(Method) then - new LazyType: - def complete(denot: SymDenotation)(using Context) = - sym.info = newInfo - .showing(i"new info of $sym = $result", capt) - else newInfo - updateInfo(sym, updatedInfo) + if signatureChanges then + val paramSymss = sym.paramSymss + def newInfo(using Context) = // will be run in this or next phase + root.toResultInResults(sym, report.error(_, tree.srcPos)): + if sym.is(Method) then + paramsToCap(methodType(paramSymss, localReturnType)) + else tree.tpt.nuType + if tree.tpt.isInstanceOf[InferredTypeTree] + && !sym.is(Param) && !sym.is(ParamAccessor) + then + val prevInfo = sym.info + new LazyType: + def complete(denot: SymDenotation)(using Context) = + assert(ctx.phase == thisPhase.next, i"$sym") + sym.info = prevInfo // set info provisionally so we can analyze the symbol in recheck + completeDef(tree, sym, this) + sym.info = newInfo + .showing(i"new info of $sym = $result", capt) + else if sym.is(Method) then + new LazyType: + def complete(denot: SymDenotation)(using Context) = + sym.info = newInfo + .showing(i"new info of $sym = $result", capt) + else newInfo + else sym.info + val updatedOwner = if ownerChanges then ctx.owner else sym.owner + updateInfo(sym, updatedInfo, updatedOwner) case tree: Bind => val sym = tree.symbol - updateInfo(sym, transformInferredType(sym.info)) + updateInfo(sym, transformInferredType(sym.info), sym.owner) case tree: TypeDef => tree.symbol match case cls: ClassSymbol => @@ -708,7 +733,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: // Install new types and if it is a module class also update module object if (selfInfo1 ne selfInfo) || (ps1 ne ps) then val newInfo = ClassInfo(prefix, cls, ps1, decls, selfInfo1) - updateInfo(cls, newInfo) + updateInfo(cls, newInfo, cls.owner) capt.println(i"update class info of $cls with parents $ps selfinfo $selfInfo to $newInfo") cls.thisType.asInstanceOf[ThisType].invalidateCaches() if cls.is(ModuleClass) then @@ -721,7 +746,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: // This would potentially give stackoverflows when setup is run repeatedly. // One test case is pos-custom-args/captures/checkbounds.scala under // ccConfig.alwaysRepeatRun = true. - updateInfo(modul, CapturingType(modul.info, selfCaptures)) + updateInfo(modul, CapturingType(modul.info, selfCaptures), modul.owner) modul.termRef.invalidateCaches() case _ => case _ => diff --git a/compiler/src/dotty/tools/dotc/cc/ccConfig.scala b/compiler/src/dotty/tools/dotc/cc/ccConfig.scala index 1a9f63b42a69..c66c7972eaf5 100644 --- a/compiler/src/dotty/tools/dotc/cc/ccConfig.scala +++ b/compiler/src/dotty/tools/dotc/cc/ccConfig.scala @@ -56,6 +56,6 @@ object ccConfig: /** Not used currently. Handy for trying out new features */ def newScheme(using Context): Boolean = - Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.8`) + Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.7`) end ccConfig \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/cc/root.scala b/compiler/src/dotty/tools/dotc/cc/root.scala index 69e5975c9ad5..3c6620678b09 100644 --- a/compiler/src/dotty/tools/dotc/cc/root.scala +++ b/compiler/src/dotty/tools/dotc/cc/root.scala @@ -260,9 +260,17 @@ object root: end CapToFresh - /** Maps cap to fresh */ + /** Maps cap to fresh. CapToFresh is a BiTypeMap since we don't want to + * freeze a set when it is mapped. On the other hand, we do not want Fresh + * values to flow back to cap since that would fail disallowRootCapability + * tests elsewhere. We therefore use `withoutMappedFutureElems` to prevent + * the map being installed for future use. + */ def capToFresh(tp: Type, origin: Origin)(using Context): Type = - if ccConfig.useSepChecks then CapToFresh(origin)(tp) else tp + if ccConfig.useSepChecks then + ccState.withoutMappedFutureElems: + CapToFresh(origin)(tp) + else tp /** Maps fresh to cap */ def freshToCap(tp: Type)(using Context): Type = diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index e9575c7d6c4a..ff41eeb81ca0 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -312,6 +312,7 @@ object NameKinds { /** Other unique names */ val CanThrowEvidenceName: UniqueNameKind = new UniqueNameKind("canThrow$") + val TryOwnerName: UniqueNameKind = new UniqueNameKind("try$") val TempResultName: UniqueNameKind = new UniqueNameKind("ev$") val DepParamName: UniqueNameKind = new UniqueNameKind("(param)") val LazyImplicitName: UniqueNameKind = new UniqueNameKind("$_lazy_implicit_$") diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 76cb2a74587b..c8893d03a48c 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -6158,6 +6158,12 @@ object Types extends TypeUtils { trait BiTypeMap extends TypeMap: thisMap => + /** Hook to control behavior on capture set variables. + * If true, install the map on capture set variables so that future elements are also mapped. + * If false, just map the elements currently present in the capture set variable. + */ + def mapFutureElements: Boolean = true + /** The inverse of the type map */ def inverse: BiTypeMap diff --git a/compiler/src/dotty/tools/dotc/transform/Recheck.scala b/compiler/src/dotty/tools/dotc/transform/Recheck.scala index 97c37de2de08..a37035cab9f0 100644 --- a/compiler/src/dotty/tools/dotc/transform/Recheck.scala +++ b/compiler/src/dotty/tools/dotc/transform/Recheck.scala @@ -449,9 +449,11 @@ abstract class Recheck extends Phase, SymTransformer: defn.UnitType def recheckTry(tree: Try, pt: Type)(using Context): Type = - val bodyType = recheck(tree.expr, pt) - val casesTypes = tree.cases.map(recheckCase(_, defn.ThrowableType, pt)) - val finalizerType = recheck(tree.finalizer, defn.UnitType) + recheckTryRest(recheck(tree.expr, pt), tree.cases, tree.finalizer, pt) + + protected def recheckTryRest(bodyType: Type, cases: List[CaseDef], finalizer: Tree, pt: Type)(using Context): Type = + val casesTypes = cases.map(recheckCase(_, defn.ThrowableType, pt)) + val finalizerType = recheck(finalizer, defn.UnitType) TypeComparer.lub(bodyType :: casesTypes) def seqLiteralElemProto(tree: SeqLiteral, pt: Type, declared: Type)(using Context): Type = diff --git a/tests/neg-custom-args/captures/filevar.check b/tests/neg-custom-args/captures/filevar.check index 276f261ec058..d0b85408b43a 100644 --- a/tests/neg-custom-args/captures/filevar.check +++ b/tests/neg-custom-args/captures/filevar.check @@ -1,10 +1,3 @@ --- Error: tests/neg-custom-args/captures/filevar.scala:8:6 ------------------------------------------------------------- -8 | var file: File^ = uninitialized // error, under sealed - | ^ - | Mutable variable file cannot have type File^ since - | that type captures the root capability `cap`. - | - | where: ^ refers to a fresh root capability in the type of variable file -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/filevar.scala:15:12 -------------------------------------- 15 | withFile: f => // error with level checking, was OK under both schemes before | ^ diff --git a/tests/neg-custom-args/captures/filevar.scala b/tests/neg-custom-args/captures/filevar.scala index 0a4d16cc1f4b..047c41c6af05 100644 --- a/tests/neg-custom-args/captures/filevar.scala +++ b/tests/neg-custom-args/captures/filevar.scala @@ -5,7 +5,7 @@ class File: def write(x: String): Unit = ??? class Service: - var file: File^ = uninitialized // error, under sealed + var file: File^ = uninitialized //OK, was error under sealed def log = file.write("log") // OK, was error under unsealed def withFile[T](op: (l: caps.Capability) ?-> (f: File^{l}) => T): T = diff --git a/tests/neg-custom-args/captures/i15772.check b/tests/neg-custom-args/captures/i15772.check index 145bfd7faae8..699229d40764 100644 --- a/tests/neg-custom-args/captures/i15772.check +++ b/tests/neg-custom-args/captures/i15772.check @@ -1,34 +1,31 @@ -- Error: tests/neg-custom-args/captures/i15772.scala:22:46 ------------------------------------------------------------ 22 | val boxed1 : ((C^) => Unit) -> Unit = box1(c) // error | ^^^^^^^ - |C^ => Unit cannot be box-converted to box C{val arg: C^}^{c} ->{cap, c} Unit - |since the additional capture set {c} resulting from box conversion is not allowed in box C{val arg: C^}^{c} => Unit + |C^ => Unit cannot be box-converted to box C{val arg: C^²}^{c} ->{cap, c} Unit + |since the additional capture set {c} resulting from box conversion is not allowed in box C{val arg: C^²}^{c} => Unit | |where: => refers to the universal root capability | ^ refers to the universal root capability + | ^² refers to a fresh root capability in the type of value arg | cap is the universal root capability -- Error: tests/neg-custom-args/captures/i15772.scala:29:35 ------------------------------------------------------------ 29 | val boxed2 : Observe[C^] = box2(c) // error | ^^^^^^^ - |C^ => Unit cannot be box-converted to box C{val arg: C^}^{c} ->{cap, c} Unit - |since the additional capture set {c} resulting from box conversion is not allowed in box C{val arg: C^}^{c} => Unit + |C^ => Unit cannot be box-converted to box C{val arg: C^²}^{c} ->{cap, c} Unit + |since the additional capture set {c} resulting from box conversion is not allowed in box C{val arg: C^²}^{c} => Unit | |where: => refers to the universal root capability | ^ refers to the universal root capability + | ^² refers to a fresh root capability in the type of value arg | cap is the universal root capability --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:35:34 --------------------------------------- +-- Error: tests/neg-custom-args/captures/i15772.scala:35:33 ------------------------------------------------------------ 35 | val boxed2 : Observe[C]^ = box2(c) // error - | ^ - |Found: box C^ - |Required: box C{val arg: C^?}^? + | ^^^^^^^ + | reference cap is not included in the allowed capture set ? + | of the enclosing method main3 | - |where: ^ refers to a fresh root capability created in value boxed2 when instantiating method c's type -> C^{cap} - | - | - |Note that the universal capability `cap` - |cannot be included in capture set ? - | - | longer explanation available when compiling with `-explain` + | Note that the universa capability cap + | cannot be included in capture set ? -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:46:2 ---------------------------------------- 46 | x: (() -> Unit) // error | ^ diff --git a/tests/neg-custom-args/captures/i21920.check b/tests/neg-custom-args/captures/i21920.check index a8dd42b44f1d..301564d23b79 100644 --- a/tests/neg-custom-args/captures/i21920.check +++ b/tests/neg-custom-args/captures/i21920.check @@ -8,7 +8,7 @@ | ^ refers to the universal root capability | | - |Note that the universal capability `cap` - |cannot be included in capture set ? + |Note that reference + |cannot be included in outer capture set ? | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/reaches.check b/tests/neg-custom-args/captures/reaches.check index f99059f8f1af..d83f02f2eb6e 100644 --- a/tests/neg-custom-args/captures/reaches.check +++ b/tests/neg-custom-args/captures/reaches.check @@ -21,13 +21,14 @@ 34 | (() => f.write()) :: Nil | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/reaches.scala:37:6 ------------------------------------------------------------ -37 | 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`. - | - | where: => refers to a fresh root capability in the type of variable cur +-- Error: tests/neg-custom-args/captures/reaches.scala:39:31 ----------------------------------------------------------- +39 | val next: () => Unit = cur.head // error + | ^^^^^^^^ + | reference cap is not included in the allowed capture set ? + | of the enclosing method runAll2 + | + | Note that the universa capability cap + | cannot be included in capture set ? -- Error: tests/neg-custom-args/captures/reaches.scala:44:16 ----------------------------------------------------------- 44 | val cur = Ref[List[Proc]](xs) // error | ^^^^^^^^^^ diff --git a/tests/neg-custom-args/captures/reaches.scala b/tests/neg-custom-args/captures/reaches.scala index 709f818a02a8..edec54719e4c 100644 --- a/tests/neg-custom-args/captures/reaches.scala +++ b/tests/neg-custom-args/captures/reaches.scala @@ -34,9 +34,9 @@ def runAll1(@use xs: List[Proc]): Unit = (() => f.write()) :: Nil def runAll2(@consume xs: List[Proc]): Unit = - var cur: List[Proc] = xs // error + var cur: List[Proc] = xs while cur.nonEmpty do - val next: () => Unit = cur.head + val next: () => Unit = cur.head // error next() cur = cur.tail diff --git a/tests/neg-custom-args/captures/sep-pairs-2.scala b/tests/neg-custom-args/captures/sep-pairs-2.scala new file mode 100644 index 000000000000..cfe6bfbf8014 --- /dev/null +++ b/tests/neg-custom-args/captures/sep-pairs-2.scala @@ -0,0 +1,21 @@ +import caps.Mutable +import caps.cap + +class Ref extends Mutable: + var x = 0 + def get: Int = x + mut def put(y: Int): Unit = x = y + +class Pair[+X, +Y](val fst: X, val snd: Y) + +def twoRefs1(): Pair[Ref^, Ref^] = + Pair(Ref(), Ref()) + +def twoRefs2(): Pair[Ref^, Ref^] = + val p = Pair(Ref(), Ref()) + Pair(p.fst, p.snd) + +def twoRefs3(): Pair[Ref^, Ref^] = // error but should work + val p = Pair(Ref(), Ref()) + p + diff --git a/tests/neg-custom-args/captures/sep-pairs.check b/tests/neg-custom-args/captures/sep-pairs.check index 08e8b6e90287..07460db49656 100644 --- a/tests/neg-custom-args/captures/sep-pairs.check +++ b/tests/neg-custom-args/captures/sep-pairs.check @@ -1,29 +1,3 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/sep-pairs.scala:32:10 ------------------------------------ -32 | Pair(Ref(), Ref()) // error // error: universal capability cannot be included in capture set - | ^^^^^ - | Found: box Ref^ - | Required: box Ref^? - | - | where: ^ refers to a fresh root capability created in method twoRefsBad when constructing mutable Ref - | - | - | Note that the universal capability `cap` - | cannot be included in capture set ? - | - | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/sep-pairs.scala:32:17 ------------------------------------ -32 | Pair(Ref(), Ref()) // error // error: universal capability cannot be included in capture set - | ^^^^^ - | Found: box Ref^ - | Required: box Ref^? - | - | where: ^ refers to a fresh root capability created in method twoRefsBad when constructing mutable Ref - | - | - | Note that the universal capability `cap` - | cannot be included in capture set ? - | - | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/sep-pairs.scala:15:10 --------------------------------------------------------- 15 | val r1: Pair[Ref^, Ref^] = mkPair(r0) // error: overlap at r0 | ^^^^^^^^^^^^^^^^ @@ -46,8 +20,8 @@ | ^² refers to a fresh root capability in the result type of method bad | cap is a fresh root capability in the type of value r1 | cap² is a fresh root capability in the type of value r1 --- Error: tests/neg-custom-args/captures/sep-pairs.scala:44:18 --------------------------------------------------------- -44 | val sameToPair: Pair[Ref^, Ref^] = Pair(fstSame, sndSame) // error +-- Error: tests/neg-custom-args/captures/sep-pairs.scala:43:18 --------------------------------------------------------- +43 | val sameToPair: Pair[Ref^, Ref^] = Pair(fstSame, sndSame) // error | ^^^^^^^^^^^^^^^^ | Separation failure in value sameToPair's type Pair[box Ref^, box Ref^²]. | One part, box Ref^, hides capabilities {fstSame}. diff --git a/tests/neg-custom-args/captures/sep-pairs.scala b/tests/neg-custom-args/captures/sep-pairs.scala index 582c635ce27d..298dc2e563c1 100644 --- a/tests/neg-custom-args/captures/sep-pairs.scala +++ b/tests/neg-custom-args/captures/sep-pairs.scala @@ -29,8 +29,7 @@ def twoRefs2(): SamePair[Ref^] = r3 def twoRefsBad(): Pair[Ref^, Ref^] = - Pair(Ref(), Ref()) // error // error: universal capability cannot be included in capture set - // but should work since this is morally equivalent to `twoRefs` + Pair(Ref(), Ref()) // ok now def test(io: Object^): Unit = val two = twoRefs() diff --git a/tests/neg-custom-args/captures/withFile.scala b/tests/neg-custom-args/captures/withFile.scala index b8cdf96f9143..8e287d8631ba 100644 --- a/tests/neg-custom-args/captures/withFile.scala +++ b/tests/neg-custom-args/captures/withFile.scala @@ -1,10 +1,19 @@ -import java.io.* object Test2: + class File: + def write(): Unit = () + def close(): Unit = () + class Box[+T](x: T) - def usingLogFile[T](op: FileOutputStream^ => T): T = - val logFile = FileOutputStream("log") + def usingLogFile[T](op: File^ => T): T = + val logFile = File() val result = op(logFile) logFile.close() result - private val later = usingLogFile { f => () => f.write(0) } // error + + private val later1 = usingLogFile { f => () => f.write() } // error + private val later2 = usingLogFile { f => Box(f) } // error + private val later3 = usingLogFile[() => Unit]: // error + f => () => f.write() // error + private val later4 = usingLogFile[Box[File^]]: // error + f => Box(f) // error From 802ae178cf829ec3815d0ef3c4782d427970e52b Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Mon, 5 May 2025 16:40:09 +0200 Subject: [PATCH 18/19] Retrigger CI with [test_non_bootstrapped] From 2f135cca20dfa04dfb6141e4ded237d70a5ae95d Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 12 May 2025 21:33:16 +0200 Subject: [PATCH 19/19] Address review comments --- compiler/src/dotty/tools/dotc/cc/CaptureRef.scala | 15 +++++++++++++-- .../src/dotty/tools/dotc/cc/CheckCaptures.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/Types.scala | 10 +--------- tests/neg-custom-args/captures/i15772.check | 2 +- tests/neg-custom-args/captures/reaches.check | 2 +- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala b/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala index f4afc375f9e3..21e05e4663b3 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureRef.scala @@ -128,6 +128,12 @@ trait CaptureRef extends TypeProxy, ValueType: final def isExclusive(using Context): Boolean = !isReadOnly && (isRootCapability || captureSetOfInfo.isExclusive) + /** The owning symbol associated with a capability this is + * - for Fresh capabilities: the owner of the hidden set + * - for TermRefs and TypeRefs: the symbol it refers to + * - for derived and path capabilities: the owner of the underlying capability + * - otherwise NoSymbol + */ final def ccOwner(using Context): Symbol = this match case root.Fresh(hidden) => hidden.owner @@ -143,7 +149,12 @@ trait CaptureRef extends TypeProxy, ValueType: case _ => NoSymbol - final def adjustedOwner(using Context): Symbol = + /** The symbol that represents the level closest-enclosing ccOwner. + * Symbols representing levels are + * - class symbols, but not inner (non-static) module classes + * - method symbols, but not accessors or constructors + */ + final def levelOwner(using Context): Symbol = def adjust(owner: Symbol): Symbol = if !owner.exists || owner.isClass && (!owner.is(Flags.Module) || owner.isStatic) @@ -296,7 +307,7 @@ trait CaptureRef extends TypeProxy, ValueType: case x @ root.Fresh(hidden) => def levelOK = if ccConfig.useFreshLevels && !CCState.ignoreFreshLevels then - val yOwner = y.adjustedOwner + val yOwner = y.levelOwner yOwner.isStaticOwner || x.ccOwner.isContainedIn(yOwner) else !y.stripReadOnly.isCap diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 740af2254e66..d15fa06e64fc 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -744,7 +744,7 @@ class CheckCaptures extends Recheck, SymTransformer: val argType = recheck(arg, freshenedFormal) .showing(i"recheck arg $arg vs $freshenedFormal = $result", capt) if formal.hasAnnotation(defn.UseAnnot) || formal.hasAnnotation(defn.ConsumeAnnot) then - // The @use and/or @consume annotation is added to `formal` by `prep areFunction` + // The @use and/or @consume 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 @@ -1277,7 +1277,7 @@ class CheckCaptures extends Recheck, SymTransformer: val msg = note match case CompareResult.LevelError(cs, ref) => if ref.stripReadOnly.isCapOrFresh then - i"""the universa capability $ref + i"""the universal capability $ref |cannot be included in capture set $cs""" else val levelStr = ref match diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index c8893d03a48c..a1cfe7addf60 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -868,19 +868,11 @@ object Types extends TypeUtils { pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, overridingRefinement) else val isRefinedMethod = rinfo.isInstanceOf[MethodOrPoly] - val joint = try - CCState.ignoringFreshLevels: + val joint = CCState.ignoringFreshLevels: pdenot.meet( new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId), pre, isRefinedMethod), pre, safeIntersection = ctx.base.pendingMemberSearches.contains(name)) - pdenot.meet( - new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId), pre, isRefinedMethod), - pre, - safeIntersection = ctx.base.pendingMemberSearches.contains(name)) - catch case ex: AssertionError => - println(i"error while do refined $tp . $name, ${pdenot.info} / $rinfo") - throw ex joint match case joint: SingleDenotation if isRefinedMethod diff --git a/tests/neg-custom-args/captures/i15772.check b/tests/neg-custom-args/captures/i15772.check index 699229d40764..574040685ad4 100644 --- a/tests/neg-custom-args/captures/i15772.check +++ b/tests/neg-custom-args/captures/i15772.check @@ -24,7 +24,7 @@ | reference cap is not included in the allowed capture set ? | of the enclosing method main3 | - | Note that the universa capability cap + | Note that the universal capability cap | cannot be included in capture set ? -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:46:2 ---------------------------------------- 46 | x: (() -> Unit) // error diff --git a/tests/neg-custom-args/captures/reaches.check b/tests/neg-custom-args/captures/reaches.check index d83f02f2eb6e..1cbd53de836c 100644 --- a/tests/neg-custom-args/captures/reaches.check +++ b/tests/neg-custom-args/captures/reaches.check @@ -27,7 +27,7 @@ | reference cap is not included in the allowed capture set ? | of the enclosing method runAll2 | - | Note that the universa capability cap + | Note that the universal capability cap | cannot be included in capture set ? -- Error: tests/neg-custom-args/captures/reaches.scala:44:16 ----------------------------------------------------------- 44 | val cur = Ref[List[Proc]](xs) // error