diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 051b4d8f3b5b..49fac7ad0cf9 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -34,7 +34,7 @@ import util.common.alwaysTrue import scala.annotation.constructorOnly /** The capture checker */ -object CheckCaptures: +object CheckCaptures { import ast.tpd.* val name: String = "cc" @@ -60,7 +60,7 @@ object CheckCaptures: kind: EnvKind, captured: CaptureSet, outer0: Env | Null, - nestedClosure: Symbol = NoSymbol)(using @constructorOnly ictx: Context): + nestedClosure: Symbol = NoSymbol)(using @constructorOnly ictx: Context) { assert(definesEnv(owner)) captured match @@ -79,7 +79,7 @@ object CheckCaptures: val res = cur cur = cur.outer res - end Env + } def definesEnv(sym: Symbol)(using Context): Boolean = sym.isOneOf(MethodOrLazy) || sym.isClass @@ -101,7 +101,6 @@ object CheckCaptures: case _ => mapOver(tp) override def toString = "SubstParamsMap" - end SubstParamsMap /** Check that a @retains annotation only mentions references that can be tracked. * This check is performed at Typer. @@ -136,8 +135,8 @@ object CheckCaptures: * @param upto controls up to which owner local fresh capabilities should be disallowed. * See disallowBadRoots for details. */ - private def disallowBadRootsIn(tp: Type, upto: Symbol, what: => String, have: => String, addendum: => String, pos: SrcPos)(using Context) = - val check = new TypeTraverser: + private def disallowBadRootsIn(tp: Type, upto: Symbol, what: => String, have: => String, addendum: => String, pos: SrcPos)(using Context) = { + val check = new TypeTraverser { private val seen = new EqHashSet[TypeRef] @@ -190,15 +189,16 @@ object CheckCaptures: finally openExistentialScopes = saved case t => traverseChildren(t) + } check.traverse(tp) - end disallowBadRootsIn + } private def contributesFreshToClass(sym: Symbol)(using Context): Boolean = sym.isField && !sym.isOneOf(DeferredOrTermParamOrAccessor) && !sym.hasAnnotation(defn.UntrackedCapturesAnnot) - trait CheckerAPI: + trait CheckerAPI { /** Complete symbol info of a val or a def */ def completeDef(tree: ValOrDefDef, sym: Symbol, completer: LazyType)(using Context): Type @@ -227,9 +227,9 @@ object CheckCaptures: /** The "use set", i.e. the capture set marked as free at this node. */ def markedFree: CaptureSet - end CheckerAPI - -class CheckCaptures extends Recheck, SymTransformer: + } +} +class CheckCaptures extends Recheck, SymTransformer { thisPhase => import ast.tpd.* @@ -330,7 +330,7 @@ class CheckCaptures extends Recheck, SymTransformer: /** Instantiate capture set variables appearing contra-variantly to their * upper approximation. */ - private def interpolate(tp: Type, sym: Symbol, startingVariance: Int = 1)(using Context): Unit = + private def interpolate(tp: Type, sym: Symbol, startingVariance: Int = 1)(using Context): Unit = { object variances extends TypeTraverser: variance = startingVariance @@ -365,7 +365,7 @@ class CheckCaptures extends Recheck, SymTransformer: variances.traverse(tp) interpolator.traverse(tp) - end interpolate + } /* Also set any previously unset owners of toplevel Fresh instances to improve * error diagnostics in separation checking. @@ -510,7 +510,7 @@ class CheckCaptures extends Recheck, SymTransformer: * environments. At each stage, only include references from `cs` that are outside * the environment's owner */ - def markFree(cs: CaptureSet, tree: Tree, addUseInfo: Boolean = true)(using Context): Unit = + def markFree(cs: CaptureSet, tree: Tree, addUseInfo: Boolean = true)(using Context): Unit = { // A captured reference with the symbol `sym` is visible from the environment // if `sym` is not defined inside the owner of the environment. inline def isVisibleFromEnv(sym: Symbol, env: Env) = @@ -607,7 +607,7 @@ class CheckCaptures extends Recheck, SymTransformer: if !cs.isAlwaysEmpty && !CCState.discardUses then recur(cs, curEnv, null) if addUseInfo then useInfos += ((tree, cs, curEnv)) - end markFree + } /** If capability `c` refers to a parameter that is not implicitly or explicitly * @use declared, report an error. @@ -655,7 +655,7 @@ class CheckCaptures extends Recheck, SymTransformer: * @param sym the constructor symbol (could be a method or a val or a class) * @param args the type arguments */ - def markFreeTypeArgs(fn: Tree, sym: Symbol, args: List[Tree])(using Context): Unit = + def markFreeTypeArgs(fn: Tree, sym: Symbol, args: List[Tree])(using Context): Unit = { def isExempt = sym.isTypeTestOrCast || defn.capsErasedValueMethods.contains(sym) if !isExempt then val paramNames = atPhase(thisPhase.prev): @@ -676,7 +676,7 @@ class CheckCaptures extends Recheck, SymTransformer: val param = fn.symbol.paramNamed(pname) if param.isUseParam then markFree(arg.nuType.deepCaptureSet, errTree) - end markFreeTypeArgs + } /** Rechecking idents involves: * - adding call captures for idents referring to methods @@ -903,7 +903,7 @@ class CheckCaptures extends Recheck, SymTransformer: * only or by a method in the class. Both captures go into the result type. We * could be more precise by distinguishing the two capture sets. */ - private def refineConstructorInstance(resType: Type, mt: MethodType, argTypes: List[Type], constr: Symbol)(using Context): Type = + private def refineConstructorInstance(resType: Type, mt: MethodType, argTypes: List[Type], constr: Symbol)(using Context): Type = { val cls = constr.owner.asClass /** First half of result pair: @@ -948,7 +948,7 @@ class CheckCaptures extends Recheck, SymTransformer: augmentConstructorType(resType, capturedVars(cls)) .showing(i"constr type $mt with $argTypes%, % in $constr = $result", capt) - end refineConstructorInstance + } private def memberCaps(mbr: Symbol)(using Context): List[Capability] = if contributesFreshToClass(mbr) then @@ -973,7 +973,7 @@ class CheckCaptures extends Recheck, SymTransformer: * we add a fresh cap in any case -- this is because we can currently hide * mutability in array vals, an example is neg-customargs/captures/matrix.scala. */ - def captureSetImpliedByFields(cls: ClassSymbol, core: Type)(using Context): CaptureSet = + def captureSetImpliedByFields(cls: ClassSymbol, core: Type)(using Context): CaptureSet = { var infos: List[String] = Nil def pushInfo(msg: => String) = if ctx.settings.YccVerbose.value then infos = msg :: infos @@ -1021,7 +1021,7 @@ class CheckCaptures extends Recheck, SymTransformer: result.hiddenSet.adoptClassifier(cl) maybeRO(result).singletonCaptureSet case _ => maybeRO(fresh).singletonCaptureSet - end captureSetImpliedByFields + } /** Recheck type applications: * - Map existential captures in result to `cap` @@ -1040,7 +1040,6 @@ class CheckCaptures extends Recheck, SymTransformer: includeCallCaptures(tree.symbol, res, tree) checkContains(tree) res - end recheckTypeApply /** Faced with a tree of form `caps.contansImpl[CS, r.type]`, check that `R` is a tracked * capability and assert that `{r} <: CS`. @@ -1080,10 +1079,10 @@ class CheckCaptures extends Recheck, SymTransformer: /** Recheck a lambda of the form * { def $anonfun(...) = ...; closure($anonfun, ...)} */ - override def recheckClosureBlock(mdef: DefDef, expr: Closure, pt: Type)(using Context): Type = + override def recheckClosureBlock(mdef: DefDef, expr: Closure, pt: Type)(using Context): Type = { val anonfun = mdef.symbol - def matchParamsAndResult(paramss: List[ParamClause], pt: Type): Unit = + def matchParamsAndResult(paramss: List[ParamClause], pt: Type): Unit = { //println(i"match $mdef against $pt") paramss match case params :: paramss1 => pt match @@ -1119,7 +1118,7 @@ class CheckCaptures extends Recheck, SymTransformer: updateTpt(resType) case _ => case Nil => - end matchParamsAndResult + } openClosures = (anonfun, pt) :: openClosures // openClosures is needed for errors but currently makes no difference @@ -1133,7 +1132,7 @@ class CheckCaptures extends Recheck, SymTransformer: recheckClosure(expr, pt, forceDependent = true) finally openClosures = openClosures.tail - end recheckClosureBlock + } /** Elements of a SeqLiteral instantiate a Seq or Array parameter, so they * should be boxed. @@ -1148,7 +1147,7 @@ class CheckCaptures extends Recheck, SymTransformer: * - Interpolate contravariant capture set variables in result type. * - for lazy vals: create a nested environment to track captures (similar to methods) */ - override def recheckValDef(tree: ValDef, sym: Symbol)(using Context): Type = + override def recheckValDef(tree: ValDef, sym: Symbol)(using Context): Type = { val savedEnv = curEnv val runInConstructor = !sym.isOneOf(Param | ParamAccessor | Lazy | NonMember) try @@ -1212,7 +1211,7 @@ class CheckCaptures extends Recheck, SymTransformer: |a capability to computed capture sets since it is globally accessible |as a $where. Global values cannot be capabilities.""", tree.namePos) - end recheckValDef + } /** Recheck method definitions: * - check body in a nested environment that tracks uses, in a nested level, @@ -1223,7 +1222,7 @@ class CheckCaptures extends Recheck, SymTransformer: * - Interpolate contravariant capture set variables in result type unless * def is anonymous. */ - override def recheckDefDef(tree: DefDef, sym: Symbol)(using Context): Type = + override def recheckDefDef(tree: DefDef, sym: Symbol)(using Context): Type = { if Synthetics.isExcluded(sym) then sym.info else // Under the deferredReaches setting: If rhs ends in a closure or @@ -1259,7 +1258,7 @@ class CheckCaptures extends Recheck, SymTransformer: // so it is not in general sound to interpolate their types. interpolateIfInferred(tree.tpt, sym) curEnv = saved - end recheckDefDef + } /** Two tests for member definitions with inferred types: * @@ -1276,7 +1275,7 @@ class CheckCaptures extends Recheck, SymTransformer: * See comment on Setup.fieldsWithExplicitTypes. So we have to make sure * that fields with inferred types would not change that capset. */ - def checkInferredResult(tp: Type, tree: ValOrDefDef)(using Context): Type = + def checkInferredResult(tp: Type, tree: ValOrDefDef)(using Context): Type = { val sym = tree.symbol def isExemptFromChecks = @@ -1345,7 +1344,7 @@ class CheckCaptures extends Recheck, SymTransformer: } case _ => tp - end checkInferredResult + } /** The normal rechecking if `sym` was already completed before */ override def skipRecheck(sym: Symbol)(using Context): Boolean = @@ -1392,7 +1391,7 @@ class CheckCaptures extends Recheck, SymTransformer: * to the environment. Other generic parents are represented as TypeApplys, where the * same check is already done in the TypeApply. */ - override def recheckClassDef(tree: TypeDef, impl: Template, cls: ClassSymbol)(using Context): Type = + override def recheckClassDef(tree: TypeDef, impl: Template, cls: ClassSymbol)(using Context): Type = { if Feature.enabled(Feature.separationChecking) then sepChecksEnabled = true val localSet = capturedVars(cls) @@ -1439,7 +1438,7 @@ class CheckCaptures extends Recheck, SymTransformer: finally completed += cls curEnv = saved - end recheckClassDef + } /** If type is of the form `T @requiresCapability(x)`, * mark `x` as free in the current environment. This is used to require the @@ -1534,7 +1533,6 @@ class CheckCaptures extends Recheck, SymTransformer: if tree.isTerm && !pt.isBoxedCapturing && pt != LhsProto then markFree(res.boxedCaptureSet, tree) res - end recheck // ------------------ Adaptation ------------------------------------- // @@ -1597,7 +1595,7 @@ class CheckCaptures extends Recheck, SymTransformer: case _ => NoType inline def testAdapted(actual: Type, expected: Type, tree: Tree, notes: List[Note]) - (fail: (Tree, Type, List[Note]) => Unit)(using Context): Type = + (fail: (Tree, Type, List[Note]) => Unit)(using Context): Type = { var expected1 = alignDependentFunction(expected, actual.stripCapturing) val falseDeps = expected1 ne expected @@ -1657,7 +1655,7 @@ class CheckCaptures extends Recheck, SymTransformer: println(i"SUCCESS $tree for $actual <:< $expected:\n${TypeComparer.explained(_.isSubType(actualBoxed, expected1))}") case _ => actualBoxed - end testAdapted + } /** Turn `expected` into a dependent function when `actual` is dependent. */ private def alignDependentFunction(expected: Type, actual: Type)(using Context): Type = @@ -1703,7 +1701,7 @@ class CheckCaptures extends Recheck, SymTransformer: * So in a sense we use `{Cls.this}` as a placeholder for certain outer captures. * that we needed to be subsumed by `Cls.this`. */ - private def addOuterRefs(expected: Type, actual: Type, pos: SrcPos)(using Context): Type = + private def addOuterRefs(expected: Type, actual: Type, pos: SrcPos)(using Context): Type = { def isPure(info: Type): Boolean = info match case info: PolyType => isPure(info.resType) @@ -1751,7 +1749,7 @@ class CheckCaptures extends Recheck, SymTransformer: expected.derivedCapturingType(ecore, erefs1) case _ => expected - end addOuterRefs + } /** A debugging method for showing the envrionments during capture checking. */ private def debugShowEnvs()(using Context): Unit = @@ -1771,9 +1769,9 @@ class CheckCaptures extends Recheck, SymTransformer: * * @param alwaysConst always make capture set variables constant after adaptation */ - def adaptBoxed(actual: Type, expected: Type, tree: Tree, covariant: Boolean, alwaysConst: Boolean)(using Context): Type = + def adaptBoxed(actual: Type, expected: Type, tree: Tree, covariant: Boolean, alwaysConst: Boolean)(using Context): Type = { - def recur(actual: Type, expected: Type, covariant: Boolean): Type = + def recur(actual: Type, expected: Type, covariant: Boolean): Type = { /** Adapt the inner shape type: get the adapted shape type, and the capture set leaked during adaptation * @param boxed if true we adapt to a boxed expected type @@ -1800,7 +1798,6 @@ class CheckCaptures extends Recheck, SymTransformer: finally curEnv = saved case _ => (actualShape, CaptureSet()) - end adaptShape //val adaptStr = i"adapting $actual ${if covariant then "~~>" else "<~~"} $expected" //println(adaptStr) @@ -1859,10 +1856,10 @@ class CheckCaptures extends Recheck, SymTransformer: else adaptedShape .capturing(if alwaysConst then CaptureSet(captures.elems) else captures) .forceBoxStatus(resultIsBoxed) - end recur + } recur(actual, expected, covariant) - end adaptBoxed + } /** If actual is a tracked Capability `a` and widened is a capturing type T^C, * improve `T^C` to `T^{a}`, following the VAR rule of CC. @@ -1926,7 +1923,6 @@ class CheckCaptures extends Recheck, SymTransformer: if adapted eq improvedVAR // no read-only-adaptation, no reaches added, no box-adaptation then actual // might as well use actual instead of improved widened else adapted.showing(i"adapt $actual vs $expected = $adapted", capt) - end adapt // ---- Unit-level rechecking ------------------------------------------- @@ -1935,15 +1931,15 @@ class CheckCaptures extends Recheck, SymTransformer: * We need to do them here since only at this phase CaptureTypes are relevant * But maybe we can then elide the check during the RefChecks phase under captureChecking? */ - def checkOverrides = new TreeTraverser: - class OverridingPairsCheckerCC(clazz: ClassSymbol, self: Type, tree: Tree)(using Context) extends OverridingPairsChecker(clazz, self): + def checkOverrides = new TreeTraverser { + class OverridingPairsCheckerCC(clazz: ClassSymbol, self: Type, tree: Tree)(using Context) extends OverridingPairsChecker(clazz, self) { /** Omit the check if one of {overriding,overridden} was nnot capture checked */ override def needsCheck(overriding: Symbol, overridden: Symbol)(using Context): Boolean = !setup.isPreCC(overriding) && !setup.isPreCC(overridden) /** Perform box adaptation for override checking */ - override def adaptOverridePair(member: Symbol, memberTp: Type, otherTp: Type)(using Context): Option[(Type, Type)] = + override def adaptOverridePair(member: Symbol, memberTp: Type, otherTp: Type)(using Context): Option[(Type, Type)] = { if member.isType then memberTp match case TypeAlias(_) => @@ -1992,7 +1988,7 @@ class CheckCaptures extends Recheck, SymTransformer: finally curEnv = saved if (actual1 eq memberTp) && (expected1 eq otherTp) then None else Some((actual1, expected1)) - end adaptOverridePair + } override def checkInheritedTraitParameters: Boolean = false @@ -2014,7 +2010,7 @@ class CheckCaptures extends Recheck, SymTransformer: checkAnnot(defn.UseAnnot) checkAnnot(defn.ConsumeAnnot) checkAnnot(defn.ReserveAnnot) - end OverridingPairsCheckerCC + } def traverse(t: Tree)(using Context) = t match @@ -2023,11 +2019,11 @@ class CheckCaptures extends Recheck, SymTransformer: checkAllOverrides(ctx.owner.asClass, OverridingPairsCheckerCC(_, _, t)) case _ => traverseChildren(t) - end checkOverrides + } private val setup: SetupAPI = thisPhase.prev.asInstanceOf[Setup] - override def checkUnit(unit: CompilationUnit)(using Context): Unit = + override def checkUnit(unit: CompilationUnit)(using Context): Unit = { capt.println(i"cc check ${unit.source}") ccState.start() setup.setupUnit(unit.tpdTree, this) @@ -2037,7 +2033,7 @@ class CheckCaptures extends Recheck, SymTransformer: val treeString = show(unit.tpdTree) report.echo(s"$echoHeader\n$treeString\n") - withCaptureSetsExplained: + withCaptureSetsExplained { def iterate(): Unit = super.checkUnit(unit) if !ctx.reporter.errorsReported @@ -2057,7 +2053,8 @@ class CheckCaptures extends Recheck, SymTransformer: postCheckWF(unit.tpdTree) if ctx.settings.YccDebug.value then show(unit.tpdTree) // this does not print tree, but makes its variables visible for dependency printing - end checkUnit + } + } // ----- Checks to do after the rechecking traversal -------------------------- @@ -2068,7 +2065,7 @@ class CheckCaptures extends Recheck, SymTransformer: * it is checked that the inferred self type is universal, in order to assure * that joint and separate compilation give the same result. */ - def checkSelfTypes(unit: tpd.Tree)(using Context): Unit = + def checkSelfTypes(unit: tpd.Tree)(using Context): Unit = { val parentTrees = mutable.HashMap[Symbol, List[Tree]]() unit.foreachSubTree { case cdef @ TypeDef(_, impl: Template) => parentTrees(cdef.symbol) = impl.parents @@ -2110,7 +2107,7 @@ class CheckCaptures extends Recheck, SymTransformer: case _ => parentTrees -= root capt.println(i"checked $root with $selfType") - end checkSelfTypes + } /** Check ill-formed capture sets in a type parameter. We used to be able to * push parameter refs into a capture set in type parameters that this type @@ -2118,7 +2115,7 @@ class CheckCaptures extends Recheck, SymTransformer: * underlying capture sets. But now these should no longer be necessary, so * instead of errors we use assertions. */ - private def checkTypeParam(tree: Tree, paramName: TypeName, meth: Symbol)(using Context): Unit = + private def checkTypeParam(tree: Tree, paramName: TypeName, meth: Symbol)(using Context): Unit = { val checker = new TypeTraverser: private var allowed: SimpleIdentitySet[TermParamRef] = SimpleIdentitySet.empty @@ -2146,7 +2143,7 @@ class CheckCaptures extends Recheck, SymTransformer: if tree.isInstanceOf[InferredTypeTree] then checker.traverse(tree.nuType) - end checkTypeParam + } /** Under the unsealed policy: Arrays are like vars, check that their element types * do not contains `cap` (in fact it would work also to check on array creation @@ -2172,7 +2169,7 @@ class CheckCaptures extends Recheck, SymTransformer: * methods or classes. */ def checkEscapingUses()(using Context) = - for (tree, uses, env) <- useInfos do + for (tree, uses, env) <- useInfos do { val seen = util.EqHashSet[Capability]() // The owner of the innermost environment of kind Boxed @@ -2215,7 +2212,7 @@ class CheckCaptures extends Recheck, SymTransformer: case _ => check(uses) - end for + } end checkEscapingUses /** Check all parent class constructors of classes extending Stateful @@ -2250,8 +2247,8 @@ class CheckCaptures extends Recheck, SymTransformer: * - Check that classes extending Stateful do not extend other classes that do * not extend Stateful yet retain exclusive capabilities */ - def postCheck(unit: tpd.Tree)(using Context): Unit = - val checker = new TreeTraverser: + def postCheck(unit: tpd.Tree)(using Context): Unit = { + val checker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = val lctx = tree match case _: DefTree | _: TypeDef if tree.symbol.exists => ctx.withOwner(tree.symbol) @@ -2275,7 +2272,7 @@ class CheckCaptures extends Recheck, SymTransformer: case TypeDef(_, impl: Template) => checkStatefulInheritance(tree.symbol.asClass, impl.parents) case _ => - end checker + } checker.traverse(unit)(using ctx.withOwner(defn.RootClass)) checkEscapingUses() @@ -2298,7 +2295,7 @@ class CheckCaptures extends Recheck, SymTransformer: checkAppliedTypesIn(tree.withType(tree.nuType)) case _ => traverseChildren(t) checkAppliedTypes.traverse(unit) - end postCheck + } /** Perform the following kinds of checks: * - Check all explicitly written capturing types for well-formedness using `checkWellFormedPost`. @@ -2308,5 +2305,4 @@ class CheckCaptures extends Recheck, SymTransformer: def postCheckWF(unit: tpd.Tree)(using Context): Unit = for chk <- todoAtPostCheck do chk() setup.postCheck() - end CaptureChecker -end CheckCaptures +} diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 1c71b7468c0b..fdea98e1d303 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -10,6 +10,7 @@ import printing.Formatting.hl import config.SourceVersion import cc.CaptureSet import cc.Capabilities.* +import util.Property import scala.annotation.threadUnsafe @@ -41,6 +42,11 @@ object Message: i"\n$what can be rewritten automatically under -rewrite $optionStr." else "" + /** A context property that turns off expansion of `^` or `=>` to explicit cap's. + * Used in the where clauses of error messages. + */ + val NoCapExpansion = new Property.Key[Unit] + /** A note can produce an added string for an error message */ abstract class Note: @@ -103,9 +109,12 @@ object Message: * If the entry was not yet recorded, allocate the next superscript corresponding * to the same string in the same name space. The first recording is the string proper * and following recordings get consecutive superscripts starting with 2. + * @param capOk if true and there is already a cap recorded that matches the entry, + * then use an expanded representation with explicit `cap` for the + * original string. * @return The possibly superscripted version of `str`. */ - def record(str: String, isType: Boolean, entry: Recorded)(using Context): String = + def record(str: String, isType: Boolean, entry: Recorded, capOK: Boolean = false)(using Context): String = if disambi.recordOK(str) then //println(s"recording $str, $isType, $entry") @@ -119,9 +128,6 @@ object Message: 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 binders have the same parameter name lists. @@ -138,13 +144,34 @@ object Message: 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 + /** The superscript in `existing` that matches the current entry, using + * `sameSuperscript` on dealiased entries as a test. The superscript is + * the index of the matching entry in existing, counting from the right + * and starting at 1. I.e last entry in the list has superscript 1, next to + * last entry has superscript 2, and so on. A result of 0 means that no + * matching entry exists. + */ + def matchingSuperscript(existing: List[Recorded]): Int = + lazy val dealiased = followAlias(entry) + existing.dropWhile(alt => !sameSuperscript(dealiased, followAlias(alt))).length + + if capOK && ctx.property(NoCapExpansion).isEmpty + && matchingSuperscript(seen(SeenKey("cap", isType = false))) > 0 + then + val capStr = record("cap", isType = false, entry) + return str match + case "^" => s"^{$capStr}" + case "=>" => s"->{$capStr}" + case "?=>" => s"?->{$capStr}" + + val key = SeenKey(str, isType) + val existing = seen(key) + var sup = matchingSuperscript(existing) + if sup == 0 then + seen(key) = entry :: existing + sup = existing.length + 1 - val suffix = alts.length match { + val suffix = sup match { case 1 => "" case n => n.toString.toCharArray.map { case '0' => '⁰' @@ -184,26 +211,27 @@ object Message: "" } - entry match - case param: TypeParamRef => - s"is a type variable${addendum("constraint", TypeComparer.bounds(param))}" - case param: TermParamRef => - s"is a reference to a value parameter" - case sym: Symbol => - val info = - if (ctx.gadt.contains(sym)) - sym.info & ctx.gadt.fullBounds(sym).nn - else - sym.info - 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: RootCapability => - val relation = - if keys.length > 1 then "refer to" - else if List("^", "=>", "?=>").exists(keys(0).startsWith) then "refers to" - else "is" - s"$relation ${ref.descr}" + inContext(ctx.withProperty(NoCapExpansion, Some(()))): + entry match + case param: TypeParamRef => + s"is a type variable${addendum("constraint", TypeComparer.bounds(param))}" + case param: TermParamRef => + s"is a reference to a value parameter" + case sym: Symbol => + val info = + if (ctx.gadt.contains(sym)) + sym.info & ctx.gadt.fullBounds(sym).nn + else + sym.info + 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: RootCapability => + val relation = + if keys.length > 1 then "refer to" + else if List("^", "=>", "?=>").exists(keys(0).startsWith) then "refers to" + else "is" + s"$relation ${ref.descr}" end explanation /** Produce a where clause with explanations for recorded iterms. @@ -276,14 +304,14 @@ object Message: if isUniversalCaptureSet(refs) && !defn.isFunctionType(parent) && !printDebug && seen.isActive => boxText ~ toTextLocal(parent) - ~ seen.record("^", isType = true, refs.elems.nth(0).asInstanceOf[RootCapability]) + ~ seen.record("^", isType = true, refs.elems.nth(0).asInstanceOf[RootCapability], capOK = true) 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.isActive => - seen.record(arrow(isContextual, isPure = false), isType = true, refs.elems.nth(0).asInstanceOf[RootCapability]) + seen.record(arrow(isContextual, isPure = false), isType = true, refs.elems.nth(0).asInstanceOf[RootCapability], capOK = true) case _ => super.funMiddleText(isContextual, isPure, refs) diff --git a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala index 9366050a5a17..5fb0c1b91733 100644 --- a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala +++ b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala @@ -22,6 +22,11 @@ trait MessageRendering { import Highlight.* import Offsets.* + /** The maximal number of lines of code that are shown in a message after the + * `^` and error message. + */ + private inline val maxRenderedLinesAfterPoint = 3 + /** Remove ANSI coloring from `str`, useful for getting real length of * strings * @@ -64,9 +69,21 @@ trait MessageRendering { val lines = linesFrom(syntax) val (before, after) = pos.beforeAndAfterPoint + def compress(offsetsAndLines: List[(Int, String)]): List[(Int, String)] = + if offsetsAndLines.isEmpty then offsetsAndLines + else + val compressedLines = + if offsetsAndLines.length > maxRenderedLinesAfterPoint then + offsetsAndLines.take(maxRenderedLinesAfterPoint - 2) + ++ List( + (offsetsAndLines(maxRenderedLinesAfterPoint - 2)._1, "..."), + offsetsAndLines.last) + else offsetsAndLines + compressedLines + ( before.zip(lines).map(render), - after.zip(lines.drop(before.length)).map(render), + compress(after.zip(lines.drop(before.length))).map(render), maxLen ) } @@ -140,7 +157,7 @@ trait MessageRendering { * * @return aligned error message */ - private def errorMsg(pos: SourcePosition, msg: String)(using Context, Level, Offset): String = { + private def errorMsg(pos: SourcePosition, msg: String, addLine: Boolean)(using Context, Level, Offset): String = { val padding = msg.linesIterator.foldLeft(pos.startColumnPadding) { (pad, line) => val lineLength = stripColor(line).length val maxPad = math.max(0, ctx.settings.pageWidth.value - offset - lineLength) - offset @@ -149,9 +166,10 @@ trait MessageRendering { else pad } - msg.linesIterator + val msgStr = msg.linesIterator .map { line => offsetBox + (if line.isEmpty then "" else padding + line) } .mkString(EOL) + if addLine then msgStr ++ s"${EOL}$offsetBox" else msgStr } // file.path or munge it to normalize for testing @@ -280,7 +298,7 @@ trait MessageRendering { if pos.exists && pos1.exists && pos1.source.file.exists then val (srcBefore, srcAfter, offset) = sourceLines(pos1) val marker = positionMarker(pos1) - val err = errorMsg(pos1, msg.message) + val err = errorMsg(pos1, msg.message, srcAfter.nonEmpty) sb.append((srcBefore ::: marker :: err :: srcAfter).mkString(EOL)) if inlineStack.nonEmpty then diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 572f4fda4f4e..f09c8c388378 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1861,12 +1861,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * the singleton list consisting of its position in `args`, otherwise `Nil`. */ def paramIndices(param: untpd.ValDef, args: List[untpd.Tree]): List[Int] = { - def loop(args: List[untpd.Tree], start: Int): List[Int] = args match { + + def loop(args: List[untpd.Tree], start: Int): List[Int] = args match case arg :: args1 => val others = loop(args1, start + 1) if (refersTo(arg, param)) start :: others else others case _ => Nil - } + val allIndices = loop(args, 0) if (allIndices.length == 1) allIndices else Nil } diff --git a/tests/neg-custom-args/captures/boundary-homebrew.check b/tests/neg-custom-args/captures/boundary-homebrew.check index 387d1aea2d6b..09134469f848 100644 --- a/tests/neg-custom-args/captures/boundary-homebrew.check +++ b/tests/neg-custom-args/captures/boundary-homebrew.check @@ -10,6 +10,7 @@ |where: ?=> refers to a fresh root capability created in anonymous function of type (using l1²: boundary.Label[boundary.Label[Int]^]^): boundary.Label[Int]^ when checking argument to parameter body of method apply | ^ refers to the universal root capability | cap is a fresh root capability created in anonymous function of type (using l2: boundary.Label[Int]^'s2): Int of parameter parameter l2² of method $anonfun + | 20 | boundary.break(l2)(using l1) 21 | 15 | diff --git a/tests/neg-custom-args/captures/boundary.check b/tests/neg-custom-args/captures/boundary.check index 0c440125a403..f0eef69ea2dd 100644 --- a/tests/neg-custom-args/captures/boundary.check +++ b/tests/neg-custom-args/captures/boundary.check @@ -6,10 +6,11 @@ | The leakage occurred when trying to match the following types: | | Found: scala.util.boundary.Label[Object^'s1] - | Required: scala.util.boundary.Label[Object^]^² + | Required: scala.util.boundary.Label[Object^{cap}]^ + | + | where: ^ refers to a fresh root capability classified as Control in the type of value local + | cap is the universal root capability | - | where: ^ and cap refer to the universal root capability - | ^² refers to a fresh root capability classified as Control in the type of value local 6 | boundary[Unit]: l2 ?=> 7 | boundary.break(l2)(using l1) // error 8 | ??? @@ -47,6 +48,7 @@ | | where: ^ and cap² refer to the universal root capability | ^² and cap refer to a fresh root capability created in package + | 6 | boundary[Unit]: l2 ?=> 7 | boundary.break(l2)(using l1) // error 8 | ??? diff --git a/tests/neg-custom-args/captures/box-adapt-contra.check b/tests/neg-custom-args/captures/box-adapt-contra.check index eeb710c27ae3..f999f172275b 100644 --- a/tests/neg-custom-args/captures/box-adapt-contra.check +++ b/tests/neg-custom-args/captures/box-adapt-contra.check @@ -6,10 +6,10 @@ -- Error: tests/neg-custom-args/captures/box-adapt-contra.scala:13:57 -------------------------------------------------- 13 | val f1: (Cap^{c} => Unit) ->{c} Unit = useCap1[Cap^{c}](c) // error, was ok when cap was a root | ^^^^^^^^^^^^^^^^^^^ - | Cap^{c} => Unit cannot be box-converted to Cap^{c} ->{cap, c} Unit - | since the additional capture set {c} resulting from box conversion is not allowed in Cap^{c} => Unit + | Cap^{c} => Unit cannot be box-converted to Cap^{c} ->{cap, c} Unit + | since the additional capture set {c} resulting from box conversion is not allowed in Cap^{c} ->{cap} Unit | - | where: => and cap refer to the universal root capability + | where: => and cap refer to 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/capt1.check b/tests/neg-custom-args/captures/capt1.check index a684c1463902..f541d77a5f3e 100644 --- a/tests/neg-custom-args/captures/capt1.check +++ b/tests/neg-custom-args/captures/capt1.check @@ -41,6 +41,7 @@ | Required: A | | Note that capability x is not included in capture set {}. + | 28 | def m() = if x == null then y else y | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/check-inferred.check b/tests/neg-custom-args/captures/check-inferred.check index 4fb0b030f7c3..b518051d9910 100644 --- a/tests/neg-custom-args/captures/check-inferred.check +++ b/tests/neg-custom-args/captures/check-inferred.check @@ -6,6 +6,7 @@ | | Inferred type : () ->{Counter.this.count} Unit | Externally visible type: () -> Unit + | 19 | count.put(count.get + 1) -- Error: tests/neg-custom-args/captures/check-inferred.scala:20:13 ---------------------------------------------------- 20 | val decr = () => // error @@ -15,6 +16,7 @@ | | Inferred type : () ->{Counter.this.count} Unit | Externally visible type: () -> Unit + | 21 | count.put(count.get - 1) -- Error: tests/neg-custom-args/captures/check-inferred.scala:24:14 ---------------------------------------------------- 24 | val count = Ref(): Object^ // error // error diff --git a/tests/neg-custom-args/captures/class-caps.check b/tests/neg-custom-args/captures/class-caps.check index 362eee3e6170..dbdd8ffc3097 100644 --- a/tests/neg-custom-args/captures/class-caps.check +++ b/tests/neg-custom-args/captures/class-caps.check @@ -5,6 +5,7 @@ | Required: (Int, Int) -> Int | | Note that capability Test.this.console is not included in capture set {}. + | 19 | log(s"adding a ($a) to b ($b)")(using console) 20 | a + b | @@ -16,6 +17,7 @@ | Required: (Int, Int) -> Int | | Note that capability Test1.this.console is not included in capture set {}. + | 29 | log(s"adding a ($a) to b ($b)")(using console) 30 | a + b | diff --git a/tests/neg-custom-args/captures/classifiers-secondclass.check b/tests/neg-custom-args/captures/classifiers-secondclass.check index af2c3e961658..4ce3933ebc9b 100644 --- a/tests/neg-custom-args/captures/classifiers-secondclass.check +++ b/tests/neg-custom-args/captures/classifiers-secondclass.check @@ -7,6 +7,7 @@ |Note that capability f.write is not included in capture set {cap.only[Read]}. | |where: cap is a fresh root capability created in anonymous function of type (f²: Levels.File^): Unit when checking argument to parameter op of method parReduce + | 42 | f.write(42) // the error stems from here 43 | a + b + f.read() // ok | @@ -20,6 +21,7 @@ |Note that capability f.write is not included in capture set {cap.only[Read]}. | |where: cap is a fresh root capability created in anonymous function of type (g²: Levels.File^): Unit when checking argument to parameter op of method parReduce + | 54 | f.write(42) // the error stems from here 55 | a + b + f.read() + g.read() // ok | @@ -33,6 +35,7 @@ |Note that capability g.write is not included in capture set {cap.only[Read]}. | |where: cap is a fresh root capability created in anonymous function of type (g²: Levels.File^): Unit when checking argument to parameter op of method parReduce + | 57 | g.write(42) // the error stems from here 58 | 0 | diff --git a/tests/neg-custom-args/captures/delayedRunops.check b/tests/neg-custom-args/captures/delayedRunops.check index 05d02af6b5a7..b64944cd1963 100644 --- a/tests/neg-custom-args/captures/delayedRunops.check +++ b/tests/neg-custom-args/captures/delayedRunops.check @@ -5,6 +5,7 @@ | Required: () -> Unit | | Note that capability ops* is not included in capture set {}. + | 13 | val ops1 = ops 14 | runOps(ops1) | @@ -16,6 +17,7 @@ | Required: () -> Unit | | Note that capability ops* is not included in capture set {}. + | 25 | val ops1: List[() ->{ops*} Unit] = ops 26 | runOps(ops1) | diff --git a/tests/neg-custom-args/captures/effect-swaps-explicit.check b/tests/neg-custom-args/captures/effect-swaps-explicit.check index 098054bc31ed..d7538ae790c8 100644 --- a/tests/neg-custom-args/captures/effect-swaps-explicit.check +++ b/tests/neg-custom-args/captures/effect-swaps-explicit.check @@ -6,6 +6,7 @@ | Required: Result[Future[T], Nothing] | | Note that capability fr is not included in capture set {}. + | 65 | fr.await.ok |-------------------------------------------------------------------------------------------------------------------- |Inline stack trace @@ -27,6 +28,7 @@ | |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 + | 70 | fr.await.ok | | longer explanation available when compiling with `-explain` @@ -40,6 +42,7 @@ | |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 fdf00220fa4e..35637778d273 100644 --- a/tests/neg-custom-args/captures/effect-swaps.check +++ b/tests/neg-custom-args/captures/effect-swaps.check @@ -6,6 +6,7 @@ | Required: Result[Future[T], Nothing] | | Note that capability fr is not included in capture set {}. + | 65 | fr.await.ok |-------------------------------------------------------------------------------------------------------------------- |Inline stack trace @@ -27,6 +28,7 @@ | |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 + | 70 | fr.await.ok | | longer explanation available when compiling with `-explain` @@ -40,6 +42,7 @@ | |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/filevar.check b/tests/neg-custom-args/captures/filevar.check index b706eceea7fd..2c146d99c737 100644 --- a/tests/neg-custom-args/captures/filevar.check +++ b/tests/neg-custom-args/captures/filevar.check @@ -11,6 +11,7 @@ | ^ refers to the universal root capability | l is a reference to a value parameter | l² is a reference to a value parameter + | 16 | val o = Service() 17 | o.file = f 18 | o.log diff --git a/tests/neg-custom-args/captures/heal-tparam-cs.check b/tests/neg-custom-args/captures/heal-tparam-cs.check index d4077b27e882..73cb8afabbbb 100644 --- a/tests/neg-custom-args/captures/heal-tparam-cs.check +++ b/tests/neg-custom-args/captures/heal-tparam-cs.check @@ -9,6 +9,7 @@ | |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 + | 11 | () => { c.use() } | | longer explanation available when compiling with `-explain` @@ -24,6 +25,7 @@ |where: => refers to a root capability associated with the result type of (c: Capp^): () => Unit | =>² and ^ refer to the universal root capability | cap is a root capability associated with the result type of (x$0: Capp^'s4): () ->{x$0} Unit + | 16 | (c1: Capp^) => () => { c1.use() } 17 | } | @@ -35,6 +37,7 @@ | Required: (c: Capp^{io}) -> () ->{net} Unit | | Note that capability x$0 is not included in capture set {net}. + | 26 | (c1: Capp^{io}) => () => { c1.use() } 27 | } | diff --git a/tests/neg-custom-args/captures/i15772.check b/tests/neg-custom-args/captures/i15772.check index 08536e05a189..5f77c8430ae3 100644 --- a/tests/neg-custom-args/captures/i15772.check +++ b/tests/neg-custom-args/captures/i15772.check @@ -6,7 +6,7 @@ 22 | val boxed1 : ((C^) => Unit) -> Unit = box1(c) // error | ^^^^^^^ |C^ => Unit cannot be box-converted to C{val arg: C^²}^{c} ->{cap, c} Unit - |since the additional capture set {c} resulting from box conversion is not allowed in C{val arg: C^²}^{c} => Unit + |since the additional capture set {c} resulting from box conversion is not allowed in C{val arg: C^²}^{c} ->{cap} Unit | |where: => and ^ and cap refer to the universal root capability | ^² refers to a fresh root capability in the type of value arg @@ -14,7 +14,7 @@ 29 | val boxed2 : Observe[C^] = box2(c) // error | ^^^^^^^ |C^ => Unit cannot be box-converted to C{val arg: C^²}^{c} ->{cap, c} Unit - |since the additional capture set {c} resulting from box conversion is not allowed in C{val arg: C^²}^{c} => Unit + |since the additional capture set {c} resulting from box conversion is not allowed in C{val arg: C^²}^{c} ->{cap} Unit | |where: => and ^ and cap refer to the universal root capability | ^² refers to a fresh root capability in the type of value arg diff --git a/tests/neg-custom-args/captures/i16114.check b/tests/neg-custom-args/captures/i16114.check index 8f4f707319f5..36c9881a2e75 100644 --- a/tests/neg-custom-args/captures/i16114.check +++ b/tests/neg-custom-args/captures/i16114.check @@ -5,9 +5,9 @@ | Required: Unit ->{io} Unit | | Note that capability fs is not included in capture set {io}. + | 18 | expect[Cap^] { -19 | io.use() -20 | fs +19 |... 21 | } | | longer explanation available when compiling with `-explain` @@ -18,9 +18,9 @@ | Required: Unit ->{fs} Unit | | Note that capability io is not included in capture set {fs}. + | 24 | expect[Cap^] { -25 | fs.use() -26 | io +25 |... 27 | } | | longer explanation available when compiling with `-explain` @@ -31,6 +31,7 @@ | Required: Unit -> Unit | | Note that capability io is not included in capture set {}. + | 36 | expect[Cap^](io) | | longer explanation available when compiling with `-explain` @@ -41,9 +42,9 @@ | Required: Unit -> Unit | | Note that capability io is not included in capture set {}. + | 39 | expect[Cap^] { -40 | io.use() -41 | io +40 |... 42 | } | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i21347.check b/tests/neg-custom-args/captures/i21347.check index a4c90c7a70f2..baa6b4fd2042 100644 --- a/tests/neg-custom-args/captures/i21347.check +++ b/tests/neg-custom-args/captures/i21347.check @@ -12,4 +12,5 @@ | ^ | Local reach capability ops* leaks into capture scope of method runOpsAlt. | You could try to abstract the capabilities referred to by ops* in a capset variable. + | 12 | op() diff --git a/tests/neg-custom-args/captures/i23431.check b/tests/neg-custom-args/captures/i23431.check index 3c8f09cdc1b9..b5108b7c0867 100644 --- a/tests/neg-custom-args/captures/i23431.check +++ b/tests/neg-custom-args/captures/i23431.check @@ -37,6 +37,7 @@ | ^ refers to the universal root capability | cap is a fresh root capability created in anonymous function of type (io3: IO^'s1): Unit of parameter parameter io3² of method $anonfun | cap² is a fresh root capability in the type of variable myIO + | 13 | myIO = io3 | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i23582.check b/tests/neg-custom-args/captures/i23582.check index 7e78d9e8e93b..31327ba3ee8d 100644 --- a/tests/neg-custom-args/captures/i23582.check +++ b/tests/neg-custom-args/captures/i23582.check @@ -7,6 +7,7 @@ |Note that capability write is not included in capture set {cap.only[Read]}. | |where: cap is a fresh root capability created in method test when checking argument to parameter op of method parReduce + | 28 | write(x) 29 | x + y + read() | diff --git a/tests/neg-custom-args/captures/i24309-region-orig.check b/tests/neg-custom-args/captures/i24309-region-orig.check index c4468d781c5a..ba7a13f4a731 100644 --- a/tests/neg-custom-args/captures/i24309-region-orig.check +++ b/tests/neg-custom-args/captures/i24309-region-orig.check @@ -3,7 +3,7 @@ | ^ | Local reach capability r1.R leaks into capture scope of an enclosing function. | You could try to abstract the capabilities referred to by r1.R in a capset variable. + | 18 | var a: Ref^{r1.R} = r1.alloc(0) -19 | var b: Ref^{r2.R} = r2.alloc(0) -20 | val c: Ref^{r1.R} = b +19 |... 21 | a diff --git a/tests/neg-custom-args/captures/lazylists-exceptions.check b/tests/neg-custom-args/captures/lazylists-exceptions.check index c0ca2bd3b258..1ad4ee61ff10 100644 --- a/tests/neg-custom-args/captures/lazylists-exceptions.check +++ b/tests/neg-custom-args/captures/lazylists-exceptions.check @@ -7,6 +7,7 @@ | | Found: LazyList[Int]^{canThrow$1} | Required: LazyList[Int]^'s1 + | 38 | if i > 9 then throw Ex1() 39 | i * i 40 | } diff --git a/tests/neg-custom-args/captures/lazylists2.check b/tests/neg-custom-args/captures/lazylists2.check index adbb35ad07e8..c295225f4167 100644 --- a/tests/neg-custom-args/captures/lazylists2.check +++ b/tests/neg-custom-args/captures/lazylists2.check @@ -37,6 +37,7 @@ 60 | class Mapped2 extends Mapped: // error | ^ | references {f, xs} are not all included in the allowed capture set {} of the self type of class Mapped2 + | 61 | this: Mapped => -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylists2.scala:62:4 ------------------------------------ 62 | new Mapped2 // error diff --git a/tests/neg-custom-args/captures/lazyvals3.check b/tests/neg-custom-args/captures/lazyvals3.check index 5f6990cf8b94..92a257cf384d 100644 --- a/tests/neg-custom-args/captures/lazyvals3.check +++ b/tests/neg-custom-args/captures/lazyvals3.check @@ -5,10 +5,9 @@ | Required: Int ->{ev1, console} Boolean | | Note that capability c is not included in capture set {ev1, console}. + | 25 | if n == 1 then -26 | console.println("CONSOLE: 1") -27 | true -28 | else +26 |... 29 | ev1(n - 1) | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/leaking-iterators.check b/tests/neg-custom-args/captures/leaking-iterators.check index f867fc5d4a79..cfc7cdf32e6e 100644 --- a/tests/neg-custom-args/captures/leaking-iterators.check +++ b/tests/neg-custom-args/captures/leaking-iterators.check @@ -9,6 +9,7 @@ | |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 + | 57 | xs.iterator.map: x => 58 | log.write(x) 59 | x * x diff --git a/tests/neg-custom-args/captures/mut-iterator4.check b/tests/neg-custom-args/captures/mut-iterator4.check index fcf3c9c35916..298faaec996c 100644 --- a/tests/neg-custom-args/captures/mut-iterator4.check +++ b/tests/neg-custom-args/captures/mut-iterator4.check @@ -14,6 +14,7 @@ |Note that capability cap is not included in capture set {it, f}. | |where: cap is a fresh root capability created in method mappedIterator when constructing instance Object with (Iterator[U]^{cap².rd}) {...} + | 22 | def hasNext = it.hasNext 23 | update def next() = f(it.next()) | diff --git a/tests/neg-custom-args/captures/nicolas1.check b/tests/neg-custom-args/captures/nicolas1.check index ec8700133439..d44f681e219f 100644 --- a/tests/neg-custom-args/captures/nicolas1.check +++ b/tests/neg-custom-args/captures/nicolas1.check @@ -7,6 +7,7 @@ | Note that capability tail* is not included in capture set {head}. | | where: ^ refers to the universal root capability + | 11 | all(nextInt(all.length)) | | 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 7a0018962776..ff6aa4200295 100644 --- a/tests/neg-custom-args/captures/reaches.check +++ b/tests/neg-custom-args/captures/reaches.check @@ -9,6 +9,7 @@ |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 | cap is a fresh root capability created in anonymous function of type (f: File^'s1): Unit of parameter parameter f² of method $anonfun + | 21 | cur = (() => f.write()) :: Nil | | longer explanation available when compiling with `-explain` @@ -23,6 +24,7 @@ |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 | cap is a fresh root capability created in anonymous function of type (f: File^'s3): Unit of parameter parameter f² of method $anonfun + | 31 | cur.set: 32 | (() => f.write()) :: Nil | @@ -106,6 +108,7 @@ | Local cap created in type of parameter x leaks into capture scope of an enclosing function | | where: cap is a fresh root capability created in value id of parameter parameter x of method $anonfun + | 61 | val f1: File^{id*} = id(f) 62 | f1 -- Error: tests/neg-custom-args/captures/reaches.scala:42:9 ------------------------------------------------------------ diff --git a/tests/neg-custom-args/captures/real-try.check b/tests/neg-custom-args/captures/real-try.check index 7f574644f7ca..89f7487ac9c8 100644 --- a/tests/neg-custom-args/captures/real-try.check +++ b/tests/neg-custom-args/captures/real-try.check @@ -12,9 +12,9 @@ | This is often caused by a locally generated exception capability leaking as part of its result. | | where: => refers to a fresh root capability classified as Control in the type of given instance canThrow$1 + | 15 | () => foo(1) -16 | catch -17 | case _: Ex1 => ??? +16 |... 18 | case _: Ex2 => ??? -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/real-try.scala:21:4 -------------------------------------- 21 | () => foo(1) // error diff --git a/tests/neg-custom-args/captures/ref-with-file.check b/tests/neg-custom-args/captures/ref-with-file.check index 0e3ebf764e3d..4bbf8eb8a75c 100644 --- a/tests/neg-custom-args/captures/ref-with-file.check +++ b/tests/neg-custom-args/captures/ref-with-file.check @@ -10,6 +10,7 @@ | ^ refers to a fresh root capability classified as Unscoped in the type of value r | ^² refers to the universal root capability | cap is a fresh root capability created in anonymous function of type (f: File^'s1): Ref[File^'s3]^ of parameter parameter f² of method $anonfun + | 19 | val r = Ref(f) 20 | r | @@ -25,6 +26,7 @@ |where: => refers to a fresh root capability created in method Test when checking argument to parameter op of method withFile | ^ refers to the universal root capability | cap is a fresh root capability created in anonymous function of type (f: File^'s6): (??? : -> Nothing) of parameter parameter f² of method $anonfun + | 22 | val r = Ref(f) 23 | ??? | @@ -35,11 +37,12 @@ |Capability cap outlives its scope: it leaks into outer capture set 's8 which is owned by method Test. |The leakage occurred when trying to match the following types: | - |Found: (f: File^'s9, r: Ref[String]^) ->'s10 Ref[String]^ - |Required: (f: File^, r: Ref[String]^) => Ref[String]^'s8 + |Found: (f: File^'s9, r: Ref[String]^{cap}) ->'s10 Ref[String]^{cap} + |Required: (f: File^{cap}, r: Ref[String]^{cap}) => Ref[String]^'s8 + | + |where: => refers to a fresh root capability created in method Test when checking argument to parameter op of method withFileAndRef + | cap is the universal root capability | - |where: => refers to a fresh root capability created in method Test when checking argument to parameter op of method withFileAndRef - | ^ and cap refer to the universal root capability 26 | r.put(f.read()) 27 | r | diff --git a/tests/neg-custom-args/captures/reference-cc.check b/tests/neg-custom-args/captures/reference-cc.check index df4567a5c173..bc6e7b6853b6 100644 --- a/tests/neg-custom-args/captures/reference-cc.check +++ b/tests/neg-custom-args/captures/reference-cc.check @@ -22,6 +22,7 @@ | |where: => refers to a fresh root capability created in value xs when checking argument to parameter op of method usingLogFile | ^ refers to the universal root capability + | 48 | LzyList(1, 2, 3).map { x => f.write(x); x * x } | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/scope-extrusions.check b/tests/neg-custom-args/captures/scope-extrusions.check index d11e6773bc7f..c6346503cb82 100644 --- a/tests/neg-custom-args/captures/scope-extrusions.check +++ b/tests/neg-custom-args/captures/scope-extrusions.check @@ -130,6 +130,7 @@ | |where: => refers to a fresh root capability created in method test when checking argument to parameter op of method withFile | ^ refers to the universal root capability + | 29 | () => println(io) | | 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 d33c008aeb0a..b80ceac7f0b2 100644 --- a/tests/neg-custom-args/captures/try.check +++ b/tests/neg-custom-args/captures/try.check @@ -35,6 +35,7 @@ | |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 + | 37 | () => 38 | raise(new Exception)(using x) 39 | 22 @@ -51,6 +52,7 @@ | |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 + | 49 | () => 50 | raise(new Exception)(using x) 51 | 22 diff --git a/tests/neg-custom-args/captures/vars.check b/tests/neg-custom-args/captures/vars.check index 58a6708dc869..267e74fcd597 100644 --- a/tests/neg-custom-args/captures/vars.check +++ b/tests/neg-custom-args/captures/vars.check @@ -37,6 +37,7 @@ | where: ^ refers to the universal root capability | cap3 is a reference to a value parameter | cap3² is a parameter in an anonymous function in method test + | 37 | def g(x: String): String = if cap3 == cap3 then "" else "a" 38 | g | diff --git a/tests/neg-macros/annot-error-annot.check b/tests/neg-macros/annot-error-annot.check index 2924e67a1227..2b953b938967 100644 --- a/tests/neg-macros/annot-error-annot.check +++ b/tests/neg-macros/annot-error-annot.check @@ -33,12 +33,14 @@ 32 | given gMember2: Num[Int] with // error | ^ | MACRO ERROR + | 33 | def zero = 0 -- Error: tests/neg-macros/annot-error-annot/Test_2.scala:35:8 --------------------------------------------------------- 34 | @error 35 | given gMember3(using DummyImplicit): Num[Int] with // error | ^ | MACRO ERROR + | 36 | def zero = 0 -- Error: tests/neg-macros/annot-error-annot/Test_2.scala:39:8 --------------------------------------------------------- 38 | @error @@ -75,12 +77,14 @@ 54 | given gLocal2: Num[Int] with // error | ^ | MACRO ERROR + | 55 | def zero = 0 -- Error: tests/neg-macros/annot-error-annot/Test_2.scala:57:10 -------------------------------------------------------- 56 | @error 57 | given gLocal3(using DummyImplicit): Num[Int] with // error | ^ | MACRO ERROR + | 58 | def zero = 0 -- Error: tests/neg-macros/annot-error-annot/Test_2.scala:61:10 -------------------------------------------------------- 60 | @error @@ -117,10 +121,12 @@ 10 |given gGlobal2: Num[Int] with // error |^ |MACRO ERROR + | 11 | def zero = 0 -- Error: tests/neg-macros/annot-error-annot/Test_2.scala:13:6 --------------------------------------------------------- 12 |@error 13 |given gGlobal3(using DummyImplicit): Num[Int] with // error |^ |MACRO ERROR + | 14 | def zero = 0 diff --git a/tests/neg-scalajs/js-enums.check b/tests/neg-scalajs/js-enums.check index e54b2a5af2da..8b8a71b1fec3 100644 --- a/tests/neg-scalajs/js-enums.check +++ b/tests/neg-scalajs/js-enums.check @@ -2,6 +2,7 @@ 4 |enum MyEnum extends js.Object: // error |^ |MyEnum extends scala.reflect.Enum which does not extend js.Any. + | 5 | case Foo -- Error: tests/neg-scalajs/js-enums.scala:9:5 ------------------------------------------------------------------------- 7 |@js.native @@ -9,11 +10,13 @@ 9 |enum MyEnumNative extends js.Object: // error |^ |MyEnumNative extends scala.reflect.Enum which does not extend js.Any. + | 10 | case Bar -- Error: tests/neg-scalajs/js-enums.scala:12:5 ------------------------------------------------------------------------ 12 |enum MyEnumAny extends js.Any: // error |^ |Non-native JS classes and objects cannot directly extend AnyRef. They must extend a JS class (native or not). + | 13 | case Foo -- Error: tests/neg-scalajs/js-enums.scala:17:5 ------------------------------------------------------------------------ 15 |@js.native @@ -21,4 +24,5 @@ 17 |enum MyEnumNativeAny extends js.Any: // error |^ |MyEnumNativeAny extends scala.reflect.Enum which does not extend js.Any. + | 18 | case Bar diff --git a/tests/neg/deferred-givens-2.check b/tests/neg/deferred-givens-2.check index 4a29141cc48b..600c35478ee4 100644 --- a/tests/neg/deferred-givens-2.check +++ b/tests/neg/deferred-givens-2.check @@ -2,11 +2,13 @@ 17 |class SortedIntWrong1 extends Sorted: // error |^ |No given instance of type Ord{type Self = SortedIntWrong1.this.Element} was found for inferring the implementation of the deferred given instance given_Ord_Element in trait Sorted + | 18 | type Element = Int 19 | override given (Element is Ord)() -- [E172] Type Error: tests/neg/deferred-givens-2.scala:21:6 ----------------------------------------------------------- 21 |class SortedIntWrong2 extends Sorted: // error |^ |No given instance of type Ord{type Self = SortedIntWrong2.this.Element} was found for inferring the implementation of the deferred given instance given_Ord_Element in trait Sorted + | 22 | type Element = Int 23 | override given (Int is Ord)() diff --git a/tests/neg/i22193.check b/tests/neg/i22193.check index 5a51a272c217..cf64fee627f4 100644 --- a/tests/neg/i22193.check +++ b/tests/neg/i22193.check @@ -22,6 +22,7 @@ 28 | fn3( // error missing argument list for value of type (=> Unit) => Unit | ^ | missing argument list for value of type (=> Unit) => Unit + | 29 | arg = "blue sleeps faster than tuesday", 30 | arg2 = "the quick brown fox jumped over the lazy dog"): | diff --git a/tests/neg/i22670.check b/tests/neg/i22670.check index 21e30ac4542e..3c62f7323104 100644 --- a/tests/neg/i22670.check +++ b/tests/neg/i22670.check @@ -2,10 +2,9 @@ 4 |package xy // error named package required for warning |^ |The package name `i22670-macro$package` will be encoded on the classpath, and can lead to undefined behaviour. + | 5 |import scala.quoted.* - 6 |transparent inline def foo = - 7 | ${ fooImpl } - 8 |def fooImpl(using Quotes): Expr[Any] = + 6 |... 9 | Expr("hello") |-------------------------------------------------------------------------------------------------------------------- | Explanation (enabled by `-explain`) diff --git a/tests/neg/illegal-match-types.check b/tests/neg/illegal-match-types.check index 36862f3b9b92..4b21e866dea7 100644 --- a/tests/neg/illegal-match-types.check +++ b/tests/neg/illegal-match-types.check @@ -5,6 +5,7 @@ | case Inv[Cov[t]] => t | The pattern contains an unaccounted type parameter `t`. | (this error can be ignored for now with `-source:3.3`) + | 8 | case Inv[Cov[t]] => t -- [E191] Type Error: tests/neg/illegal-match-types.scala:10:26 -------------------------------------------------------- 10 |type ContraNesting[X] = X match // error @@ -13,6 +14,7 @@ | case Contra[Cov[t]] => t | The pattern contains an unaccounted type parameter `t`. | (this error can be ignored for now with `-source:3.3`) + | 11 | case Contra[Cov[t]] => t -- [E191] Type Error: tests/neg/illegal-match-types.scala:15:22 -------------------------------------------------------- 15 |type AndTypeMT[X] = X match // error @@ -21,6 +23,7 @@ | case t & Seq[Any] => t | The pattern contains an unaccounted type parameter `t`. | (this error can be ignored for now with `-source:3.3`) + | 16 | case t & Seq[Any] => t -- [E191] Type Error: tests/neg/illegal-match-types.scala:22:33 -------------------------------------------------------- 22 |type TypeAliasWithBoundMT[X] = X match // error @@ -29,6 +32,7 @@ | case IsSeq[t] => t | The pattern contains a type alias `IsSeq`. | (this error can be ignored for now with `-source:3.3`) + | 23 | case IsSeq[t] => t -- [E191] Type Error: tests/neg/illegal-match-types.scala:29:34 -------------------------------------------------------- 29 |type TypeMemberExtractorMT[X] = X match // error @@ -37,6 +41,7 @@ | case TypeMemberAux[t] => t | The pattern contains an abstract type member `TypeMember` that does not refine a member in its parent. | (this error can be ignored for now with `-source:3.3`) + | 30 | case TypeMemberAux[t] => t -- [E191] Type Error: tests/neg/illegal-match-types.scala:40:35 -------------------------------------------------------- 40 |type TypeMemberExtractorMT2[X] = X match // error @@ -45,4 +50,5 @@ | case TypeMemberAux2[t] => t | The pattern contains an abstract type member `TypeMember` with bounds that need verification. | (this error can be ignored for now with `-source:3.3`) + | 41 | case TypeMemberAux2[t] => t diff --git a/tests/neg/singleton-ctx-bound.check b/tests/neg/singleton-ctx-bound.check index 785123c0e680..302d115deb36 100644 --- a/tests/neg/singleton-ctx-bound.check +++ b/tests/neg/singleton-ctx-bound.check @@ -31,4 +31,5 @@ 33 |class D extends A: // error |^ |No given instance of type Singleton{type Self = D.this.Elem} was found for inferring the implementation of the deferred given instance given_Singleton_Elem in trait A. Failed to synthesize an instance of type Singleton{type Self = D.this.Elem}: D.this.Elem is not a singleton + | 34 | type Elem = Int diff --git a/tests/neg/unroll-clause-interleaving.check b/tests/neg/unroll-clause-interleaving.check index eea8a7383e09..e0683997fc36 100644 --- a/tests/neg/unroll-clause-interleaving.check +++ b/tests/neg/unroll-clause-interleaving.check @@ -2,6 +2,7 @@ 6 | final def foo(@unroll x: Int = 0)[T](// error | ^ | Cannot have multiple parameter lists containing `@unroll` annotation + | 7 | s: T, 8 | @unroll y: Boolean = true, 9 | ): String = "" + x + s + y diff --git a/tests/warn/i21258.check b/tests/warn/i21258.check index e9edc3606909..3782f8bf9885 100644 --- a/tests/warn/i21258.check +++ b/tests/warn/i21258.check @@ -2,5 +2,6 @@ 4 | type MT[X] = X match { // warn | ^ | Match type upper bound inferred as String, where previously it was defaulted to Any + | 5 | case Int => String 6 | } diff --git a/tests/warn/nonunit-statement.check b/tests/warn/nonunit-statement.check index 7e7939e1c163..46a75dfd3065 100644 --- a/tests/warn/nonunit-statement.check +++ b/tests/warn/nonunit-statement.check @@ -56,12 +56,9 @@ 96 | if (b) { // warn, at least one branch looks interesting | ^ | unused value of type Int + | 97 | println("true") - 98 | i - 99 | } -100 | else { -101 | println("false") -102 | j + 98 |... 103 | } -- [E176] Potential Issue Warning: tests/warn/nonunit-statement.scala:116:4 -------------------------------------------- 116 | set += a // warn because cannot know whether the `set` was supposed to be consumed or assigned