diff --git a/compiler/src/dotty/tools/dotc/cc/Capability.scala b/compiler/src/dotty/tools/dotc/cc/Capability.scala index 64ca777f1b76..53e272a19ef4 100644 --- a/compiler/src/dotty/tools/dotc/cc/Capability.scala +++ b/compiler/src/dotty/tools/dotc/cc/Capability.scala @@ -5,7 +5,6 @@ package cc import core.* import Types.*, Symbols.*, Contexts.*, Decorators.* import util.{SimpleIdentitySet, EqHashMap} -import typer.ErrorReporting.Addenda import util.common.alwaysTrue import scala.collection.mutable import CCState.* @@ -787,12 +786,9 @@ object Capabilities: && prefixAllowsAddHidden && vs.addHidden(x.hiddenSet, y) case x: ResultCap => - val result = y match + y match case y: ResultCap => vs.unify(x, y) case _ => y.derivesFromShared - if !result then - TypeComparer.addErrorNote(CaptureSet.ExistentialSubsumesFailure(x, y)) - result case GlobalCap => y match case GlobalCap => true @@ -900,7 +896,7 @@ object Capabilities: case _ => c1 def showAsCapability(using Context) = - i"capability ${ctx.printer.toTextCapability(this).show}" + i"${ctx.printer.toTextCapability(this).show}" def toText(printer: Printer): Text = printer.toTextCapability(this) end Capability diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index 125569c16033..c4659ea9fa96 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -15,6 +15,7 @@ import CaptureSet.VarState import Capabilities.* import StdNames.nme import config.Feature +import dotty.tools.dotc.core.NameKinds.TryOwnerName /** Attachment key for capturing type trees */ private val Captures: Key[CaptureSet] = Key() @@ -624,9 +625,13 @@ extension (sym: Symbol) || sym.info.hasAnnotation(defn.ConsumeAnnot) def qualString(prefix: String)(using Context): String = + if !sym.exists then "" else i" $prefix ${sym.ownerString}" + + def ownerString(using Context): String = if !sym.exists then "" - else if sym.isAnonymousFunction then i" $prefix enclosing function" - else i" $prefix $sym" + else if sym.isAnonymousFunction then i"an enclosing function" + else if sym.name.is(TryOwnerName) then i"an enclosing try expression" + else sym.show extension (tp: AnnotatedType) /** Is this a boxed capturing type? */ diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index d3ad7004d55e..8cc30b03a389 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -10,12 +10,11 @@ import annotation.threadUnsafe import annotation.constructorOnly import annotation.internal.sharable import reporting.trace +import reporting.Message.Note import printing.{Showable, Printer} import printing.Texts.* import util.{SimpleIdentitySet, Property, EqHashMap} -import typer.ErrorReporting.Addenda import scala.collection.{mutable, immutable} -import TypeComparer.ErrorNote import CCState.* import TypeOps.AvoidMap import compiletime.uninitialized @@ -161,8 +160,8 @@ sealed abstract class CaptureSet extends Showable: final def keepAlways: Boolean = this.isInstanceOf[EmptyWithProvenance] - def failWith(fail: TypeComparer.ErrorNote)(using Context): false = - TypeComparer.addErrorNote(fail) + def failWith(note: Note)(using Context): false = + TypeComparer.addErrorNote(note) false /** Try to include an element in this capture set. @@ -1301,20 +1300,8 @@ object CaptureSet: /** A TypeMap that is the identity on capabilities */ trait IdentityCaptRefMap extends TypeMap - /** A value of this class is produced and added as a note to ccState - * when a subsumes check decides that an existential variable `ex` cannot be - * instantiated to the other capability `other`. - */ - case class ExistentialSubsumesFailure(val ex: ResultCap, val other: Capability) extends ErrorNote: - def description(using Context): String = - def reason = - if other.isTerminalCapability then "" - else " since that capability is not a `Sharable` capability" - i"""the existential capture root in ${ex.originalBinder.resType} - |cannot subsume the capability $other$reason.""" - /** Failure indicating that `elem` cannot be included in `cs` */ - case class IncludeFailure(cs: CaptureSet, elem: Capability, levelError: Boolean = false) extends ErrorNote, Showable: + case class IncludeFailure(cs: CaptureSet, elem: Capability, levelError: Boolean = false) extends Note, Showable: private var myTrace: List[CaptureSet] = cs :: Nil def trace: List[CaptureSet] = myTrace @@ -1323,36 +1310,77 @@ object CaptureSet: res.myTrace = cs1 :: this.myTrace res - def description(using Context): String = - def why = - val reasons = cs.elems.toList.collect: - case c: FreshCap if !c.acceptsLevelOf(elem) => - i"$elem${elem.levelOwner.qualString("in")} is not visible from $c${c.ccOwner.qualString("in")}" - case c: FreshCap if !elem.tryClassifyAs(c.hiddenSet.classifier) => - i"$c is classified as ${c.hiddenSet.classifier} but $elem is not" - if reasons.isEmpty then "" - else reasons.mkString("\nbecause ", "\nand ", "") - cs match - case cs: Var => - if !cs.levelOK(elem) then - val levelStr = elem match - case ref: TermRef => i", defined in ${ref.symbol.maybeOwner}\n" - case _ => " " - i"""${elem.showAsCapability}${levelStr}cannot be included in outer capture set $cs""" - else if !elem.tryClassifyAs(cs.classifier) then - i"""${elem.showAsCapability} is not classified as ${cs.classifier}, therefore it - |cannot be included in capture set $cs of ${cs.classifier.name} elements""" - else if cs.isBadRoot(elem) then - elem match - case elem: FreshCap => - i"""local ${elem.showAsCapability} created in ${elem.ccOwner} - |cannot be included in outer capture set $cs""" - case _ => - i"universal ${elem.showAsCapability} cannot be included in capture set $cs" - else - i"${elem.showAsCapability} cannot be included in capture set $cs" - case _ => - i"${elem.showAsCapability} is not included in capture set $cs$why" + override def showAsPrefix(using Context) = cs match + case cs: Var => + !cs.levelOK(elem) + || cs.isBadRoot(elem) && elem.isInstanceOf[FreshCap] + case _ => + false + + /** An include failure F1 covers another include failure F2 unless F2 + * strictly subsumes F1, which means they describe the same capture sets + * and the element in F2 is more specific than the element in F1. + */ + override def covers(other: Note)(using Context) = other match + case other @ IncludeFailure(cs1, elem1, _) => + val strictlySubsumes = + cs.elems == cs1.elems + && elem1.singletonCaptureSet.mightSubcapture(elem.singletonCaptureSet) + !strictlySubsumes + case _ => false + + def trailing(msg: String)(using Context): String = + i""" + | + |Note that $msg.""" + + def leading(msg: String)(using Context): String = + i"""$msg. + |The leakage occurred when trying to match the following types: + | + |""" + + def render(using Context): String = cs match + case cs: Var => + def ownerStr = + if !cs.description.isEmpty then "" else cs.owner.qualString("which is owned by") + if !cs.levelOK(elem) then + val outlivesStr = elem match + case ref: TermRef => i"${ref.symbol.maybeOwner.qualString("defined in")} outlives its scope:\n" + case _ => " outlives its scope: " + leading: + i"""Capability ${elem.showAsCapability}${outlivesStr}it leaks into outer capture set $cs$ownerStr""" + else if !elem.tryClassifyAs(cs.classifier) then + trailing: + i"""capability ${elem.showAsCapability} is not classified as ${cs.classifier}, therefore it + |cannot be included in capture set $cs of ${cs.classifier.name} elements""" + else if cs.isBadRoot(elem) then + elem match + case elem: FreshCap => + leading: + i"""Local capability ${elem.showAsCapability} created in ${elem.ccOwner} outlives its scope: + |It leaks into outer capture set $cs$ownerStr""" + case _ => + trailing: + i"universal capability ${elem.showAsCapability} cannot be included in capture set $cs" + else + trailing: + i"capability ${elem.showAsCapability} cannot be included in capture set $cs" + case _ => + def why = + val reasons = cs.elems.toList.collect: + case c: FreshCap if !c.acceptsLevelOf(elem) => + i"$elem${elem.levelOwner.qualString("in")} is not visible from $c${c.ccOwner.qualString("in")}" + case c: FreshCap if !elem.tryClassifyAs(c.hiddenSet.classifier) => + i"$c is classified as ${c.hiddenSet.classifier} but ${elem.showAsCapability} is not" + case c: ResultCap if !c.subsumes(elem) => + val toAdd = if elem.isTerminalCapability then "" else " since that capability is not a SharedCapability" + i"$c, which is existentially bound in ${c.originalBinder.resType}, cannot subsume ${elem.showAsCapability}$toAdd" + if reasons.isEmpty then "" + else reasons.mkString("\nbecause ", "\nand ", "") + + trailing: + i"capability ${elem.showAsCapability} is not included in capture set $cs$why" override def toText(printer: Printer): Text = inContext(printer.printerContext): @@ -1370,11 +1398,19 @@ object CaptureSet: * @param lo the lower type of the orginal type comparison, or NoType if not known * @param hi the upper type of the orginal type comparison, or NoType if not known */ - case class MutAdaptFailure(cs: CaptureSet, lo: Type = NoType, hi: Type = NoType) extends ErrorNote: - def description(using Context): String = + case class MutAdaptFailure(cs: CaptureSet, lo: Type = NoType, hi: Type = NoType) extends Note: + + def render(using Context): String = def ofType(tp: Type) = if tp.exists then i"of the mutable type $tp" else "of a mutable type" - i"""$cs is an exclusive capture set ${ofType(hi)}, - |it cannot subsume a read-only capture set ${ofType(lo)}""" + i""" + | + |Note that $cs is an exclusive capture set ${ofType(hi)}, + |it cannot subsume a read-only capture set ${ofType(lo)}.""" + + // Show only one failure of this kind + override def covers(other: Note)(using Context) = + other.isInstanceOf[MutAdaptFailure] + end MutAdaptFailure /** A VarState serves as a snapshot mechanism that can undo * additions of elements or super sets if an operation fails diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 061aabc03f44..a08e47b9995d 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -14,18 +14,19 @@ import typer.ForceDegree import typer.Inferencing.isFullyDefined import typer.RefChecks.{checkAllOverrides, checkSelfAgainstParents, OverridingPairsChecker} import typer.Checking.{checkBounds, checkAppliedTypesIn} -import typer.ErrorReporting.{Addenda, NothingToAdd, err} +import typer.ErrorReporting.err import typer.ProtoTypes.{LhsProto, WildcardSelectionProto, SelectionProto} import util.{SimpleIdentitySet, EqHashMap, EqHashSet, SrcPos, Property} import util.chaining.tap import transform.{Recheck, PreRecheck, CapturedVars} import Recheck.* import scala.collection.mutable -import CaptureSet.{withCaptureSetsExplained, IncludeFailure, ExistentialSubsumesFailure, MutAdaptFailure} +import CaptureSet.{withCaptureSetsExplained, IncludeFailure, MutAdaptFailure} import CCState.* import StdNames.nme import NameKinds.{DefaultGetterName, WildcardParamName, UniqueNameKind} import reporting.{trace, Message, OverrideError} +import reporting.Message.Note import Annotations.Annotation import Capabilities.* import Mutability.* @@ -200,9 +201,6 @@ object CheckCaptures: && !sym.isOneOf(DeferredOrTermParamOrAccessor) && !sym.hasAnnotation(defn.UntrackedCapturesAnnot) - private def ownerStr(owner: Symbol)(using Context): String = - if owner.isAnonymousFunction then "enclosing function" else owner.show - trait CheckerAPI: /** Complete symbol info of a val or a def */ def completeDef(tree: ValOrDefDef, sym: Symbol, completer: LazyType)(using Context): Type @@ -399,7 +397,7 @@ class CheckCaptures extends Recheck, SymTransformer: case (fail: IncludeFailure) :: _ => fail.cs case _ => target def msg(provisional: Boolean) = - def toAdd: String = errorNotes(otherNotes).toAdd.mkString + def toAdd: String = otherNotes.map(_.render).mkString def descr: String = val d = realTarget.description if d.isEmpty then provenance else "" @@ -1208,7 +1206,7 @@ class CheckCaptures extends Recheck, SymTransformer: // too annoying. This is a hole since a defualt getter's result type // might leak into a type variable. - def fail(tree: Tree, expected: Type, addenda: Addenda): Unit = + def fail(tree: Tree, expected: Type, notes: List[Note]): Unit = def maybeResult = if sym.is(Method) then " result" else "" report.error( em"""$sym needs an explicit$maybeResult type because the inferred type does not conform to @@ -1218,7 +1216,7 @@ class CheckCaptures extends Recheck, SymTransformer: | Externally visible type: $expected""", tree.srcPos) - def addenda(expected: Type) = Addenda: + def addendum(expected: Type) = Note: def result = if tree.isInstanceOf[ValDef] then"" else " result" i""" | @@ -1237,7 +1235,7 @@ class CheckCaptures extends Recheck, SymTransformer: val expected = tpt.tpe.dropAllRetains todoAtPostCheck += { () => withCapAsRoot: - testAdapted(tp, expected, tree.rhs, addenda(expected))(fail) + testAdapted(tp, expected, tree.rhs, addendum(expected) :: Nil)(fail) // The check that inferred <: expected is done after recheck so that it // does not interfere with normal rechecking by constraining capture set variables. } @@ -1444,34 +1442,27 @@ class CheckCaptures extends Recheck, SymTransformer: type BoxErrors = mutable.ListBuffer[Message] | Null - private def errorNotes(notes: List[TypeComparer.ErrorNote])(using Context): Addenda = - if notes.isEmpty then NothingToAdd - else new Addenda: - override def toAdd(using Context) = notes.map: note => - i""" - | - |Note that ${note.description}.""" - /** Addendas for error messages that show where we have under-approximated by - * mapping a a capability in contravariant position to the empty set because + * mapping of a capability in contravariant position to the empty set because * the original result type of the map was not itself a capability. */ - private def addApproxAddenda(using Context) = - new TypeAccumulator[Addenda]: - def apply(add: Addenda, t: Type) = t match + private def addApproxAddenda(using Context): TypeAccumulator[List[Note]] = + new TypeAccumulator: + def apply(notes: List[Note], t: Type) = t match case CapturingType(t, CaptureSet.EmptyWithProvenance(ref, mapped)) => /* val (origCore, kind) = original match case tp @ AnnotatedType(parent, ann) if ann.hasSymbol(defn.ReachCapabilityAnnot) => (parent, " deep") case _ => (original, "")*/ - add ++ Addenda: + Note: i""" | |Note that a capability $ref in a capture set appearing in contravariant position |was mapped to $mapped which is not a capability. Therefore, it was under-approximated to the empty set.""" + :: notes case _ => - foldOver(add, t) + foldOver(notes, t) /** Massage `actual` and `expected` types before checking conformance. * Massaging is done by the methods following this one: @@ -1480,8 +1471,8 @@ class CheckCaptures extends Recheck, SymTransformer: * If the resulting types are not compatible, try again with an actual type * where local capture roots are instantiated to root variables. */ - override def checkConformsExpr(actual: Type, expected: Type, tree: Tree, addenda: Addenda)(using Context): Type = - try testAdapted(actual, expected, tree, addenda)(err.typeMismatch) + override def checkConformsExpr(actual: Type, expected: Type, tree: Tree, notes: List[Note])(using Context): Type = + try testAdapted(actual, expected, tree, notes: List[Note])(err.typeMismatch) catch case ex: AssertionError => println(i"error while checking $tree: $actual against $expected") throw ex @@ -1496,8 +1487,8 @@ class CheckCaptures extends Recheck, SymTransformer: case _ => NoType case _ => NoType - inline def testAdapted(actual: Type, expected: Type, tree: Tree, addenda: Addenda) - (fail: (Tree, Type, Addenda) => Unit)(using Context): Type = + inline def testAdapted(actual: Type, expected: Type, tree: Tree, notes: List[Note]) + (fail: (Tree, Type, List[Note]) => Unit)(using Context): Type = var expected1 = alignDependentFunction(expected, actual.stripCapturing) val falseDeps = expected1 ne expected @@ -1544,11 +1535,12 @@ class CheckCaptures extends Recheck, SymTransformer: } TypeComparer.compareResult(tryCurrentType || tryWidenNamed) match - case TypeComparer.CompareResult.Fail(notes) => + case TypeComparer.CompareResult.Fail(cmpNotes) => capt.println(i"conforms failed for ${tree}: $actual vs $expected") if falseDeps then expected1 = unalignFunction(expected1) - fail(tree.withType(actualBoxed), expected1, - addApproxAddenda(addenda ++ errorNotes(notes), expected1)) + val toAdd0 = notes ++ cmpNotes + val toAdd1 = addApproxAddenda(toAdd0, expected1) + fail(tree.withType(actualBoxed), expected1, toAdd1) actual case /*OK*/ _ => if debugSuccesses then tree match diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 1d1c497b2196..77fdc24a01cc 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -26,6 +26,7 @@ import cc.* import Capabilities.Capability import NameKinds.WildcardParamName import MatchTypes.isConcrete +import reporting.Message.Note import scala.util.boundary, boundary.break /** Provides methods to compare types. @@ -61,7 +62,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling private var monitored = false private var maxErrorLevel: Int = -1 - protected var errorNotes: List[(Int, ErrorNote)] = Nil + protected var errorNotes: List[(Int, Note)] = Nil val undoLog = mutable.ArrayBuffer[() => Unit]() @@ -3323,12 +3324,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling def reduceMatchWith[T](op: MatchReducer => T)(using Context): T = inSubComparer(matchReducer)(op) - /** Add given ErrorNote note, provided there is not yet an error note with - * the same class as `note`. + /** Add given note, provided there is not yet an error note that covers `note` + * If the new note is added, any existing note covered by it is removed first. */ - def addErrorNote(note: ErrorNote): Unit = - if errorNotes.forall(_._2.kind != note.kind) then - errorNotes = (recCount, note) :: errorNotes + def addErrorNote(note: Note)(using Context): Unit = + if !errorNotes.exists(_._2.covers(note)) then + errorNotes = (recCount, note) :: errorNotes.filterConserve(n => !note.covers(n._2)) assert(maxErrorLevel <= recCount) maxErrorLevel = recCount @@ -3355,20 +3356,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling object TypeComparer { - /** A base trait for data producing addenda to error messages */ - trait ErrorNote: - /** A disciminating kind. An error note is not added if it has the same kind - * as an already existing error note. - */ - def kind: Class[?] = getClass - - def description(using Context): String - end ErrorNote - /** A richer compare result, returned by `testSubType` and `test`. */ enum CompareResult: case OK, OKwithGADTUsed, OKwithOpaquesUsed - case Fail(errorNotes: List[ErrorNote]) + case Fail(errorNotes: List[Note]) /** Class for unification variables used in `natValue`. */ private class AnyConstantType extends UncachedGroundType with ValueType { @@ -3546,10 +3537,10 @@ object TypeComparer { def inNestedLevel(op: => Boolean)(using Context): Boolean = currentComparer.inNestedLevel(op) - def addErrorNote(note: ErrorNote)(using Context): Unit = + def addErrorNote(note: Note)(using Context): Unit = currentComparer.addErrorNote(note) - def updateErrorNotes(f: PartialFunction[ErrorNote, ErrorNote])(using Context): Unit = + def updateErrorNotes(f: PartialFunction[Note, Note])(using Context): Unit = currentComparer.errorNotes = currentComparer.errorNotes.mapConserve: p => val (level, note) = p if f.isDefinedAt(note) then (level, f(note)) else p @@ -3963,7 +3954,7 @@ class ExplainingTypeComparer(initctx: Context, short: Boolean) extends TypeCompa private val b = new StringBuilder private var lastForwardGoal: String | Null = null - private def appendFailure(notes: List[ErrorNote]) = + private def appendFailure(notes: List[Note]) = if lastForwardGoal != null then // last was deepest goal that failed b.append(s" = false") for case note: printing.Showable <- notes do diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 32c9bf91f919..f0bb57652fd9 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -41,6 +41,28 @@ object Message: i"\n$what can be rewritten automatically under -rewrite $optionStr." else "" + /** A note can produce an added string for an error message */ + abstract class Note: + + /** Should the note be shown before the actual message or after? + * Default is after. + */ + def showAsPrefix(using Context): Boolean = false + + /** The note rendered as part of an error message */ + def render(using Context): String + + /** If note N1 covers note N2 then N1 and N2 won't be shown together in + * an error message. Instead we show the note that's strictly better in terms + * of the "covers" partial ordering, or, if there's no strict wionner, the first + * added note. + */ + def covers(other: Note)(using Context): Boolean = false + + object Note: + def apply(msg: Context ?=> String) = new Note: + def render(using Context) = msg + enum Disambiguation: case All case AllExcept(strs: List[String]) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index e86538eb8110..c281cbab544e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -18,7 +18,7 @@ import ast.desugar import config.{Feature, MigrationVersion, ScalaVersion} import transform.patmat.Space import transform.patmat.SpaceEngine -import typer.ErrorReporting.{err, matchReductionAddendum, substitutableTypeSymbolsInScope, Addenda, NothingToAdd} +import typer.ErrorReporting.{err, matchReductionAddendum, substitutableTypeSymbolsInScope} import typer.ProtoTypes.{ViewProto, SelectionProto, FunProto} import typer.Implicits.* import typer.Inferencing @@ -38,7 +38,7 @@ import scala.jdk.CollectionConverters.* import dotty.tools.dotc.util.SourceFile import dotty.tools.dotc.config.SourceVersion import DidYouMean.* -import Message.Disambiguation +import Message.{Disambiguation, Note} /** Messages * ======== @@ -298,7 +298,7 @@ extends NotFoundMsg(MissingIdentID) { } } -class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tree], addenda: Addenda = NothingToAdd)(using Context) +class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tree], notes: List[Note] = Nil)(using Context) extends TypeMismatchMsg(found, expected)(TypeMismatchID): private val shouldSuggestNN = @@ -343,7 +343,7 @@ class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tre mapOver(tp) case _ => mapOver(tp) - + val preface = notes.filter(_.showAsPrefix).map(_.render).mkString val found1 = reported(found) reported.setVariance(-1) val expected1 = reported(expected) @@ -351,7 +351,7 @@ class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tre if (found1 frozen_<:< expected1) || reported.fbounded then (found, expected) else (found1, expected1) val (foundStr, expectedStr) = Formatting.typeDiff(found2.normalized, expected2.normalized) - i"""|Found: $foundStr + i"""|${preface}Found: $foundStr |Required: $expectedStr${reported.notes}""" end msg @@ -359,8 +359,7 @@ class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tre def importSuggestions = if expected.isTopType || found.isBottomType then "" else ctx.typer.importSuggestionAddendum(ViewProto(found.widen, expected)) - - addenda.toAdd.mkString ++ super.msgPostscript ++ importSuggestions + notes.filter(!_.showAsPrefix).map(_.render).mkString ++ super.msgPostscript ++ importSuggestions override def explain(using Context) = val treeStr = inTree.map(x => s"\nTree:\n\n${x.show}\n").getOrElse("") diff --git a/compiler/src/dotty/tools/dotc/transform/Recheck.scala b/compiler/src/dotty/tools/dotc/transform/Recheck.scala index 34e3773ba147..63b9525a88f4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Recheck.scala +++ b/compiler/src/dotty/tools/dotc/transform/Recheck.scala @@ -15,7 +15,7 @@ import typer.ErrorReporting.err import typer.ProtoTypes.{AnySelectionProto, LhsProto} import typer.TypeAssigner.seqLitType import typer.ConstFold -import typer.ErrorReporting.{Addenda, NothingToAdd} +import reporting.Message.Note import config.Printers.recheckr import util.Property import StdNames.nme @@ -633,11 +633,11 @@ abstract class Recheck extends Phase, SymTransformer: println(i"fail while $actual iscompat $expected") throw ex - def checkConformsExpr(actual: Type, expected: Type, tree: Tree, addenda: Addenda = NothingToAdd)(using Context): Type = + def checkConformsExpr(actual: Type, expected: Type, tree: Tree, notes: List[Note] = Nil)(using Context): Type = //println(i"check conforms $actual <:< $expected") if !isCompatible(actual, expected) then recheckr.println(i"conforms failed for ${tree}: $actual vs $expected") - err.typeMismatch(tree.withType(actual), expected, addenda) + err.typeMismatch(tree.withType(actual), expected, notes) actual def checkUnit(unit: CompilationUnit)(using Context): Unit = diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index c3393d39ab05..76e55cda279f 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -12,9 +12,9 @@ import util.Spans.NoSpan import util.SrcPos import config.Feature import reporting.* +import Message.Note import collection.mutable - object ErrorReporting { import tpd.* @@ -71,19 +71,6 @@ object ErrorReporting { case _ => foldOver(s, tp) tps.foldLeft("")(collectMatchTrace) - /** A mixin trait that can produce added elements for an error message */ - trait Addenda: - def toAdd(using Context): List[String] - def ++(follow: Addenda) = new Addenda: - def toAdd(using Context) = Addenda.this.toAdd ++ follow.toAdd - - object Addenda: - def apply(msg: Context ?=> String): Addenda = new Addenda: - def toAdd(using Context) = msg :: Nil - - object NothingToAdd extends Addenda: - def toAdd(using Context): List[String] = Nil - class Errors(using Context) { /** An explanatory note to be added to error messages @@ -180,7 +167,7 @@ object ErrorReporting { def patternConstrStr(tree: Tree): String = ??? - def typeMismatch(tree: Tree, pt: Type, addenda: Addenda = NothingToAdd): Tree = { + def typeMismatch(tree: Tree, pt: Type, notes: List[Note] = Nil): Tree = { val normTp = normalize(tree.tpe, pt) val normPt = normalize(pt, pt) @@ -199,11 +186,11 @@ object ErrorReporting { def missingElse = tree match case If(_, _, elsep @ Literal(Constant(()))) if elsep.span.isSynthetic => - Addenda("\nMaybe you are missing an else part for the conditional?") + Note("\nMaybe you are missing an else part for the conditional?") :: Nil case _ => - NothingToAdd + Nil - errorTree(tree, TypeMismatch(treeTp, expectedTp, Some(tree), addenda ++ missingElse)) + errorTree(tree, TypeMismatch(treeTp, expectedTp, Some(tree), notes ++ missingElse)) } /** A subtype log explaining why `found` does not conform to `expected` */ diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index f0569d850cb1..d3e3a0d06bd8 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -31,6 +31,7 @@ import Feature.{migrateTo3, sourceVersion} import config.Printers.{implicits, implicitsDetailed} import collection.mutable import reporting.* +import Message.Note import transform.Splicer import annotation.tailrec @@ -467,7 +468,7 @@ object Implicits: } } - abstract class SearchFailureType extends ErrorType, Addenda { + abstract class SearchFailureType extends ErrorType { def expectedType: Type def argument: Tree @@ -485,7 +486,7 @@ object Implicits: else i"convert from ${argument.tpe} to ${clarify(expectedType)}" } - def toAdd(using Context) = Nil + def notes(using Context): List[Note] = Nil } class NoMatchingImplicits(val expectedType: Type, val argument: Tree, constraint: Constraint = OrderingConstraint.empty) @@ -540,10 +541,12 @@ object Implicits: /** A failure value indicating that an implicit search for a conversion was not tried */ case class TooUnspecific(target: Type) extends NoMatchingImplicits(NoType, EmptyTree, OrderingConstraint.empty): - override def toAdd(using Context) = - i""" - |Note that implicit conversions were not tried because the result of an implicit conversion - |must be more specific than $target""" :: Nil + override def notes(using Context) = + Note: + i""" + |Note that implicit conversions were not tried because the result of an implicit conversion + |must be more specific than $target""" + :: Nil override def msg(using Context) = super.msg.append(i"\nThe expected type $target is not specific enough, so no search was attempted") @@ -567,14 +570,16 @@ object Implicits: str2 = alt2.ref.showRef em"both $str1 and $str2 $qualify".withoutDisambiguation() - override def toAdd(using Context) = + override def notes(using Context) = if !argument.isEmpty && argument.tpe.widen.isRef(defn.NothingClass) then Nil else val what = if (expectedType.isInstanceOf[SelectionProto]) "extension methods" else "conversions" - i""" - |Note that implicit $what cannot be applied because they are ambiguous; - |$explanation""" :: Nil + Note: + i""" + |Note that implicit $what cannot be applied because they are ambiguous; + |$explanation""" + :: Nil def asNested = if nested then this else AmbiguousImplicits(alt1, alt2, expectedType, argument, nested = true) end AmbiguousImplicits diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index fc71c0e43034..3294c1d58ce6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4218,7 +4218,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer then tryExtensionOrConversion(tree, pt, mbrProto, qual, locked, compat, inSelect) else - err.typeMismatch(qual, selProto, failure.reason) // TODO: report NotAMember instead, but need to be aware of failure + err.typeMismatch(qual, selProto, failure.reason.notes) // TODO: report NotAMember instead, but need to be aware of failure rememberSearchFailure(qual, failure) catch case ex: TypeError => nestedFailure(ex) @@ -4889,7 +4889,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else val tree1 = healAdapt(tree, pt) if tree1 ne tree then readapt(tree1) - else err.typeMismatch(tree, pt, failure) + else err.typeMismatch(tree, pt, failure.notes) pt match case _: SelectionProto => diff --git a/tests/neg-custom-args/captures/boundary.check b/tests/neg-custom-args/captures/boundary.check index b64d1edef8a6..0c440125a403 100644 --- a/tests/neg-custom-args/captures/boundary.check +++ b/tests/neg-custom-args/captures/boundary.check @@ -2,11 +2,12 @@ 4 | boundary[AnyRef^]: 5 | l1 ?=> // error // error | ^ + | Capability cap outlives its scope: it leaks into outer capture set 's1 which is owned by value local. + | The leakage occurred when trying to match the following types: + | | Found: scala.util.boundary.Label[Object^'s1] | Required: scala.util.boundary.Label[Object^]^² | - | Note that capability cap cannot be included in outer capture set 's1. - | | 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 ?=> diff --git a/tests/neg-custom-args/captures/cc-poly-2.check b/tests/neg-custom-args/captures/cc-poly-2.check index 77cb6e369c2e..62be3ba87085 100644 --- a/tests/neg-custom-args/captures/cc-poly-2.check +++ b/tests/neg-custom-args/captures/cc-poly-2.check @@ -4,9 +4,9 @@ | Found: (d : Test.D^) | Required: Test.D^{c1} | - | Note that capability cap is not included in capture set {c1}. + | Note that capability d is not included in capture set {c1}. | - | where: ^ and cap refer to a fresh root capability in the type of 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 ------------------------------------ @@ -15,6 +15,6 @@ | Found: (x : Test.D^{d}) | Required: Test.D^{c1} | - | Note that capability d is not included in capture set {c1}. + | Note that capability x is not included in capture set {c1}. | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/effect-swaps-explicit.check b/tests/neg-custom-args/captures/effect-swaps-explicit.check index d46f9d3bf749..098054bc31ed 100644 --- a/tests/neg-custom-args/captures/effect-swaps-explicit.check +++ b/tests/neg-custom-args/captures/effect-swaps-explicit.check @@ -19,10 +19,11 @@ -- [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[Result[Future[T^'s2]^'s3, E^'s4]^'s5]^'s6) ?->{fr, async} Future[T^'s7]^{fr, contextual$9} - |Required: (boundary.Label[Result[Future[T^'s8]^'s9, E^'s10]]^) ?=> Future[T^'s8]^'s9 + |Capability contextual$9 outlives its scope: it leaks into outer capture set 's2 which is owned by method fail4. + |The leakage occurred when trying to match the following types: | - |Note that capability contextual$9 cannot be included in outer capture set 's9. + |Found: (contextual$9: boundary.Label[Result[Future[T^'s3]^'s4, E^'s5]^'s6]^'s7) ?->{fr, async} Future[T^'s8]^{fr, contextual$9} + |Required: (boundary.Label[Result[Future[T^'s9]^'s2, E^'s10]]^) ?=> Future[T^'s9]^'s2 | |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 diff --git a/tests/neg-custom-args/captures/effect-swaps.check b/tests/neg-custom-args/captures/effect-swaps.check index 2148de702501..fdf00220fa4e 100644 --- a/tests/neg-custom-args/captures/effect-swaps.check +++ b/tests/neg-custom-args/captures/effect-swaps.check @@ -19,10 +19,11 @@ -- [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[Result[Future[T^'s2]^'s3, E^'s4]^'s5]^'s6) ?->{fr, async} Future[T^'s7]^{fr, contextual$9} - |Required: (boundary.Label[Result[Future[T^'s8]^'s9, E^'s10]]^) ?=> Future[T^'s8]^'s9 + |Capability contextual$9 outlives its scope: it leaks into outer capture set 's2 which is owned by method fail4. + |The leakage occurred when trying to match the following types: | - |Note that capability contextual$9 cannot be included in outer capture set 's9. + |Found: (contextual$9: boundary.Label[Result[Future[T^'s3]^'s4, E^'s5]^'s6]^'s7) ?->{fr, async} Future[T^'s8]^{fr, contextual$9} + |Required: (boundary.Label[Result[Future[T^'s9]^'s2, E^'s10]]^) ?=> Future[T^'s9]^'s2 | |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 diff --git a/tests/neg-custom-args/captures/filevar.check b/tests/neg-custom-args/captures/filevar.check index 41d749727a04..ed3996b1e4d8 100644 --- a/tests/neg-custom-args/captures/filevar.check +++ b/tests/neg-custom-args/captures/filevar.check @@ -1,10 +1,11 @@ -- [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^) ?->'s1 File^'s2 ->'s3 Unit - |Required: (l: scala.caps.Capability^) ?-> (f: File^{l}) => Unit + |Capability l outlives its scope: it leaks into outer capture set 's1 of parameter f. + |The leakage occurred when trying to match the following types: | - |Note that capability l cannot be included in outer capture set 's4 of parameter f. + |Found: (l: scala.caps.Capability^) ?->'s2 File^'s3 ->'s4 Unit + |Required: (l: scala.caps.Capability^) ?-> (f: File^{l}) => Unit | |where: => refers to a root capability associated with the result type of (using l: scala.caps.Capability^): (f: File^{l}) => Unit | ^ refers to the universal root capability diff --git a/tests/neg-custom-args/captures/heal-tparam-cs.check b/tests/neg-custom-args/captures/heal-tparam-cs.check index eb910f007614..d4077b27e882 100644 --- a/tests/neg-custom-args/captures/heal-tparam-cs.check +++ b/tests/neg-custom-args/captures/heal-tparam-cs.check @@ -1,10 +1,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/heal-tparam-cs.scala:10:25 ------------------------------- 10 | val test1 = localCap { c => // error | ^ - |Found: (c: Capp^'s1) ->'s2 () ->{c} Unit - |Required: (c: Capp^) => () ->'s3 Unit + |Capability c outlives its scope: it leaks into outer capture set 's1 which is owned by value test1. + |The leakage occurred when trying to match the following types: | - |Note that capability c cannot be included in outer capture set 's3. + |Found: (c: Capp^'s2) ->'s3 () ->{c} Unit + |Required: (c: Capp^) => () ->'s1 Unit | |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 @@ -14,17 +15,15 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/heal-tparam-cs.scala:15:13 ------------------------------- 15 | localCap { c => // error | ^ - | Found: (x$0: Capp^'s4) ->'s5 () ->{x$0} Unit - | Required: (c: Capp^) -> () => Unit + |Found: (x$0: Capp^'s4) ->'s5 () ->{x$0} Unit + |Required: (c: Capp^) -> () => Unit | - | Note that capability x$0 is not included in capture set {cap}. + |Note that capability x$0 is not included in capture set {cap} + |because cap, which is existentially bound in () =>² Unit, cannot subsume x$0 since that capability is not a SharedCapability. | - | Note that the existential capture root in () =>² Unit - | cannot subsume the capability (x$0 : Capp^'s4) since that capability is not a `Sharable` capability.. - | - | 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 + |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 | } | diff --git a/tests/neg-custom-args/captures/i15923.check b/tests/neg-custom-args/captures/i15923.check index 6ae4cb76f4ac..44c918ff57be 100644 --- a/tests/neg-custom-args/captures/i15923.check +++ b/tests/neg-custom-args/captures/i15923.check @@ -1,13 +1,14 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15923.scala:12:21 --------------------------------------- 12 | val leak = withCap(cap => mkId(cap)) // error | ^^^^^^^^^^^^^^^^ - |Found: (lcap: scala.caps.Capability^) ?->'s1 Cap^'s2 ->'s3 Id[Cap^'s4]^'s5 - |Required: (lcap: scala.caps.Capability^) ?-> Cap^{lcap} => Id[Cap^'s6]^'s7 + |Capability cap outlives its scope: it leaks into outer capture set 's1 which is owned by value leak. + |The leakage occurred when trying to match the following types: | - |Note that capability cap cannot be included in outer capture set 's6. + |Found: (lcap: scala.caps.Capability^) ?->'s2 Cap^'s3 ->'s4 Id[Cap^'s5]^'s6 + |Required: (lcap: scala.caps.Capability^) ?-> Cap^{lcap} => Id[Cap^'s1]^'s7 | - |where: => refers to a root capability associated with the result type of (using lcap: scala.caps.Capability^): Cap^{lcap} => Id[Cap^'s6]^'s7 + |where: => refers to a root capability associated with the result type of (using lcap: scala.caps.Capability^): Cap^{lcap} => Id[Cap^'s1]^'s7 | ^ refers to the universal root capability - | cap is a root capability associated with the result type of (x$0: Cap^'s2): Id[Cap^'s4]^'s5 + | cap is a root capability associated with the result type of (x$0: Cap^'s3): Id[Cap^'s5]^'s6 | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i15923a.check b/tests/neg-custom-args/captures/i15923a.check index bc2495391625..d21ce5cfd04b 100644 --- a/tests/neg-custom-args/captures/i15923a.check +++ b/tests/neg-custom-args/captures/i15923a.check @@ -1,14 +1,15 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15923a.scala:7:21 --------------------------------------- 7 | val leak = withCap(lcap => () => mkId(lcap)) // error | ^^^^^^^^^^^^^^^^^^^^^^^^ - |Found: (lcap: Cap^'s1) ->'s2 () ->'s3 Id[Cap^'s4]^'s5 - |Required: (lcap: Cap^) => () =>² Id[Cap^'s6]^'s7 + |Capability cap outlives its scope: it leaks into outer capture set 's1 which is owned by value leak. + |The leakage occurred when trying to match the following types: | - |Note that capability cap cannot be included in outer capture set 's6. + |Found: (lcap: Cap^'s2) ->'s3 () ->'s4 Id[Cap^'s5]^'s6 + |Required: (lcap: Cap^) => () =>² Id[Cap^'s1]^'s7 | |where: => refers to a fresh root capability created in value leak when checking argument to parameter op of method withCap - | =>² refers to a root capability associated with the result type of (lcap: Cap^): () =>² Id[Cap^'s6]^'s7 + | =>² refers to a root capability associated with the result type of (lcap: Cap^): () =>² Id[Cap^'s1]^'s7 | ^ refers to the universal root capability - | cap is a root capability associated with the result type of (): Id[Cap^'s4]^'s5 + | cap is a root capability associated with the result type of (): Id[Cap^'s5]^'s6 | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i15923b.check b/tests/neg-custom-args/captures/i15923b.check index 5078b97a66db..ef2e1ed7e16f 100644 --- a/tests/neg-custom-args/captures/i15923b.check +++ b/tests/neg-custom-args/captures/i15923b.check @@ -1,11 +1,12 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15923b.scala:8:21 --------------------------------------- 8 | val leak = withCap(f) // error | ^ + |Capability lcap outlives its scope: it leaks into outer capture set 's1 which is owned by value leak. + |The leakage occurred when trying to match the following types: + | |Found: (x$0: Cap^) -> Id[Cap^{x$0}] |Required: (lcap: Cap^) => Id[Cap^'s1]^'s2 | - |Note that capability lcap cannot be included in outer capture set 's1. - | |where: => refers to a fresh root capability created in value leak when checking argument to parameter op of method withCap | ^ 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 d48897126600..860f80ade5d5 100644 --- a/tests/neg-custom-args/captures/i16226.check +++ b/tests/neg-custom-args/captures/i16226.check @@ -22,10 +22,8 @@ | LazyRef[B^'s19]{val elem: () => B^'s20}^{f1, ref1} |Required: (ref: LazyRef[A]^{io}, f: A =>² B) =>³ LazyRef[B]^ | - |Note that capability f1 is not included in capture set {cap}. - | - |Note that the existential capture root in LazyRef[B]^² - |cannot subsume the capability (f1 : A^'s15 ->'s16 B^'s17) since that capability is not a `Sharable` capability.. + |Note that capability f1 is not included in capture set {cap} + |because cap, which is existentially bound in LazyRef[B]^², cannot subsume f1 since that capability is not a SharedCapability. | |where: => and cap refer to a root capability associated with the result type of (ref1: LazyRef[A^'s11]{val elem: () ->'s12 A^'s13}^'s14, f1: A^'s15 ->'s16 B^'s17): | LazyRef[B^'s19]{val elem: () => B^'s20}^{f1, ref1} diff --git a/tests/neg-custom-args/captures/i21920.check b/tests/neg-custom-args/captures/i21920.check index c9b1aa276d93..6479386622fa 100644 --- a/tests/neg-custom-args/captures/i21920.check +++ b/tests/neg-custom-args/captures/i21920.check @@ -1,13 +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^'s1) ->'s2 Cell[File^'s3]{val head: () ->'s4 IterableOnce[File^'s5]^'s6}^'s7 - |Required: File^ => Cell[File^'s8]{val head: () ->'s9 IterableOnce[File^'s10]^'s11}^'s12 + |Capability cap outlives its scope: it leaks into outer capture set 's1 which is owned by an enclosing function. + |The leakage occurred when trying to match the following types: | - |Note that capability cap cannot be included in outer capture set 's13. + |Found: (f: File^'s2) ->'s3 Cell[File^'s4]{val head: () ->'s5 IterableOnce[File^'s6]^'s7}^'s8 + |Required: File^ => Cell[File^'s9]{val head: () ->'s10 IterableOnce[File^'s11]^'s12}^'s13 | |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 - | cap is a root capability associated with the result type of (): IterableOnce[File^'s13]^² + | cap is a root capability associated with the result type of (): IterableOnce[File^'s1]^² | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i23431.check b/tests/neg-custom-args/captures/i23431.check index 5bbcb2b720f0..3c8f09cdc1b9 100644 --- a/tests/neg-custom-args/captures/i23431.check +++ b/tests/neg-custom-args/captures/i23431.check @@ -4,11 +4,11 @@ | Found: (io : IO^) | Required: IO^² | - | Note that capability cap is not included in capture set {cap²} - | because cap in method setIO is not visible from cap² in variable myIO. + | Note that capability io is not included in capture set {cap} + | because (io : IO^) in method setIO is not visible from cap in variable myIO. | - | where: ^ and cap refer to a fresh root capability in the type of parameter io - | ^² and cap² refer to a fresh root capability in the type of variable myIO + | where: ^ refers to a fresh root capability in the type of parameter io + | ^² and cap refer to a fresh root capability in the type of variable myIO | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i23431.scala:11:13 --------------------------------------- @@ -17,11 +17,11 @@ | Found: (io2 : IO^) | Required: IO^² | - | Note that capability cap is not included in capture set {cap²} - | because cap in enclosing function is not visible from cap² in variable myIO. + | Note that capability io2 is not included in capture set {cap} + | because (io2 : IO^) in an enclosing function is not visible from cap in variable myIO. | - | where: ^ and cap refer to a fresh root capability in the type of parameter io2 - | ^² and cap² refer to a fresh root capability in the type of variable myIO + | where: ^ refers to a fresh root capability in the type of parameter io2 + | ^² and cap refer to a fresh root capability in the type of variable myIO | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i23431.scala:12:12 --------------------------------------- @@ -31,7 +31,7 @@ |Required: IO^ => Unit | |Note that capability cap is not included in capture set {cap²} - |because cap in enclosing function is not visible from cap² in variable myIO. + |because cap in an enclosing function is not visible from cap² in variable myIO. | |where: => refers to a fresh root capability created in anonymous function of type (io1: IO^): Unit when checking argument to parameter op of method withIO | ^ refers to the universal root capability diff --git a/tests/neg-custom-args/captures/i24137.check b/tests/neg-custom-args/captures/i24137.check index 3f7d14144c32..60919d7b7591 100644 --- a/tests/neg-custom-args/captures/i24137.check +++ b/tests/neg-custom-args/captures/i24137.check @@ -4,7 +4,7 @@ | Found: (b : B{val elem1: A^{a}; val elem2: A^{a}}^{cap, a}) | Required: B^{async} | - | Note that capability cap is not included in capture set {async}. + | Note that capability b is not included in capture set {async}. | | where: cap is a fresh root capability in the type of value b | diff --git a/tests/neg-custom-args/captures/lazylists-exceptions.check b/tests/neg-custom-args/captures/lazylists-exceptions.check index 0f31ab58cca6..c0ca2bd3b258 100644 --- a/tests/neg-custom-args/captures/lazylists-exceptions.check +++ b/tests/neg-custom-args/captures/lazylists-exceptions.check @@ -1,11 +1,12 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylists-exceptions.scala:37:17 ------------------------- 37 | tabulate(10) { i => // error | ^ + | Capability canThrow$1 defined in an enclosing try expression outlives its scope: + | it leaks into outer capture set 's1 of method problem. + | The leakage occurred when trying to match the following types: + | | Found: LazyList[Int]^{canThrow$1} | Required: LazyList[Int]^'s1 - | - | Note that capability canThrow$1, defined in method try$1 - | cannot be included in outer capture set 's1 of method problem. 38 | if i > 9 then throw Ex1() 39 | i * i 40 | } diff --git a/tests/neg-custom-args/captures/leaking-iterators.check b/tests/neg-custom-args/captures/leaking-iterators.check index b3b8e36fef79..f867fc5d4a79 100644 --- a/tests/neg-custom-args/captures/leaking-iterators.check +++ b/tests/neg-custom-args/captures/leaking-iterators.check @@ -1,10 +1,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/leaking-iterators.scala:56:16 ---------------------------- 56 | usingLogFile: log => // error | ^ - |Found: (log: java.io.FileOutputStream^'s1) ->'s2 cctest.Iterator[Int]^{log} - |Required: java.io.FileOutputStream^ => cctest.Iterator[Int]^'s3 + |Capability log outlives its scope: it leaks into outer capture set 's1 which is owned by method test. + |The leakage occurred when trying to match the following types: | - |Note that capability log cannot be included in outer capture set 's3. + |Found: (log: java.io.FileOutputStream^'s2) ->'s3 cctest.Iterator[Int]^{log} + |Required: java.io.FileOutputStream^ => cctest.Iterator[Int]^'s1 | |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 diff --git a/tests/neg-custom-args/captures/outer-var.check b/tests/neg-custom-args/captures/outer-var.check index 45489ab0ff5b..ae9f6f3619a0 100644 --- a/tests/neg-custom-args/captures/outer-var.check +++ b/tests/neg-custom-args/captures/outer-var.check @@ -4,11 +4,11 @@ | Found: (q : () => Unit) | Required: () ->{p, q²} Unit | - | Note that capability cap is not included in capture set {p, q²}. + | Note that capability q is not included in capture set {p, q²}. | - | where: => and cap refer 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 + | 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 | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/outer-var.scala:13:9 ------------------------------------- @@ -39,9 +39,9 @@ | Found: (q : () => Unit) | Required: () ->{p} Unit | - | Note that capability cap cannot be included in capture set {p} of variable y. + | Note that capability q cannot be included in capture set {p} of variable y. | - | where: => and cap refer to a fresh root capability in the type of parameter q + | 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 e20a318064bb..8c099c520d60 100644 --- a/tests/neg-custom-args/captures/reaches.check +++ b/tests/neg-custom-args/captures/reaches.check @@ -124,7 +124,7 @@ -- Error: tests/neg-custom-args/captures/reaches.scala:60:36 ----------------------------------------------------------- 60 | val leaked = usingFile[File^{id*}]: f => // error: separation | ^ - | Local cap created in type of parameter x leaks into capture scope of enclosing function + | 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) diff --git a/tests/neg-custom-args/captures/real-try.check b/tests/neg-custom-args/captures/real-try.check index 684dcb7c982d..7f574644f7ca 100644 --- a/tests/neg-custom-args/captures/real-try.check +++ b/tests/neg-custom-args/captures/real-try.check @@ -19,30 +19,33 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/real-try.scala:21:4 -------------------------------------- 21 | () => foo(1) // error | ^^^^^^^^^^^^ + | Capability canThrow$2 defined in an enclosing try expression outlives its scope: + | it leaks into outer capture set 's1 of value x. + | The leakage occurred when trying to match the following types: + | | Found: () ->{canThrow$2} Unit | Required: () ->'s1 Unit | - | Note that capability canThrow$2, defined in method try$2 - | cannot be included in outer capture set 's1 of value x. - | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/real-try.scala:27:4 -------------------------------------- 27 | () => Cell(foo(1)) // error | ^^^^^^^^^^^^^^^^^^ - | Found: () ->{canThrow$3} Cell[Unit]^'s2 - | Required: () ->'s3 Cell[Unit]^'s4 + | Capability canThrow$3 defined in an enclosing try expression outlives its scope: + | it leaks into outer capture set 's2 of value y. + | The leakage occurred when trying to match the following types: | - | Note that capability canThrow$3, defined in method try$3 - | cannot be included in outer capture set 's3 of value y. + | Found: () ->{canThrow$3} Cell[Unit]^'s3 + | Required: () ->'s2 Cell[Unit]^'s4 | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/real-try.scala:33:8 -------------------------------------- 33 | Cell(() => foo(1)) // error | ^^^^^^^^^^^^^^^^^^ + | Capability canThrow$4 defined in an enclosing try expression outlives its scope: + | it leaks into outer capture set 's5 of value b. + | The leakage occurred when trying to match the following types: + | | Found: Cell[() ->{canThrow$4} Unit] | Required: Cell[() ->'s5 Unit]^'s6 | - | Note that capability canThrow$4, defined in method try$4 - | cannot be included in outer capture set 's5 of value b. - | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/reference-cc.check b/tests/neg-custom-args/captures/reference-cc.check index 476cb2a09ceb..df4567a5c173 100644 --- a/tests/neg-custom-args/captures/reference-cc.check +++ b/tests/neg-custom-args/captures/reference-cc.check @@ -1,10 +1,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reference-cc.scala:44:29 --------------------------------- 44 | val later = usingLogFile { file => () => file.write(0) } // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |Found: (file: java.io.FileOutputStream^'s1) ->'s2 () ->{file} Unit - |Required: java.io.FileOutputStream^ => () ->'s3 Unit + |Capability file outlives its scope: it leaks into outer capture set 's1 which is owned by value later. + |The leakage occurred when trying to match the following types: | - |Note that capability file cannot be included in outer capture set 's3. + |Found: (file: java.io.FileOutputStream^'s2) ->'s3 () ->{file} Unit + |Required: java.io.FileOutputStream^ => () ->'s1 Unit | |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 @@ -13,10 +14,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reference-cc.scala:47:25 --------------------------------- 47 | val xs = usingLogFile: f => // error | ^ - |Found: (f: java.io.FileOutputStream^'s4) ->'s5 LzyList[Int]^{f} - |Required: java.io.FileOutputStream^ => LzyList[Int]^'s6 + |Capability f outlives its scope: it leaks into outer capture set 's4 which is owned by value xs. + |The leakage occurred when trying to match the following types: | - |Note that capability f cannot be included in outer capture set 's6. + |Found: (f: java.io.FileOutputStream^'s5) ->'s6 LzyList[Int]^{f} + |Required: java.io.FileOutputStream^ => LzyList[Int]^'s4 | |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 @@ -30,7 +32,7 @@ |Required: () => Double | |Note that capability canThrow$1 is not included in capture set {cap} - |because (canThrow$1 : CanThrow[LimitExceeded]) in method try$1 is not visible from cap in enclosing function. + |because (canThrow$1 : CanThrow[LimitExceeded]) in an enclosing try expression is not visible from cap in an enclosing function. | |where: => and cap refer to a fresh root capability created in anonymous function of type (using erased x$1: CanThrow[LimitExceeded]): () => Double when instantiating expected result type () ->{cap²} Double of function literal | diff --git a/tests/neg-custom-args/captures/scope-extrude-mut.check b/tests/neg-custom-args/captures/scope-extrude-mut.check index 5f1671b19a47..905becd123e4 100644 --- a/tests/neg-custom-args/captures/scope-extrude-mut.check +++ b/tests/neg-custom-args/captures/scope-extrude-mut.check @@ -4,10 +4,10 @@ | Found: (a1 : A^) | Required: A^² | - | Note that capability cap is not included in capture set {cap²} - | because cap in method b is not visible from cap² in variable a. + | Note that capability a1 is not included in capture set {cap} + | because (a1 : A^) in method b is not visible from cap in variable a. | - | where: ^ and cap refer to a fresh root capability classified as Mutable in the type of value a1 - | ^² and cap² refer to a fresh root capability classified as Mutable in the type of variable a + | where: ^ refers to a fresh root capability classified as Mutable in the type of value a1 + | ^² and cap refer to a fresh root capability classified as Mutable in the type of variable a | | 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 new file mode 100644 index 000000000000..e20aa8995047 --- /dev/null +++ b/tests/neg-custom-args/captures/scope-extrusions.check @@ -0,0 +1,163 @@ +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scope-extrusions.scala:9:8 ------------------------------- +9 | v = x // error + | ^ + | Found: (x : IO) + | Required: IO^ + | + | Note that capability x is not included in capture set {cap} + | because (x : IO) in method f1 is not visible from cap in variable v. + | + | where: ^ and cap refer to a fresh root capability classified as SharedCapability in the type of variable v + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scope-extrusions.scala:10:8 ------------------------------ +10 | w = g // error + | ^ + | Found: () ->{x} Unit + | Required: () => Unit + | + | Note that capability x is not included in capture set {cap} + | because (x : IO) in method f1 is not visible from cap in variable w. + | + | where: => and cap refer to a fresh root capability in the type of variable w + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scope-extrusions.scala:19:11 ----------------------------- +19 | withFile(io => io) // error + | ^^^^^^^^ + |Capability io outlives its scope: it leaks into outer capture set 's1 which is owned by method test. + |The leakage occurred when trying to match the following types: + | + |Found: (io: IO^'s2) ->'s3 IO^{io} + |Required: IO^ => IO^'s1 + | + |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 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scope-extrusions.scala:20:11 ----------------------------- +20 | withFile(id) // error + | ^^ + |Capability x outlives its scope: it leaks into outer capture set 's4 which is owned by method test. + |The leakage occurred when trying to match the following types: + | + |Found: (x: IO^) ->'s5 IO^{x} + |Required: IO^ => IO^'s4 + | + |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 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scope-extrusions.scala:21:11 ----------------------------- +21 | withFile(x => id(x)) // error + | ^^^^^^^^^^ + |Capability x outlives its scope: it leaks into outer capture set 's6 which is owned by method test. + |The leakage occurred when trying to match the following types: + | + |Found: (x: IO^'s7) ->'s8 IO^{x} + |Required: IO^ => IO^'s6 + | + |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 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scope-extrusions.scala:22:11 ----------------------------- +22 | withFile(id2) // error, note mentions cap since we never have a more specific include failure + | ^^^ + |Capability cap outlives its scope: it leaks into outer capture set 's9 which is owned by method test. + |The leakage occurred when trying to match the following types: + | + |Found: (x: IO^) ->'s10 IO^² + |Required: IO^ => IO^'s9 + | + |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 + | ^² refers to a root capability associated with the result type of (x: IO^): IO^² + | cap is a root capability associated with the result type of (x: IO^): IO^'s9 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scope-extrusions.scala:23:11 ----------------------------- +23 | withFile(x => id2(x)) // error, note mentions cap since we never have a more specific include failure + | ^^^^^^^^^^^ + |Capability cap outlives its scope: it leaks into outer capture set 's11 which is owned by method test. + |The leakage occurred when trying to match the following types: + | + |Found: (x: IO^'s12) ->'s13 IO^ + |Required: IO^² => IO^'s11 + | + |where: => refers to a fresh root capability created in method test when checking argument to parameter op of method withFile + | ^ refers to a root capability associated with the result type of (x: IO^'s12): IO^ + | ^² refers to the universal root capability + | cap is a root capability associated with the result type of (x: IO^²): IO^'s11 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scope-extrusions.scala:24:11 ----------------------------- +24 | withFile(identity) // error, note mentions cap since we never have a more specific include failure + | ^^^^^^^^ + |Capability cap outlives its scope: it leaks into outer capture set 's14 which is owned by method test. + |The leakage occurred when trying to match the following types: + | + |Found: (x: IO^'s15) ->'s16 IO^'s17 + |Required: IO^ => IO^'s14 + | + |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 root capability associated with the result type of (x: IO^): IO^'s14 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scope-extrusions.scala:25:11 ----------------------------- +25 | withFile(x => identity(x)) // error, note mentions cap since we never have a more specific include failure + | ^^^^^^^^^^^^^^^^ + |Capability cap outlives its scope: it leaks into outer capture set 's18 which is owned by method test. + |The leakage occurred when trying to match the following types: + | + |Found: (x: IO^'s19) ->'s20 IO^'s21 + |Required: IO^ => IO^'s18 + | + |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 root capability associated with the result type of (x: IO^): IO^'s18 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scope-extrusions.scala:27:12 ----------------------------- +27 | withFile: io => // error + | ^ + |Capability io outlives its scope: it leaks into outer capture set 's22 which is owned by method test. + |The leakage occurred when trying to match the following types: + | + |Found: (io: IO^'s23) ->'s24 () ->{io} Unit + |Required: IO^ => () ->'s22 Unit + | + |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 +28 | () => println(io) + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scope-extrusions.scala:30:21 ----------------------------- +30 | val f2: IO => IO = (x: IO) => x // error + | ^^^^^^^^^^^^ + | Found: (x: IO^) ->'s25 IO^{x} + | Required: IO^ => IO^² + | + | Note that capability x is not included in capture set {cap} + | because (x : IO) is not visible from cap in value f2. + | + | where: => refers to a fresh root capability in the type of value f2 + | ^ refers to the universal root capability + | ^² and cap refer to a fresh root capability classified as SharedCapability in the type of value f2 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scope-extrusions.scala:32:21 ----------------------------- +32 | val f4: IO => IO = f3 // error + | ^^ + | Found: (f3 : (x$0: IO) ->{} IO^{x$0}) + | Required: IO^ => IO^² + | + | Note that capability x$0 is not included in capture set {cap} + | because (x$0 : IO) is not visible from cap in value f4. + | + | where: => refers to a fresh root capability in the type of value f4 + | ^ refers to the universal root capability + | ^² and cap refer to a fresh root capability classified as SharedCapability in the type of value f4 + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/scope-extrusions.scala b/tests/neg-custom-args/captures/scope-extrusions.scala new file mode 100644 index 000000000000..277a8df6cfbb --- /dev/null +++ b/tests/neg-custom-args/captures/scope-extrusions.scala @@ -0,0 +1,33 @@ +// Tests quality of error messages +class IO extends caps.SharedCapability + +def test(io: IO): Unit = + var v: IO = io + var w: () => Unit = () => () + def f1(x: IO) = + def g() = println(x) + v = x // error + w = g // error + + def withFile[T](op: IO => T): T = + val io = IO() + op(io) + + def id(x: IO): x.type = x + def id2(x: IO): IO = x + + withFile(io => io) // error + withFile(id) // error + withFile(x => id(x)) // error + withFile(id2) // error, note mentions cap since we never have a more specific include failure + withFile(x => id2(x)) // error, note mentions cap since we never have a more specific include failure + withFile(identity) // error, note mentions cap since we never have a more specific include failure + withFile(x => identity(x)) // error, note mentions cap since we never have a more specific include failure + + withFile: io => // error + () => println(io) + + val f2: IO => IO = (x: IO) => x // error + val f3 = (x: IO) => x + val f4: IO => IO = f3 // error + diff --git a/tests/neg-custom-args/captures/scoped-caps.check b/tests/neg-custom-args/captures/scoped-caps.check index 4c738997c213..d01b55ad0747 100644 --- a/tests/neg-custom-args/captures/scoped-caps.check +++ b/tests/neg-custom-args/captures/scoped-caps.check @@ -85,7 +85,7 @@ |Required: B^² | |Note that capability cap is not included in capture set {cap²} - |because cap in enclosing function is not visible from cap² in value _$14. + |because cap in an enclosing function is not visible from cap² in value _$14. | |where: ^ and cap refer to a fresh root capability created in anonymous function of type (x: S): B^³ when instantiating method apply's type (x: S^³): B^⁴ | ^² and cap² refer to a fresh root capability in the type of value _$14 diff --git a/tests/neg-custom-args/captures/scoped-caps2.check b/tests/neg-custom-args/captures/scoped-caps2.check index 21867171cb6b..cd739e80bc20 100644 --- a/tests/neg-custom-args/captures/scoped-caps2.check +++ b/tests/neg-custom-args/captures/scoped-caps2.check @@ -61,7 +61,7 @@ |Required: C^² | |Note that capability cap is not included in capture set {cap²} - |because cap in enclosing function is not visible from cap² in value _$8. + |because cap in an enclosing function is not visible from cap² in value _$8. | |where: ^ and cap refer to a fresh root capability classified as SharedCapability created in anonymous function of type (x: C): C when instantiating method apply's type (x: C^³): C^⁴ | ^² and cap² refer to a fresh root capability classified as SharedCapability in the type of value _$8 diff --git a/tests/neg-custom-args/captures/sep-curried-par.check b/tests/neg-custom-args/captures/sep-curried-par.check index 04c052f9c7cc..746b5d041338 100644 --- a/tests/neg-custom-args/captures/sep-curried-par.check +++ b/tests/neg-custom-args/captures/sep-curried-par.check @@ -1,11 +1,12 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/sep-curried-par.scala:23:32 ------------------------------ 23 | val bar = (p1: () => Unit) => (p2: () ->{p1, cap} Unit) => par(p1, p2) // error, but error message could be better | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (p2: () ->{p1, cap} Unit) ->{p1} Unit - | Required: (() ->'s1 Unit) ->'s2 Unit + | Capability p1 defined in an enclosing function outlives its scope: + | it leaks into outer capture set 's1 which is owned by value bar. + | The leakage occurred when trying to match the following types: | - | Note that capability p1, defined in method $anonfun - | cannot be included in outer capture set 's2. + | Found: (p2: () ->{p1, cap} Unit) ->{p1} Unit + | Required: (() ->'s2 Unit) ->'s1 Unit | | where: cap is 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 54aa8a464ae4..84206a8f78ce 100644 --- a/tests/neg-custom-args/captures/simple-using.check +++ b/tests/neg-custom-args/captures/simple-using.check @@ -1,10 +1,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/simple-using.scala:8:17 ---------------------------------- 8 | usingLogFile { f => () => f.write(2) } // error | ^^^^^^^^^^^^^^^^^^^^^ - |Found: (f: java.io.FileOutputStream^'s1) ->'s2 () ->{f} Unit - |Required: java.io.FileOutputStream^ => () ->'s3 Unit + |Capability f outlives its scope: it leaks into outer capture set 's1 which is owned by method test. + |The leakage occurred when trying to match the following types: | - |Note that capability f cannot be included in outer capture set 's3. + |Found: (f: java.io.FileOutputStream^'s2) ->'s3 () ->{f} Unit + |Required: java.io.FileOutputStream^ => () ->'s1 Unit | |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 diff --git a/tests/neg-custom-args/captures/try.check b/tests/neg-custom-args/captures/try.check index aa0137e1a08a..33b69e0e3fd6 100644 --- a/tests/neg-custom-args/captures/try.check +++ b/tests/neg-custom-args/captures/try.check @@ -34,10 +34,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:36:4 ------------------------------------------- 36 | (x: CanThrow[Exception]) => // error | ^ - |Found: (x: CT[Exception]^) ->'s3 () ->{x} Int - |Required: CT[Exception]^ => () ->'s4 Int + |Capability x outlives its scope: it leaks into outer capture set 's3 which is owned by value xx. + |The leakage occurred when trying to match the following types: | - |Note that capability x cannot be included in outer capture set 's4. + |Found: (x: CT[Exception]^) ->'s4 () ->{x} Int + |Required: CT[Exception]^ => () ->'s3 Int | |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 @@ -49,10 +50,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:48:2 ------------------------------------------- 48 | (x: CanThrow[Exception]) => // error | ^ - |Found: (x: CT[Exception]^) ->'s5 () ->{x} Int - |Required: CT[Exception]^ => () ->'s6 Int + |Capability x outlives its scope: it leaks into outer capture set 's5 which is owned by value global. + |The leakage occurred when trying to match the following types: | - |Note that capability x cannot be included in outer capture set 's6. + |Found: (x: CT[Exception]^) ->'s6 () ->{x} Int + |Required: CT[Exception]^ => () ->'s5 Int | |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 diff --git a/tests/neg-custom-args/captures/usingLogFile.check b/tests/neg-custom-args/captures/usingLogFile.check index b4ef40afd587..51cbe5b334d1 100644 --- a/tests/neg-custom-args/captures/usingLogFile.check +++ b/tests/neg-custom-args/captures/usingLogFile.check @@ -1,10 +1,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/usingLogFile.scala:22:29 --------------------------------- 22 | val later = usingLogFile { f => () => f.write(0) } // error | ^^^^^^^^^^^^^^^^^^^^^ - |Found: (f: java.io.FileOutputStream^'s1) ->'s2 () ->{f} Unit - |Required: java.io.FileOutputStream^ => () ->'s3 Unit + |Capability f outlives its scope: it leaks into outer capture set 's1 which is owned by value later. + |The leakage occurred when trying to match the following types: | - |Note that capability f cannot be included in outer capture set 's3. + |Found: (f: java.io.FileOutputStream^'s2) ->'s3 () ->{f} Unit + |Required: java.io.FileOutputStream^ => () ->'s1 Unit | |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 @@ -13,10 +14,11 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/usingLogFile.scala:27:38 --------------------------------- 27 | private val later2 = usingLogFile { f => Cell(() => f.write(0)) } // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |Found: (f: java.io.FileOutputStream^'s4) ->'s5 Test2.Cell[() ->{f} Unit]^'s6 - |Required: java.io.FileOutputStream^ => Test2.Cell[() ->'s7 Unit]^'s8 + |Capability f outlives its scope: it leaks into outer capture set 's4 which is owned by value later2. + |The leakage occurred when trying to match the following types: | - |Note that capability f cannot be included in outer capture set 's7. + |Found: (f: java.io.FileOutputStream^'s5) ->'s6 Test2.Cell[() ->{f} Unit]^'s7 + |Required: java.io.FileOutputStream^ => Test2.Cell[() ->'s4 Unit]^'s8 | |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 @@ -25,10 +27,11 @@ -- [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^'s9) ->'s10 Int ->{f} Unit - |Required: java.io.OutputStream^ => Int ->'s11 Unit + |Capability f outlives its scope: it leaks into outer capture set 's9 which is owned by value later. + |The leakage occurred when trying to match the following types: | - |Note that capability f cannot be included in outer capture set 's11. + |Found: (f: java.io.OutputStream^'s10) ->'s11 Int ->{f} Unit + |Required: java.io.OutputStream^ => Int ->'s9 Unit | |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 @@ -37,10 +40,11 @@ -- [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^'s12) ->'s13 () ->{_$1} Unit - |Required: java.io.OutputStream^ => () ->'s14 Unit + |Capability _$1 outlives its scope: it leaks into outer capture set 's12 which is owned by value later. + |The leakage occurred when trying to match the following types: | - |Note that capability _$1 cannot be included in outer capture set 's14. + |Found: (_$1: java.io.OutputStream^'s13) ->'s14 () ->{_$1} Unit + |Required: java.io.OutputStream^ => () ->'s12 Unit | |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 diff --git a/tests/neg-custom-args/captures/vars.check b/tests/neg-custom-args/captures/vars.check index c992228ca26d..58a6708dc869 100644 --- a/tests/neg-custom-args/captures/vars.check +++ b/tests/neg-custom-args/captures/vars.check @@ -28,14 +28,15 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/vars.scala:36:10 ----------------------------------------- 36 | local { cap3 => // error | ^ - | Found: (cap3: CC^) ->'s1 String ->{cap3} String - | Required: CC^ -> String ->{cap3²} String + | Capability cap3 outlives its scope: it leaks into outer capture set {cap3²} which is owned by method test. + | The leakage occurred when trying to match the following types: | - | Note that capability cap3 cannot be included in outer capture set {cap3²}. + | Found: (cap3: CC^) ->'s1 String ->{cap3} String + | Required: CC^ -> String ->{cap3²} String | - | 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 + | 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 |