diff --git a/.github/workflows/release-maven-artifacts.yml b/.github/workflows/release-maven-artifacts.yml new file mode 100644 index 000000000000..dd774602d262 --- /dev/null +++ b/.github/workflows/release-maven-artifacts.yml @@ -0,0 +1,31 @@ +name: Release Artifacts to Maven + +on: + workflow_call: + inputs: + environment: + required: true + type: string + +jobs: + release-maven-artifacts: + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-java@v4 + with: + distribution: 'adopt' + java-version: 17 + cache: 'sbt' + - uses: sbt/setup-sbt@v1 + - name: Publish Artifacts to the Maven Repository + run : sbt scala3-bootstrapped-new/publish + env: + MAVEN_REPOSITORY_USER : ${{ secrets.MAVEN_REPOSITORY_USER }} + MAVEN_REPOSITORY_TOKEN: ${{ secrets.MAVEN_REPOSITORY_TOKEN }} + MAVEN_REPOSITORY_HOST : ${{ vars.MAVEN_REPOSITORY_HOST }} + MAVEN_REPOSITORY_REALM: ${{ vars.MAVEN_REPOSITORY_REALM }} + MAVEN_REPOSITORY_URL : ${{ vars.MAVEN_REPOSITORY_URL }} + NEWNIGHTLY : ${{ vars.NEWNIGHTLY }} + NIGHTLYBUILD : ${{ vars.NIGHTLYBUILD }} diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml new file mode 100644 index 000000000000..5e8dc2af0534 --- /dev/null +++ b/.github/workflows/release-nightly.yml @@ -0,0 +1,20 @@ +name: Nightly Release of Scala 3 +run-name: Nightly Release of Scala 3 (${{ github.sha }}) + +on: + workflow_dispatch: + schedule: + - cron: '0 3 * * *' # Every day at 3 AM + +jobs: + + ## TODO: Add the compilation pipeline here + + ## TODO: Add the test pipeline here + + release-maven-artifacts: + # TODO: Add a dependency to the test ad compilation pipeline before releasing + uses: ./.github/workflows/release-maven-artifacts.yml + with: + environment: release-nightly + secrets: inherit diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 96c8c4c4f845..76a584ef146a 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -559,6 +559,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def makeOnlyAnnot(qid: Tree)(using Context) = New(AppliedTypeTree(ref(defn.OnlyCapabilityAnnot.typeRef), qid :: Nil), Nil :: Nil) + def makeConsumeAnnot()(using Context): Tree = + New(ref(defn.ConsumeAnnot.typeRef), Nil :: Nil) + def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(using Context): DefDef = DefDef(nme.CONSTRUCTOR, joinParams(tparams, vparamss), TypeTree(), rhs) diff --git a/compiler/src/dotty/tools/dotc/cc/SepCheck.scala b/compiler/src/dotty/tools/dotc/cc/SepCheck.scala index f5589caf3872..a3f3d40a42f6 100644 --- a/compiler/src/dotty/tools/dotc/cc/SepCheck.scala +++ b/compiler/src/dotty/tools/dotc/cc/SepCheck.scala @@ -22,9 +22,9 @@ import Capabilities.* * * - Hidden sets of arguments must not be referred to in the same application * - Hidden sets of (result-) types must not be referred to alter in the same scope. - * - Returned hidden sets can only refer to @consume parameters. + * - Returned hidden sets can only refer to consume parameters. * - If returned hidden sets refer to an encloding this, the reference must be - * from a @consume method. + * from a consume method. * - Consumed entities cannot be used subsequently. * - Entitites cannot be consumed in a loop. */ @@ -422,7 +422,7 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: def consumeError(ref: Capability, loc: SrcPos, pos: SrcPos)(using Context): Unit = report.error( em"""Separation failure: Illegal access to $ref, which was passed to a - |@consume parameter or was used as a prefix to a @consume method on line ${loc.line + 1} + |consume parameter or was used as a prefix to a consume method on line ${loc.line + 1} |and therefore is no longer available.""", pos) @@ -433,7 +433,7 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: def consumeInLoopError(ref: Capability, pos: SrcPos)(using Context): Unit = report.error( em"""Separation failure: $ref appears in a loop, therefore it cannot - |be passed to a @consume parameter or be used as a prefix of a @consume method call.""", + |be passed to a consume parameter or be used as a prefix of a consume method call.""", pos) // ------------ Checks ----------------------------------------------------- @@ -587,16 +587,16 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: /** Check validity of consumed references `refsToCheck`. The references are consumed * because they are hidden in a Fresh result type or they are referred - * to in an argument to a @consume parameter or in a prefix of a @consume method -- + * to in an argument to a consume parameter or in a prefix of a consume method -- * which one applies is determined by the role parameter. * * This entails the following checks: * - The reference must be defined in the same as method or class as * the access. * - If the reference is to a term parameter, that parameter must be - * marked as @consume as well. + * marked as consume as well. * - If the reference is to a this type of the enclosing class, the - * access must be in a @consume method. + * access must be in a consume method. * * References that extend caps.Sharable are excluded from checking. * As a side effect, add all checked references with the given position `pos` @@ -631,7 +631,7 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: then report.error( em"""Separation failure: $descr non-local this of class ${ref.cls}. - |The access must be in a @consume method to allow this.""", + |The access must be in a consume method to allow this.""", pos) case _ => @@ -643,7 +643,7 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: val (pluralS, singleS) = if badParams.tail.isEmpty then ("", "s") else ("s", "") report.error( em"""Separation failure: $descr parameter$pluralS ${paramsStr(badParams.toList)}. - |The parameter$pluralS need$singleS to be annotated with @consume to allow this.""", + |The parameter$pluralS need$singleS to be annotated with consume to allow this.""", pos) role match @@ -769,7 +769,7 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: /** If `tpe` appears as a (result-) type of a definition, treat its * hidden set minus its explicitly declared footprint as consumed. - * If `tpe` appears as an argument to a @consume parameter, treat + * If `tpe` appears as an argument to a consume parameter, treat * its footprint as consumed. */ def checkLegalRefs() = role match @@ -786,7 +786,7 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: case TypeRole.Argument(arg) => if tpe.hasAnnotation(defn.ConsumeAnnot) then val capts = captures(arg).footprint - checkConsumedRefs(capts, tpe, role, i"argument to @consume parameter with type ${arg.nuType} refers to", pos) + checkConsumedRefs(capts, tpe, role, i"argument to consume parameter with type ${arg.nuType} refers to", pos) case _ => if !tpe.hasAnnotation(defn.UntrackedCapturesAnnot) then @@ -910,7 +910,7 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: checkConsumedRefs( captures(qual).footprint, qual.nuType, TypeRole.Qualifier(qual, tree.symbol), - i"call prefix of @consume ${tree.symbol} refers to", qual.srcPos) + i"call prefix of consume ${tree.symbol} refers to", qual.srcPos) case tree: GenericApply => traverseChildren(tree) tree.tpe match diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index ec312631a61b..aa9b9f399634 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -758,8 +758,8 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: && !(sym.is(Method) && sym.owner.isClass) then report.error( - em"""@consume cannot be used here. Only member methods and their term parameters - |can have @consume annotations.""", + em"""consume cannot be used here. Only member methods and their term parameters + |can have a consume modifier.""", tree.srcPos) else if annotCls == defn.UseAnnot then if !ccConfig.allowUse then diff --git a/compiler/src/dotty/tools/dotc/cc/Synthetics.scala b/compiler/src/dotty/tools/dotc/cc/Synthetics.scala index bb2228932cb8..a32cb76fc127 100644 --- a/compiler/src/dotty/tools/dotc/cc/Synthetics.scala +++ b/compiler/src/dotty/tools/dotc/cc/Synthetics.scala @@ -168,4 +168,4 @@ object Synthetics: transformCompareCaptures) end transform -end Synthetics \ No newline at end of file +end Synthetics diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 5102f7021825..02c6d1d66023 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1076,8 +1076,8 @@ class Definitions { @tu lazy val UncheckedCapturesAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedCaptures") @tu lazy val UntrackedCapturesAnnot: ClassSymbol = requiredClass("scala.caps.unsafe.untrackedCaptures") @tu lazy val UseAnnot: ClassSymbol = requiredClass("scala.caps.use") - @tu lazy val ConsumeAnnot: ClassSymbol = requiredClass("scala.caps.consume") @tu lazy val ReserveAnnot: ClassSymbol = requiredClass("scala.caps.reserve") + @tu lazy val ConsumeAnnot: ClassSymbol = requiredClass("scala.caps.internal.consume") @tu lazy val RefineOverrideAnnot: ClassSymbol = requiredClass("scala.caps.internal.refineOverride") @tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile") @tu lazy val LanguageFeatureMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.languageFeature") diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 0bde164186b7..18873dfa83af 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -454,6 +454,7 @@ object StdNames { val compiletime : N = "compiletime" val compose: N = "compose" val conforms_ : N = "$conforms" + val consume: N = "consume" val contents: N = "contents" val copy: N = "copy" val create: N = "create" @@ -530,6 +531,7 @@ object StdNames { val isEmpty: N = "isEmpty" val isInstanceOf_ : N = "isInstanceOf" val isInstanceOfPM: N = "$isInstanceOf$" + val isSameLabelAs : N = "isSameLabelAs" val java: N = "java" val key: N = "key" val label: N = "label" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index f6dd3c2396d4..530a2c29a94c 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -35,6 +35,7 @@ import config.SourceVersion.* import config.SourceVersion import dotty.tools.dotc.config.MigrationVersion import dotty.tools.dotc.util.chaining.* +import dotty.tools.dotc.config.Feature.ccEnabled object Parsers { @@ -216,6 +217,8 @@ object Parsers { def isPureArrow: Boolean = isPureArrow(nme.PUREARROW) || isPureArrow(nme.PURECTXARROW) def isErased = isIdent(nme.erased) && in.erasedEnabled && in.isSoftModifierInParamModifierPosition + def isConsume = + isIdent(nme.consume) && ccEnabled //\&& in.isSoftModifierInParamModifierPosition def isSimpleLiteral = simpleLiteralTokens.contains(in.token) || isIdent(nme.raw.MINUS) && numericLitTokens.contains(in.lookahead.token) @@ -3325,11 +3328,15 @@ object Parsers { private def addModifier(mods: Modifiers): Modifiers = { val tok = in.token val name = in.name - val mod = atSpan(in.skipToken()) { modOfToken(tok, name) } - - if mods.isOneOf(mod.flags) then - syntaxError(RepeatedModifier(mod.flags.flagsString, source, mod.span), mod.span) - addMod(mods, mod) + if isConsume then + val consumeAnnot = atSpan(in.skipToken())(makeConsumeAnnot()) + mods.withAddedAnnotation(consumeAnnot) + else + val mod = atSpan(in.skipToken()): + modOfToken(tok, name) + if mods.isOneOf(mod.flags) then + syntaxError(RepeatedModifier(mod.flags.flagsString, source, mod.span), mod.span) + addMod(mods, mod) } def addFlag(mods: Modifiers, flag: FlagSet): Modifiers = @@ -3555,7 +3562,8 @@ object Parsers { * UsingParamClause ::= ‘(’ ‘using’ (DefTermParams | ContextTypes) ‘)’ * DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’ * DefTermParams ::= DefTermParam {‘,’ DefTermParam} - * DefTermParam ::= {Annotation} [‘erased’] [‘inline’] Param + * DefTermParam ::= {Annotation} TermParamMods Param + * TermParamMods ::= [‘erased‘] [‘inline’] | [‘consume‘] * * Param ::= id `:' ParamType [`=' Expr] * @@ -3592,7 +3600,7 @@ object Parsers { def param(): ValDef = { val start = in.offset var mods = impliedMods.withAnnotations(annotations()) - if isErased then + if isConsume || isErased then mods = addModifier(mods) if paramOwner.isClass then mods = addFlag(modifiers(start = mods), ParamAccessor) @@ -3664,6 +3672,7 @@ object Parsers { var mods = EmptyModifiers if in.lookahead.isColon then (mods, true) + else if isConsume then (mods, true) else if isErased then mods = addModifier(mods) val paramsAreNamed = diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index ca2f5da3df3c..52e03de60dea 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -1228,7 +1228,8 @@ object Scanners { && (softModifierNames.contains(name) || name == nme.erased && erasedEnabled || name == nme.tracked && trackedEnabled - || name == nme.update && Feature.ccEnabled) + || name == nme.update && Feature.ccEnabled + || name == nme.consume && Feature.ccEnabled) def isSoftModifierInModifierPosition: Boolean = isSoftModifier && inModifierPosition() diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index cda3e7d4b6f2..4a23f00f3c53 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -15,7 +15,7 @@ import util.SourcePosition import scala.util.control.NonFatal import scala.annotation.switch import config.{Config, Feature} -import ast.tpd +import ast.{tpd, untpd} import cc.* import CaptureSet.Mutability import Capabilities.* @@ -716,8 +716,12 @@ class PlainPrinter(_ctx: Context) extends Printer { case _ => literalText(String.valueOf(const.value)) } - /** Usual target for `Annotation#toText`, overridden in RefinedPrinter */ - def annotText(annot: Annotation): Text = s"@${annot.symbol.name}" + /** Usual target for `Annotation#toText`, overridden in RefinedPrinter, which also + * looks at trees. + */ + override def annotText(annot: Annotation): Text = annotText(annot.symbol) + + protected def annotText(sym: Symbol): Text = s"@${sym.name}" def toText(annot: Annotation): Text = annot.toText(this) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 1e58268c2e38..30934b615649 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -1140,6 +1140,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { /** Textual representation of an instance creation expression without the leading `new` */ protected def constrText(tree: untpd.Tree): Text = toTextLocal(tree).stripPrefix(keywordStr("new ")) // DD + override def annotText(annot: Annotation): Text = annotText(annot.symbol, annot.tree) + protected def annotText(sym: Symbol, tree: untpd.Tree): Text = def recur(t: untpd.Tree): Text = t match case Apply(fn, Nil) => recur(fn) @@ -1152,9 +1154,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case New(tpt) => recur(tpt) case _ => val annotSym = sym.orElse(tree.symbol.enclosingClass) - s"@${if annotSym.exists then annotSym.name.toString else t.show}" + if annotSym.exists then annotText(annotSym) else s"@${t.show}" recur(tree) + protected override def annotText(sym: Symbol): Text = + if sym == defn.ConsumeAnnot then "consume" else super.annotText(sym) + protected def modText(mods: untpd.Modifiers, sym: Symbol, kw: String, isType: Boolean): Text = { // DD val suppressKw = if (enclDefIsClass) mods.isAllOf(LocalParam) else mods.is(Param) var flagMask = @@ -1175,8 +1180,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { Text(annotTexts, " ") ~~ flagsText ~~ (Str(kw) provided !suppressKw) } - override def annotText(annot: Annotation): Text = annotText(annot.symbol, annot.tree) - def optText(name: Name)(encl: Text => Text): Text = if (name.isEmpty) "" else encl(toText(name)) diff --git a/compiler/src/dotty/tools/dotc/transform/DropBreaks.scala b/compiler/src/dotty/tools/dotc/transform/DropBreaks.scala index 5f26a6af6c3c..d5f555763bb4 100644 --- a/compiler/src/dotty/tools/dotc/transform/DropBreaks.scala +++ b/compiler/src/dotty/tools/dotc/transform/DropBreaks.scala @@ -27,6 +27,8 @@ object DropBreaks: /** The number of other references to associated label */ var otherRefs: Int = 0 + override def toString() = s"LabelUsage($goto, $enclMeth, returnRefs = $returnRefs, otherRefs = $otherRefs)" + private val LabelUsages = new Property.Key[Map[Symbol, LabelUsage]] private val ShadowedLabels = new Property.Key[Set[Symbol]] @@ -63,13 +65,22 @@ class DropBreaks extends MiniPhase: */ def unapply(expr: Tree)(using Context): Option[(Symbol, Symbol)] = stripTyped(expr) match case If( - Apply(Select(Select(ex: Ident, label), eq), (lbl @ Ident(local)) :: Nil), + Apply(Select(ex: Ident, isSameLabelAs), (lbl @ Ident(local)) :: Nil), Select(ex2: Ident, value), Apply(throww, (ex3: Ident) :: Nil)) - if label == nme.label && eq == nme.eq && local == nme.local && value == nme.value + if isSameLabelAs == nme.isSameLabelAs && local == nme.local && value == nme.value && throww.symbol == defn.throwMethod && ex.symbol == ex2.symbol && ex.symbol == ex3.symbol => Some((ex.symbol, lbl.symbol)) + case If( + Apply(Select(ex: Ident, isSameLabelAs), (lbl @ Ident(local)) :: Nil), + Literal(_), // in the case where the value is constant folded + Apply(throww, (ex3: Ident) :: Nil)) + if isSameLabelAs == nme.isSameLabelAs && local == nme.local + && throww.symbol == defn.throwMethod + && ex.symbol == ex3.symbol + && expr.tpe.isSingleton => + Some((ex.symbol, lbl.symbol)) case _ => None end GuardedThrow diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 8bf88a0027c4..b4d766a1bd24 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -14,9 +14,11 @@ import Flags.*, Constants.* import Decorators.* import NameKinds.{PatMatStdBinderName, PatMatAltsName, PatMatResultName} import config.Printers.patmatch +import config.Feature import reporting.* import ast.* import util.Property.* +import cc.{CapturingType, Capabilities} import scala.annotation.tailrec import scala.collection.mutable @@ -427,8 +429,11 @@ object PatternMatcher { && !hasExplicitTypeArgs(extractor) case _ => false } + val castTp = if Feature.ccEnabled + then CapturingType(tpt.tpe, scrutinee.termRef.singletonCaptureSet) + else tpt.tpe TestPlan(TypeTest(tpt, isTrusted), scrutinee, tree.span, - letAbstract(ref(scrutinee).cast(tpt.tpe)) { casted => + letAbstract(ref(scrutinee).cast(castTp)) { casted => nonNull += casted patternPlan(casted, pat, onSuccess) }) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index e86414cc2183..4e74b558b5db 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -995,6 +995,10 @@ class Namer { typer: Typer => end if } + private def normalizeFlags(denot: SymDenotation)(using Context): Unit = + if denot.is(Method) && denot.hasAnnotation(defn.ConsumeAnnot) then + denot.setFlag(Mutable) + /** Intentionally left without `using Context` parameter. We need * to pick up the context at the point where the completer was created. */ @@ -1004,6 +1008,7 @@ class Namer { typer: Typer => addInlineInfo(sym) denot.info = typeSig(sym) invalidateIfClashingSynthetic(denot) + normalizeFlags(denot) Checking.checkWellFormed(sym) denot.info = avoidPrivateLeaks(sym) } diff --git a/compiler/test/dotty/tools/backend/jvm/LabelBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/LabelBytecodeTests.scala index aea567b87f91..6a420e2bb56e 100644 --- a/compiler/test/dotty/tools/backend/jvm/LabelBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/LabelBytecodeTests.scala @@ -21,7 +21,7 @@ class LabelBytecodeTests extends DottyBytecodeTest { """val local = boundary.Label[Long]() |try break(5L)(using local) |catch case ex: boundary.Break[Long] @unchecked => - | if ex.label eq local then ex.value + | if ex.isSameLabelAs(local) then ex.value | else throw ex """.stripMargin, "Long", diff --git a/docs/_docs/internals/exclusive-capabilities.md b/docs/_docs/internals/exclusive-capabilities.md index 938eb5ccdd55..07d3de3f124a 100644 --- a/docs/_docs/internals/exclusive-capabilities.md +++ b/docs/_docs/internals/exclusive-capabilities.md @@ -6,21 +6,19 @@ Language design draft ## Capability Kinds A capability is called - - _exclusive_ if it is `cap` or it has an exclusive capability in its capture set. - - _shared_ otherwise. - -There is a new top capability `shared` which can be used as a capability for deriving shared capture sets. Other shared capabilities are created as read-only versions of exclusive capabilities. + - _shared_ if it is classified as a `SharedCapability` + - _exclusive_ otherwise. ## Update Methods We introduce a new trait ```scala -trait Mutable +trait Mutable extends ExclusiveCapability, Classifier ``` It is used as a base trait for types that define _update methods_ using a new soft modifier `update`. -`update` can only be used in classes or objects extending `Mutable`. An update method is allowed to access exclusive capabilities in the method's environment. By contrast, a normal method in a type extending `Mutable` may access exclusive capabilities only if they are defined locally or passed to it in parameters. +`update` can only be used in classes or objects extending `Mutable`. An update method is allowed to access exclusive capabilities in the method's environment. By contrast, a normal method in a type extending `Mutable` may access exclusive capabilities only if they are defined locally or passed to it in parameters. **Example:** ```scala @@ -115,11 +113,6 @@ a method that accesses exclusive capabilities. If `x` is an exclusive capability of a type extending `Mutable`, `x.rd` is its associated, shared _read-only_ capability. -`shared` can be understood as the read-only capability corresponding to `cap`. -```scala - shared = cap.rd -``` - A _top capability_ is either `cap` or `shared`. @@ -134,7 +127,7 @@ The meaning of `^` and `=>` is the same as before: **Implicitly added capture sets** -A reference to a type extending any of the traits `Capability` or `Mutable` gets an implicit capture set `{shared}` in case no explicit capture set is given. +A reference to a type extending trait `Mutable` gets an implicit capture set `{cap.rd}` provided no explicit capture set is given. For instance, a matrix multiplication method can be expressed as follows: @@ -148,7 +141,7 @@ def mul(a: Matrix, b: Matrix, c: Matrix^): Unit = ``` Here, `a` and `b` are implicitly read-only, and `c`'s type has capture set `cap`. I.e. with explicit capture sets this would read: ```scala -def mul(a: Matrix^{shared}, b: Matrix^{shared}, c: Matrix^{cap}): Unit +def mul(a: Matrix^{cap.rd}, b: Matrix^{cap.rd}, c: Matrix^{cap}): Unit ``` Separation checking will then make sure that `a` and `b` must be different from `c`. @@ -173,8 +166,6 @@ The read-only function `ro` maps capture sets to read-only capture sets. It is d - `ro(x) = x` if `x` is shared - `ro(x) = x.rd` if `x` is exclusive - - ## Subcapturing Subcapturing has to take the mode of capture sets into account. We let `m` stand for arbitrary modes. @@ -271,7 +262,7 @@ A reference `p.m` to an update method or class `m` of a mutable type is allowed If `e` is an expression of a type `T^cs` extending `Mutable` and the expected type is a value type that is not a mutable type, then the type of `e` is mapped to `T^ro(cs)`. - + + +To integrate exception and capture checking, only two changes are needed: + + - `CanThrow` is declared as a class extending `Control`, so all references to `CanThrow` instances are tracked. + - Escape checking is extended to `try` expressions. The result type of a `try` is not allowed to + capture capabilities defined in the body of the `try`. diff --git a/docs/_docs/reference/experimental/capture-checking/classes.md b/docs/_docs/reference/experimental/capture-checking/classes.md new file mode 100644 index 000000000000..9f9269915233 --- /dev/null +++ b/docs/_docs/reference/experimental/capture-checking/classes.md @@ -0,0 +1,233 @@ +--- +layout: doc-page +title: "Capture Checking of Classes" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/capture-checking/classes.html +--- + +## Introduction + +The principles for capture checking closures also apply to classes. For instance, consider: +```scala +class Logger(using fs: FileSystem): + def log(s: String): Unit = ... summon[FileSystem] ... + +def test(xfs: FileSystem): Logger^{xfs} = + Logger(xfs) +``` +Here, class `Logger` retains the capability `fs` as a (private) field. Hence, the result +of `test` is of type `Logger^{xfs}` + +Sometimes, a tracked capability is meant to be used only in the constructor of a class, but +is not intended to be retained as a field. This fact can be communicated to the capture +checker by declaring the parameter as `@constructorOnly`. Example: +```scala +import annotation.constructorOnly + +class NullLogger(using @constructorOnly fs: FileSystem): + ... +def test2(using fs: FileSystem): NullLogger = NullLogger() // OK +``` + +The captured references of a class include _local capabilities_ and _argument capabilities_. Local capabilities are capabilities defined outside the class and referenced from its body. Argument capabilities are passed as parameters to the primary constructor of the class. Local capabilities are inherited: +the local capabilities of a superclass are also local capabilities of its subclasses. Example: + +```scala +class Cap extends caps.SharedCapability + +def test(a: Cap, b: Cap, c: Cap) = + class Super(y: Cap): + def f = a + class Sub(x: Cap) extends Super(x) + def g = b + Sub(c) +``` +Here class `Super` has local capability `a`, which gets inherited by class +`Sub` and is combined with `Sub`'s own local capability `b`. Class `Sub` also has an argument capability corresponding to its parameter `x`. This capability gets instantiated to `c` in the final constructor call `Sub(c)`. Hence, +the capture set of that call is `{a, b, c}`. + +The capture set of the type of `this` of a class is inferred by the capture checker, unless the type is explicitly declared with a self type annotation like this one: +```scala +class C: + self: D^{a, b} => ... +``` +The inference observes the following constraints: + + - The type of `this` of a class `C` includes all captured references of `C`. + - The type of `this` of a class `C` is a subtype of the type of `this` + of each parent class of `C`. + - The type of `this` must observe all constraints where `this` is used. + +For instance, in +```scala +class Cap extends caps.SharedCapability +def test(c: Cap) = + class A: + val x: A = this + def f = println(c) // error +``` +we know that the type of `this` must be pure, since `this` is the right hand side of a `val` with type `A`. However, in the last line we find that the capture set of the class, and with it the capture set of `this`, would include `c`. This leads to a contradiction, and hence to a checking error: +``` + | def f = println(c) // error + | ^ + | reference (c : Cap) is not included in the allowed capture set {} + | of the enclosing class A +``` + +## Capture Tunnelling + +Consider the following simple definition of a `Pair` class: +```scala +class Pair[+A, +B](x: A, y: B): + def fst: A = x + def snd: B = y +``` +What happens if `Pair` is instantiated like this (assuming `ct` and `fs` are two capabilities in scope)? +```scala +def x: Int ->{ct} String +def y: Logger^{fs} +def p = Pair(x, y) +``` +The last line will be typed as follows: +```scala +def p: Pair[Int ->{ct} String, Logger^{fs}] = Pair(x, y) +``` +This might seem surprising. The `Pair(x, y)` value does capture capabilities `ct` and `fs`. Why don't they show up in its type at the outside? + +The answer is capture tunnelling. Once a type variable is instantiated to a capturing type, the +capture is not propagated beyond this point. On the other hand, if the type variable is instantiated +again on access, the capture information "pops out" again. For instance, even though `p` is technically pure because its capture set is empty, writing `p.fst` would record a reference to the captured capability `ct`. So if this access was put in a closure, the capability would again form part of the outer capture set. E.g. +```scala +() => p.fst : () -> Int ->{ct} String +``` +In other words, references to capabilities "tunnel through" in generic instantiations from creation to access; they do not affect the capture set of the enclosing generic data constructor applications. +This principle plays an important part in making capture checking concise and practical. + + +## A Larger Example + +As a larger example, we present an implementation of lazy lists and some use cases. For simplicity, +our lists are lazy only in their tail part. This corresponds to what the Scala-2 type `Stream` did, whereas Scala 3's `LazyList` type computes strictly less since it is also lazy in the first argument. + +Here is the base trait `LzyList` for our version of lazy lists: +```scala +trait LzyList[+A]: + def isEmpty: Boolean + def head: A + def tail: LzyList[A]^{this} +``` +Note that `tail` carries a capture annotation. It says that the tail of a lazy list can +potentially capture the same references as the lazy list as a whole. + +The empty case of a `LzyList` is written as usual: +```scala +object LzyNil extends LzyList[Nothing]: + def isEmpty = true + def head = ??? + def tail = ??? +``` +Here is a formulation of the class for lazy cons nodes: +```scala +import scala.compiletime.uninitialized + +final class LzyCons[+A](hd: A, tl: () => LzyList[A]^) extends LzyList[A]: + private var forced = false + private var cache: LzyList[A]^{this} = uninitialized + private def force = + if !forced then { cache = tl(); forced = true } + cache + + def isEmpty = false + def head = hd + def tail: LzyList[A]^{this} = force +end LzyCons +``` +The `LzyCons` class takes two parameters: A head `hd` and a tail `tl`, which is a function +returning a `LzyList`. Both the function and its result can capture arbitrary capabilities. +The result of applying the function is memoized after the first dereference of `tail` in +the private mutable field `cache`. Note that the typing of the assignment `cache = tl()` relies on the monotonicity rule for `{this}` capture sets. + +Here is an extension method to define an infix cons operator `#:` for lazy lists. It is analogous +to `::` but instead of a strict list it produces a lazy list without evaluating its right operand. +```scala +extension [A](x: A) + def #:(xs1: => LzyList[A]^): LzyList[A]^{xs1} = + LzyCons(x, () => xs1) +``` +Note that `#:` takes an impure call-by-name parameter `xs1` as its right argument. The result +of `#:` is a lazy list that captures that argument. + +As an example usage of `#:`, here is a method `tabulate` that creates a lazy list +of given length with a generator function `gen`. The generator function is allowed +to have side effects. +```scala +def tabulate[A](n: Int)(gen: Int => A): LzyList[A]^{gen} = + def recur(i: Int): LzyList[A]^{gen} = + if i == n then LzyNil + else gen(i) #: recur(i + 1) + recur(0) +``` +Here is a use of `tabulate`: +```scala +class LimitExceeded extends Exception +def squares(n: Int)(using ct: CanThrow[LimitExceeded]) = + tabulate(10): i => + if i > 9 then throw LimitExceeded() + i * i +``` +The inferred result type of `squares` is `LzyList[Int]^{ct}`, i.e it is a lazy list of +`Int`s that can throw the `LimitExceeded` exception when it is elaborated by calling `tail` +one or more times. + +Here are some further extension methods for mapping, filtering, and concatenating lazy lists: +```scala +extension [A](xs: LzyList[A]^) + def map[B](f: A => B): LzyList[B]^{xs, f} = + if xs.isEmpty then LzyNil + else f(xs.head) #: xs.tail.map(f) + + def filter(p: A => Boolean): LzyList[A]^{xs, p} = + if xs.isEmpty then LzyNil + else if p(xs.head) then xs.head #: xs.tail.filter(p) + else xs.tail.filter(p) + + def concat(ys: LzyList[A]^): LzyList[A]^{xs, ys} = + if xs.isEmpty then ys + else xs.head #: xs.tail.concat(ys) + + def drop(n: Int): LzyList[A]^{xs} = + if n == 0 then xs else xs.tail.drop(n - 1) +``` +Their capture annotations are all as one would expect: + + - Mapping a lazy list produces a lazy list that captures the original list as well + as the (possibly impure) mapping function. + - Filtering a lazy list produces a lazy list that captures the original list as well + as the (possibly impure) filtering predicate. + - Concatenating two lazy lists produces a lazy list that captures both arguments. + - Dropping elements from a lazy list gives a safe approximation where the original list is captured in the result. In fact, it's only some suffix of the list that is retained at run time, but our modelling identifies lazy lists and their suffixes, so this additional knowledge would not be useful. + +Of course the function passed to `map` or `filter` could also be pure. After all, `A -> B` is a subtype of `(A -> B)^{cap}` which is the same as `A => B`. In that case, the pure function +argument will _not_ show up in the result type of `map` or `filter`. For instance: +```scala +val xs = squares(10) +val ys: LzyList[Int]^{xs} = xs.map(_ + 1) +``` +The type of the mapped list `ys` has only `xs` in its capture set. The actual function +argument does not show up since it is pure. Likewise, if the lazy list +`xs` was pure, it would not show up in any of the method results. +This demonstrates that capability-based +effect systems with capture checking are naturally _effect polymorphic_. + +This concludes our example. It's worth mentioning that an equivalent program defining and using standard, strict lists would require no capture annotations whatsoever. It would compile exactly as written now in standard Scala 3, yet one gets the capture checking for free. Essentially, `=>` already means "can capture anything" and since in a strict list side effecting operations are not retained in the result, there are no additional captures to record. A strict list could of course capture side-effecting closures in its elements but then tunnelling applies, since +these elements are represented by a type variable. This means we don't need to annotate anything there either. + +Another possibility would be a variant of lazy lists that requires all functions passed to `map`, `filter` and other operations like it to be pure. E.g. `map` on such a list would be defined like this: +```scala +extension [A](xs: LzyList[A]) + def map[B](f: A -> B): LzyList[B] = ... +``` +That variant would not require any capture annotations either. + +To summarize, there are two "sweet spots" of data structure design: strict lists in +side-effecting or resource-aware code and lazy lists in purely functional code. +Both are already correctly capture-typed without requiring any explicit annotations. Capture annotations only come into play where the semantics gets more complicated because we deal with delayed effects such as in impure lazy lists or side-effecting iterators over strict lists. This property is probably one of the greatest plus points of our approach to capture checking compared to previous techniques which tend to be more noisy. diff --git a/docs/_docs/reference/experimental/capture-checking/classifiers.md b/docs/_docs/reference/experimental/capture-checking/classifiers.md new file mode 100644 index 000000000000..bbba42c37da9 --- /dev/null +++ b/docs/_docs/reference/experimental/capture-checking/classifiers.md @@ -0,0 +1,109 @@ +--- +layout: doc-page +title: "Capability Classifiers" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/capture-checking/classifiers.html +--- + +## Introduction + +Capabilities are extremely versatile. They can express concepts from many different domains. Exceptions, continuations, I/O, mutation, information flow, security permissions, are just some examples, the list goes on. + +Sometimes it is important to restrict, or: _classify_ what kind of capabilities are expected or returned in a context. For instance, we might want to allow only control capabilities such as `CanThrow`s or boundary `Label`s but no +other capabilities. Or might want to allow mutation, but no other side effects. This is achieved by having a capability class extend a _classifier_. + +For instance, the `scala.caps` package defines a classifier trait called `Control`, +like this: +```scala + trait Control extends SharedCapability, Classifier +``` +The [Gears library](https://lampepfl.github.io/gears/) then defines a capability class `Async` which extends `Control`. + +```scala + trait Async extends Control +``` +Unlike normal inheritance, classifiers also restrict the capture set of a capability. For instance, say we have a function +```scala + def f(using async: Async^) = body +``` +(the `^` is as usual redundant here since `Async` is a capability trait). +Then we have the guarantee that any actual `async` argument can only capture +capabilities that have types extending `Control`. No other capabilities such as mutation or I/O are allowed. + +A class or trait becomes a classifier by extending directly the marker trait +`caps.Classifier`. So with the definitions above, `Control` is a classifier trait, but `Async` is not, since it extends `Classifier` only indirectly, through `Control`. + +Classifiers are unique: a class cannot extend directly or transitively at the same time two unrelated classifier traits. So if a class transitively extends two classifier traits `C1` and `C2` then one of them must be a subtrait of the other. + +### Predefined Classifiers + +The `caps` object defines the `Classifier` trait itself and some traits that extend it: +```scala +trait Classifier + +sealed trait Capability + +trait SharedCapability extends Capability Classifier +trait Control extends SharedCapability, Classifier + +trait ExclusiveCapability extends Capability, Classifier +trait Mutable extends ExclusiveCapability, Classifier +``` +Here is a graph showing the hierarchy of predefined classifier traits: +``` + Capability + / \ + / \ + / \ + / \ + SharedCapability ExclusiveCapability + | | + | | + | | + | | + Control Mutable +``` +At the top of the hierarchy, we distinguish between _shared_ and _exclusive_ capabilities in two classifier traits `SharedCapability` and `ExclusiveCapability`. All capability classes we have seen so far are shared. +`ExclusiveCapability` is a base trait for capabilities that allow only un-aliased access to the data they represent, with the rules governed by [separation checking](separation-checking.md). Separation checking is currently an optional extension of capture checking, enabled by a different language import. Since `Capability` is a sealed trait, all capability classes are either shared or exclusive. + +`Control` capabilities are shared. This means they cannot directly or indirectly capture exclusive capabilities such as capabilities that control access to mutable state. Typical `Control` capabilities are: + + - `Label`s that enable to return from a `boundary`, + - `CanThrow` capabilities that enable throwing exceptions, or + - `Async` capabilities that allow to suspend. + +These are all expressed by having their capability classes extend `Control`. + +### Classifier Restriction + +Consider the following problem: The `Try.apply` method takes in its `body` parameter a computation, and runs it while catching any exceptions or `boundary.break` aborts. The exception or break will be re-issued when calling +the `get` method of a `Try` object. What should a capability-aware signature of `Try` be? + +The body passed to `Try.apply` can have arbitrary effects, so it can retain arbitrary capabilities. Yet the resulting `Try` object will retain only those capabilities of `body` which are classified as `Control`. So the signature of `Try.apply` should look like this: +```scala +object Try: + def apply[T](body: => T): Try[T]^{body.only[Control]} +``` +Note a new form of capability in the result's capture set: `body.only[Control]`. This is called a _restricted capability_. The general form of a restricted capability is +`c.only[A]` where + + - `c` is a regular, unrestricted capability + - `A` is a classifier trait. + + When substituting a capability set for the underlying capability, we drop all capabilities that are known to be unrelated to the classifier. For instance, say we have an actual body `{ expr }` that uses a capture set `{io, async}` where + + - `io` is an `IO` capability, where IO is assumed to extend `SharedCapability` but not `Control`. + - `async` is a `Control` capability. + +Then the result of `Try { expr }` would have type `Try^{async}`. We drop `io` since +`io`'s type is a `Capability` class that does not extend `Control`. + +If `expr` would use an additional capability `proc: () => Unit`, then `proc` would also show up in the result capture set. Since `proc` is fully effect-polymorphic, we can't exclude that it retains `Control` capabilities, so we have to keep it in the restricted capture set. These elements are shown together in the following example: +```scala +class IO extends caps.SharedCapability +class Async extends caps.Control + +def test(io: IO, async: Async, proc: () => Unit) = + val r = Try: + // code accessing `io`, `async`, and `proc` and returning an `Int. + val _: Try[Int]^{async, proc} = r +``` diff --git a/docs/_docs/reference/experimental/capture-checking/how-to-use.md b/docs/_docs/reference/experimental/capture-checking/how-to-use.md new file mode 100644 index 000000000000..d08125dc3fc8 --- /dev/null +++ b/docs/_docs/reference/experimental/capture-checking/how-to-use.md @@ -0,0 +1,19 @@ +--- +layout: doc-page +title: "How to Use the Capture Checker" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/capture-checking/how-to-use.html +--- + +## Enabling Capture Checking + +TODO + +## Compilation Options + +The following options are relevant for capture checking. + + - **-Vprint:cc** Prints the program with capturing types as inferred by capture checking. + - **-Ycc-verbose** Prints capabilities and capturing types in more detail. + - **-Ycc-debug** Gives more detailed, implementation-oriented information about capture checking, as described in the next section. + + The implementation supporting capture checking with these options is currently in branch `cc-experiment` on dotty.epfl.ch. diff --git a/docs/_docs/reference/experimental/capture-checking/internals.md b/docs/_docs/reference/experimental/capture-checking/internals.md new file mode 100644 index 000000000000..6aff9b208fd9 --- /dev/null +++ b/docs/_docs/reference/experimental/capture-checking/internals.md @@ -0,0 +1,84 @@ +--- +layout: doc-page +title: "Capture Checking Internals" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/capture-checking/internals.html +--- + +The capture checker is architected as a propagation constraint solver, which runs as a separate phase after type-checking and some initial transformations. + +Constraint variables stand for unknown capture sets. A constraint variable is introduced + + - for every part of a previously inferred type, + - for the accessed references of every method, class, anonymous function, or by-name argument, + - for the parameters passed in a class constructor call. + +Capture sets in explicitly written types are treated as constants (before capture checking, such sets are simply ignored). + +The capture checker essentially rechecks the program with the usual typing rules. Every time a subtype requirement between capturing types is checked, this translates to a subcapturing test on capture sets. If the two sets are constant, this is simply a yes/no question, where a no will produce an error message. + +If the lower set `C₁` of a comparison `C₁ <: C₂` is a variable, the set `C₂` is recorded +as a _superset_ of `C₁`. If the upper set `C₂` is a variable, the elements of `C₁` are _propagated_ to `C₂`. Propagation of an element `x` to a set `C` means that `x` is included as an element in `C`, and it is also propagated +to all known supersets of `C`. If such a superset is a constant, it is checked that `x` is included in it. If that's not the case, the original comparison `C₁ <: C₂` has no solution and an error is reported. + +The type checker also performs various maps on types, for instance when substituting actual argument types for formal parameter types in dependent functions, or mapping +member types with "as-seen-from" in a selection. Maps keep track of the variance +of positions in a type. The variance is initially covariant, it flips to +contravariant in function parameter positions, and can be either covariant, +contravariant, or nonvariant in type arguments, depending on the variance of +the type parameter. + +When capture checking, the same maps are also performed on capture sets. If a capture set is a constant, its elements (which are capabilities) are mapped as regular types. If the result of such a map is not a capability, the result is approximated according to the variance of the type. A covariant approximation replaces a type by its capture set. +A contravariant approximation replaces it with the empty capture set. A nonvariant +approximation replaces the enclosing capturing type with a range of possible types +that gets propagated and resolved further out. + +When a mapping `m` is performed on a capture set variable `C`, a new variable `Cm` is created that contains the mapped elements and that is linked with `C`. If `C` subsequently acquires further elements through propagation, these are also propagated to `Cm` after being transformed by the `m` mapping. `Cm` also gets the same supersets as `C`, mapped again using `m`. + +One interesting aspect of the capture checker concerns the implementation of capture tunnelling. The [foundational theory](https://infoscience.epfl.ch/record/290885) on which capture checking is based makes tunnelling explicit through so-called _box_ and +_unbox_ operations. Boxing hides a capture set and unboxing recovers it. The capture checker inserts virtual box and unbox operations based on actual and expected types similar to the way the type checker inserts implicit conversions. When capture set variables are first introduced, any capture set in a capturing type that is an instance of a type parameter instance is marked as "boxed". A boxing operation is +inserted if the expected type of an expression is a capturing type with +a boxed capture set variable. The effect of the insertion is that any references +to capabilities in the boxed expression are forgotten, which means that capture +propagation is stopped. Dually, if the actual type of an expression has +a boxed variable as capture set, an unbox operation is inserted, which adds all +elements of the capture set to the environment. + +Boxing and unboxing has no runtime effect, so the insertion of these operations is only simulated; the only visible effect is the retraction and insertion +of variables in the capture sets representing the environment of the currently checked expression. + +The `-Ycc-debug` option provides some insight into the workings of the capture checker. +When it is turned on, boxed sets are marked explicitly and capture set variables are printed with an ID and some information about their provenance. For instance, the string `{f, xs}33M5V` indicates a capture set +variable that is known to hold elements `f` and `xs`. The variable's ID is `33`. The `M` +indicates that the variable was created through a mapping from a variable with ID `5`. The latter is a regular variable, as indicated + by `V`. + +Generally, the string following the capture set consists of alternating numbers and letters where each number gives a variable ID and each letter gives the provenance of the variable. Possible letters are + + - `V` : a regular variable, + - `M` : a variable resulting from a _mapping_ of the variable indicated by the string to the right, + - `B` : similar to `M` but where the mapping is a _bijection_, + - `F` : a variable resulting from _filtering_ the elements of the variable indicated by the string to the right, + - `I` : a variable resulting from an _intersection_ of two capture sets, + - `D` : a variable resulting from the set _difference_ of two capture sets. + - `R` : a regular variable that _refines_ a class parameter, so that the capture + set of a constructor argument is known in the class instance type. + +At the end of a compilation run, `-Ycc-debug` will print all variable dependencies of variables referred to in previous output. Here is an example: +``` +Capture set dependencies: + {}2V :: + {}3V :: + {}4V :: + {f, xs}5V :: {f, xs}31M5V, {f, xs}32M5V + {f, xs}31M5V :: {xs, f} + {f, xs}32M5V :: +``` +This section lists all variables that appeared in previous diagnostics and their dependencies, recursively. For instance, we learn that + + - variables 2, 3, 4 are empty and have no dependencies, + - variable `5` has two dependencies: variables `31` and `32` which both result from mapping variable `5`, + - variable `31` has a constant fixed superset `{xs, f}` + - variable `32` has no dependencies. + + + diff --git a/docs/_docs/reference/experimental/capture-checking/polymorphism.md b/docs/_docs/reference/experimental/capture-checking/polymorphism.md new file mode 100644 index 000000000000..8853b54c82a2 --- /dev/null +++ b/docs/_docs/reference/experimental/capture-checking/polymorphism.md @@ -0,0 +1,92 @@ +--- +layout: doc-page +title: "Capability Polymorphism" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/capture-checking/polymorphism.html +--- + +## Introduction + +It is sometimes convenient to write operations that are parameterized with a capture set of capabilities. For instance consider a type of event sources +`Source` on which `Listener`s can be registered. Listeners can hold certain capabilities, which show up as a parameter to `Source`: +```scala +class Source[X^]: + private var listeners: Set[Listener^{X}] = Set.empty + def register(x: Listener^{X}): Unit = + listeners += x + + def allListeners: Set[Listener^{X}] = listeners +``` +The type variable `X^` can be instantiated with a set of capabilities. It can occur in capture sets in its scope. For instance, in the example above +we see a variable `listeners` that has as type a `Set` of `Listeners` capturing `X`. The `register` method takes a listener of this type +and assigns it to the variable. + +Capture-set variables `X^` without user-annotated bounds by default range over the interval `>: {} <: {caps.cap}` which is the universe of capture sets instead of regular types. + +Under the hood, such capture-set variables are represented as regular type variables within the special interval + `>: CapSet <: CapSet^`. +For instance, `Source` from above could be equivalently +defined as follows: +```scala +class Source[X >: CapSet <: CapSet^]: + ... +``` +`CapSet` is a sealed trait in the `caps` object. It cannot be instantiated or inherited, so its only +purpose is to identify type variables which are capture sets. In non-capture-checked +usage contexts, the type system will treat `CapSet^{a}` and `CapSet^{a,b}` as the type `CapSet`, whereas +with capture checking enabled, it will take the annotated capture sets into account, +so that `CapSet^{a}` and `CapSet^{a,b}` are distinct. +This representation based on `CapSet` is subject to change and +its direct use is discouraged. + +Capture-set variables can be inferred like regular type variables. When they should be instantiated +explicitly one supplies a concrete capture set. For instance: +```scala +class Async extends caps.SharedCapability + +def listener(async: Async): Listener^{async} = ??? + +def test1(async1: Async, others: List[Async]) = + val src = Source[{async1, others*}] + ... +``` +Here, `src` is created as a `Source` on which listeners can be registered that refer to the `async` capability or to any of the capabilities in list `others`. So we can continue the example code above as follows: +```scala + src.register(listener(async1)) + others.map(listener).foreach(src.register) + val ls: Set[Listener^{async, others*}] = src.allListeners +``` +A common use-case for explicit capture parameters is describing changes to the captures of mutable fields, such as concatenating +effectful iterators: +```scala +class ConcatIterator[A, C^](var iterators: mutable.List[IterableOnce[A]^{C}]): + def concat(it: IterableOnce[A]^): ConcatIterator[A, {C, it}]^{this, it} = + iterators ++= it // ^ + this // track contents of `it` in the result +``` +In such a scenario, we also should ensure that any pre-existing alias of a `ConcatIterator` object should become +inaccessible after invoking its `concat` method. This is achieved with [mutation and separation tracking](separation-checking.md) which are currently in development. + +## Capability Members + +Just as parametrization by types can be equally expressed with type members, we could +also define the `Source[X^]` class above using a _capability member_: +```scala +class Source: + type X^ + private var listeners: Set[Listener^{this.X}] = Set.empty + ... // as before +``` +Here, we can refer to capability members using paths in capture sets (such as `{this.X}`). Similarly to type members, +capability members can be upper- and lower-bounded with capture sets: +```scala +trait Thread: + type Cap^ + def run(block: () ->{this.Cap} -> Unit): Unit + +trait GPUThread extends Thread: + type Cap^ >: {cudaMalloc, cudaFree} <: {caps.cap} +``` +Since `caps.cap` is the top element for subcapturing, we could have also left out the +upper bound: `type Cap^ >: {cudaMalloc, cudaFree}`. + +**Advanced uses:** We discuss more advanced uses cases for capability members [here](advanced.md). diff --git a/docs/_docs/reference/experimental/capture-checking/scoped-caps.md b/docs/_docs/reference/experimental/capture-checking/scoped-caps.md new file mode 100644 index 000000000000..b0296fb47b03 --- /dev/null +++ b/docs/_docs/reference/experimental/capture-checking/scoped-caps.md @@ -0,0 +1,122 @@ +--- +layout: doc-page +title: "Scoped Caps" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/capture-checking/scoped-caps.html +--- + +## Scoped Universal Capabilities + +When discussing escape checking, we referred to a scoping discipline. That is, capture sets can contain only capabilities that are visible at the point where the set is defined. But that raises the question where a universal capability `cap` is defined? In fact, what is written as the top type `cap` can mean different capabilities, depending on scope. Usually a `cap` refers to a universal capability defined in the scope where the `cap` appears. + +Special rules apply to `cap`s in method and function parameters and results. For example, take this method: + +```scala + def makeLogger(fs: FileSystem^): Logger^ = new Logger(fs) +``` +This creates a `Logger` that captures `fs`. +We could have been more specific in specifying `Logger^{fs}` as the return type of makeLogger, but the current definition is also valid, and might be preferable if we want to hide details what the returned logger captures. If we write it as above then certainly the implied `cap` in the return type of `Logger` should be able subsume the capability `fs`. This means that this `cap` has to be defined in a scope in which +`fs` is visible. + +In logic, the usual way to achieve this scoping is with an existential binder. We can express the type of `makeLogger` like this: +```scala +makeLogger: (fs: ∃cap₁.FileSystem^{cap₁}): ∃cap₂. Logger^{cap₂} +``` +In words: `makeLogger` takes a parameter `fs` of type `Filesystem` capturing _some_ universal capability `cap` and returns a `Logger` capturing some other (possibly different) universal `cap`. + +We can also turn the existential in the function parameter to a universal "forall" +in the function itself. In that alternative notation, the type of makeLogger would read like this: +```scala +makeLogger: ∀cap₁.(fs: FileSystem^{cap₁}): ∃cap₂. Logger^{cap₂} +``` +There's a connection with [capture polymorphism](polymorphism.md) here. `cap`s in function parameters behave like additional +capture parameters that can be instantiated at the call site to arbitrary capabilities. + +The conventions for method types carry over to function types. A function type +```scala + (x: T) -> U^ +``` +is interpreted as having an existentially bound `cap` in the result, like this +```scala + (x: T) -> ∃cap.U^{cap} +``` +The same rules hold for the other kinds of function arrows, `=>`, `?->`, and `?=>`. So `cap` can in this case +subsume the function parameter `x` since it is locally bound in the function result. + +However, the expansion of `cap` into an existentially bound variable only applies to functions that use +the dependent function style syntax, with explicitly named parameters. Parametric functions such as +`A => B^` or `(A₍, ..., Aₖ) -> B^` don't bind their result cap in an existential quantifier. +For instance, the function +```scala + (x: A) -> B -> C^ +``` +is interpreted as +```scala + (x: A) -> ∃cap.B -> C^{cap} +``` +In other words, existential quantifiers are only inserted in results of function arrows that follow an explicitly named parameter list. + +To summarize: + + - If a function result type follows a named parameter list and contains covariant occurrences of `cap`, + we replace these occurrences with a fresh existential variable which + is bound by a quantifier scoping over the result type. + - If a function parameter type contains covariant occurrences of `cap`, we replace these occurrences with + a fresh existential variable scoping over the parameter type. + - Occurrences of `cap` elsewhere are not translated. They can be seen as representing an existential in the + scope of the definition in which they appear. + +**Examples:** + + - `A => B` is an alias type that expands to `A ->{cap} B`, therefore + `(x: T) -> A => B` expands to `(x: T) -> ∃cap.(A ->{cap} B)`. + + - `(x: T) -> Iterator[A => B]` expands to `() -> ∃cap.Iterator[A ->{cap} B]` + + + \ No newline at end of file diff --git a/docs/_docs/reference/experimental/capture-checking/separation-checking.md b/docs/_docs/reference/experimental/capture-checking/separation-checking.md new file mode 100644 index 000000000000..6bb0fd3bf47c --- /dev/null +++ b/docs/_docs/reference/experimental/capture-checking/separation-checking.md @@ -0,0 +1,396 @@ +--- +layout: doc-page +title: "Separation Checking" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/capture-checking/separation-checking.html +--- + +## Introduction + +Separation checking is an extension of capture checking that enforces unique, un-aliased access to capabilities. It is enabled by a language import +```scala +import language.experimental.separationChecking +``` +(or the corresponding setting `-language:experimental.separationChecking`). +The import has to be given in addition to the `language.experimental.captureChecking` import that enables capture checking. +The reason for the second language import is that separation checking is less mature than capture checking proper, so we are less sure +we got the balance of safety and expressivity right for it at the present time. + +The purpose of separation checking is to ensure that certain accesses to capabilities are not aliased. As an example, consider matrix multiplication. A method that multiplies matrices `a`, and `b` into `c`, could be declared like this: +```scala +def multiply(a: Matrix, b: Matrix, c: Matrix): Unit +``` +But that signature alone does not tell us which matrices are supposed to be inputs and which one is the output. Nor does it guarantee that an input matrix is not re-used as the output, which would lead to wrong results. + +With separation checking, we can declare `Matrix` as a `Mutable` type, like this: +```scala +class Matrix(nrows: Int, ncols: Int) extends Mutable: + update def setElem(i: Int, j: Int, x: Double): Unit = ... + def getElem(i: Int, j: Int): Double = ... +``` +We further declare that the `setElem` method in a `Matrix` is an `update` method, which means it has a side effect. + +Separation checking gives a special interpretation to the following modified signature of `multiply`: +```scala +def multiply(a: Matrix, b: Matrix, c: Matrix^): Unit +``` +In fact, only a single character was added: `c`'s type now carries a universal capability. This signature enforces at the same time two desirable properties: + + - Matrices `a`, and `b` are read-only; `multiply` will not call their update method. By contrast, the `c` matrix can be updated. + - Matrices `a` and `b` are different from matrix `c`, but `a` and `b` could refer to the same matrix. + +So, effectively, anything that can be updated must be unaliased. + + + +## Capability Kinds + +A capability is called + - _shared_ if it is [classified](../classifiers.md) as a `SharedCapability` + - _exclusive_ otherwise. + +## The Mutable Trait + +We introduce a new trait +```scala +trait Mutable extends ExclusiveCapability, Classifier +``` +It is used as a [classifier](../classifiers.md) trait for types that define _update methods_ using +a new soft modifier `update`. + +**Example:** +```scala +class Ref(init: Int) extends Mutable: + private var current = init + def get: Int = current + update def set(x: Int): Unit = current = x +``` +`update` can only be used in classes or objects extending `Mutable`. An update method is allowed to access exclusive capabilities in the method's environment. By contrast, a normal method in a type extending `Mutable` may access exclusive capabilities only if they are defined locally or passed to it in parameters. + +In class `Ref`, the `set` should be declared as an update method since it accesses the exclusive write capability of the variable `current` in its environment. + +`update` can also be used on an inner class of a class or object extending `Mutable`. It gives all code in the class the right +to access exclusive capabilities in the class environment. Normal classes +can only access exclusive capabilities defined in the class or passed to it in parameters. + +```scala +object Registry extends Mutable: + var count = 0 + update class Counter: + update def next: Int = + count += 1 + count +``` +Normal method members of `Mutable` classes cannot call update methods. This is indicated since accesses in the callee are recorded in the caller. So if the callee captures exclusive capabilities so does the caller. + +An update method cannot implement or override a normal method, whereas normal methods may implement or override update methods. Since methods such as `toString` or `==` inherited from Object are normal methods, it follows that none of these methods may be implemented as an update method. + +The `apply` method of a function type is also a normal method, hence `Mutable` classes may not implement a function type using an update method as the `apply` method. + +## Mutable Types + +A type is called a _mutable_ if it extends `Mutable` and it has an update method or an update class as non-private member or constructor. + +When we create an instance of a mutable type we always add `cap` to its capture set. For instance, if class `Ref` is declared as shown previously then `new Ref(1)` has type `Ref[Int]^`. + +**Restriction:** A non-mutable type cannot be downcast by a pattern match to a mutable type. + +**Definition:** A class is _read_only_ if the following conditions are met: + + 1. It does not extend any exclusive capabilities from its environment. + 2. It does not take parameters with exclusive capabilities. + 3. It does not contain mutable fields, or fields that take exclusive capabilities. + +**Restriction:** If a class or trait extends `Mutable` all its parent classes or traits must either extend `Mutable` or be read-only. + +The idea is that when we upcast a reference to a type extending `Mutable` to a type that does not extend `Mutable`, we cannot possibly call a method on this reference that uses an exclusive capability. Indeed, by the previous restriction this class must be a read-only class, which means that none of the code implemented +in the class can access exclusive capabilities on its own. And we +also cannot override any of the methods of this class with a method +accessing exclusive capabilities, since such a method would have +to be an update method and update methods are not allowed to override regular methods. + + + +**Example:** + +Consider trait `IterableOnce` from the standard library. + +```scala +trait IterableOnce[+T] extends Mutable: + def iterator: Iterator[T]^{this} + update def foreach(op: T => Unit): Unit + update def exists(op: T => Boolean): Boolean + ... +``` +The trait is a mutable type with many update methods, among them `foreach` and `exists`. These need to be classified as `update` because their implementation in the subtrait `Iterator` uses the update method `next`. +```scala +trait Iterator[T] extends IterableOnce[T]: + def iterator = this + def hasNext: Boolean + update def next(): T + update def foreach(op: T => Unit): Unit = ... + update def exists(op; T => Boolean): Boolean = ... + ... +``` +But there are other implementations of `IterableOnce` that are not mutable types (even though they do indirectly extend the `Mutable` trait). Notably, collection classes implement `IterableOnce` by creating a fresh +`iterator` each time one is required. The mutation via `next()` is then restricted to the state of that iterator, whereas the underlying collection is unaffected. These implementations would implement each `update` method in `IterableOnce` by a normal method without the `update` modifier. + +```scala +trait Iterable[T] extends IterableOnce[T]: + def iterator = new Iterator[T] { ... } + def foreach(op: T => Unit) = iterator.foreach(op) + def exists(op: T => Boolean) = iterator.exists(op) +``` +Here, `Iterable` is not a mutable type since it has no update method as member. +All inherited update methods are (re-)implemented by normal methods. + +**Note:** One might think that we don't need a base trait `Mutable` since in any case +a mutable type is defined by the presence of update methods, not by what it extends. In fact the importance of `Mutable` is that it defines _the other methods_ as read-only methods that _cannot_ access exclusive capabilities. For types not extending `Mutable`, this is not the case. For instance, the `apply` method of a function type is not an update method and the type itself does not extend `Mutable`. But `apply` may well be implemented by +a method that accesses exclusive capabilities. + +## Read-only Capabilities + +If `x` is an exclusive capability of a type extending `Mutable`, `x.rd` is its associated _read-only_ capability. It counts as a shared capability. A read-only capability does not permit access to the mutable fields of a matrix. + +A read-only capability can be seen as a classified capability +using a classifier trait `Read` that extends `Mutable`. I.e. +`x.rd` can be seen as being essentially the same as `x.only[Read]`. +(Currently, this precise equivalence is still waiting to be implemented.) + +**Implicitly added capture sets** + +A reference to a type extending trait `Mutable` gets an implicit capture set `{cap.rd}` provided no explicit capture set is given. This is different from +other capability traits which implicitly add `{cap}`. + +For instance, consider the matrix multiplication method mentioned previously: +```scala +def multiply(a: Matrix, b: Matrix, c: Matrix^): Unit +``` +Here, `a` and `b` are implicitly read-only, and `c`'s type has capture set `cap`. I.e. with explicit capture sets this would read: +```scala +def mul(a: Matrix^{cap.rd}, b: Matrix^{cap.rd}, c: Matrix^{cap}): Unit +``` +Separation checking will then make sure that `a` and `b` must be different from `c`. + +## Accesses to Mutable Types + +An access `p.m` to an update method or class `m` in a mutable type is permitted only if the type `M` of the prefix `p` retains exclusive capabilities. If `M` is pure or its capture set has only shared +capabilities then the access is not permitted. + +A _read-only access_ is a reference `x` to a type extending `Mutable` that is either + + - widened to a value type that is not a mutable type, or + - immediately followed by a selection with a member that is a normal method or class (not an update method or class). + +A read-only access charges the read-only capability `x.rd` to its environment. Other accesses charge the full capability `x`. + +**Example:** + +Consider a reference `x` and two closures `f` and `g`. + +```scala +val x = Ref(1) +val f = () => x.get // f: () ->{x.rd} Unit +val g = () => x.set(1) // g: () ->{x} Unit +``` + +`f` accesses a regular method, so it charges only `x.rd` to its environment which shows up in its capture set. By contrast, `g` +accesses an update method of `x`, so its capture set is `{x}`. + +A reference to a mutable type with an exclusive capture set can be widened to a reference with a read-only set. For instance, the following is OK: +```scala +val a: Ref^ = Ref(1) +val b1: Ref^{a.rd} = a +val b2: Ref^{cap.rd} = a +``` + +## Read-Only Capsets + +If we consider subtyping and subcapturing, we observe what looks like a contradiction: `x.rd` is seen as a restricted capability, so `{x.rd}` should subcapture `{x}`. Yet, we have seen in the example above that sometimes it goes the other way: `a`'s capture set is either `{a}` or `{cap}`, yet `a` can be used to define `b1` and `b2`, with capture sets `{a.rd}` and `{cap.rd}`, respectively. + +The contradiction can be explained by noting that we use a capture set in two different roles. + +First, and as always, a capture set defines _retained capabilities_ that may or may be not used by a value. More capabilities give larger types, and the empty capture set is the smallest set according to that ordering. That makes sense: If a higher-order function like `map` is willing to accept a function `A => B` that can have arbitrary effects it's certainly OK to pass a pure function of type `A -> B` to it. + +But for mutations, we use a capture set in a second role, in which it defines a set of _access permissions_. If we have a `Ref^`, we can access all its methods, but if we have a `Ref^{cap.rd}`, we can access only regular methods, not update methods. From that viewpoint a mutable type with exclusive capabilities lets you do more than a mutable type with just read-only capabilities. So by the Liskov substitution principle, sets with exclusive capabilities subcapture sets with only read-only capabilities. + +The contradiction can be solved by distinguishing these two roles. For access permissions, we express read-only sets with an additional _qualifier_ `RD`. That qualifier is used only in the formal theory and the implementation, it currently cannot be expressed in source. +We add an implicit read-only qualifier `RD` to all capture sets on mutable types that consist only of shared or read-only capabilities. +So when we write +```scala +val b1: Ref^{a.rd} = a +``` +we really mean +```scala +val b1: Ref^{a.rd}.RD = a +``` + +The subcapturing theory for sets is then as before, with the following additional rules: + + - `C <: C.RD` + - `C₁.RD <: C₂.RD` if `C₍ <: C₂` + - `{x, ...}.RD = {x.rd, ...}.RD` + - `{x.rd, ...} <: {x, ...}` + +## Separation Checking + +The idea behind separation checking is simple: We now interpret each occurrence of `cap` as a separate top capability. This includes derived syntaxes like `A^` and `B => C`. We further keep track during capture checking which capabilities are subsumed by each `cap`. If capture checking widens a capability `x` to a top capability `capᵢ`, we say `x` is _hidden_ by `capᵢ`. The rule then is that any capability hidden by a top capability `capᵢ` cannot be referenced independently or hidden in another `capⱼ` in code that can see `capᵢ`. + +Here's an example: +```scala +val x: C^ = y + ... x ... // ok + ... y ... // error +``` +This principle ensures that capabilities such as `x` that have `cap` as underlying capture set are un-aliased or "fresh". Any previously existing aliases such as `y` in the code above are inaccessible as long as `x` is also visible. + +Separation checking applies only to exclusive capabilities and their read-only versions. Any capability extending `SharedCapability` in its type is exempted; the following definitions and rules do not apply to them. + +**Definitions:** + + - The _transitive capture set_ `tcs(c)` of a capability `c` with underlying capture set `C` is `c` itself, plus the transitive capture set of `C`. + + - The _transitive capture set_ `tcs(C)` of a capture set C is the union + of `tcs(c)` for all elements `c` of `C`. + + - Two capture sets _interfere_ if one contains an exclusive capability `x` and the other also contains `x` or contains the read-only capability `x.rd`. Conversely, two capture sets are _separated_ if their transitive capture sets don't interfere. + +Separation checks are applied in the following scenarios: + +### Checking Applications + +When checking a function application `f(e_1, ..., e_n)`, we instantiate each `cap` in a formal parameter of `f` to a fresh top capability and compare the argument types with these instantiated parameter types. We then check that the hidden set of each instantiated top capability for an argument `eᵢ` is separated from the capture sets of all the other arguments as well as from the capture sets of the function prefix and the function result. For instance a +call to +```scala +multiply(a, b, a) +``` +would be rejected since `a` appears in the hidden set of the last parameter of multiply, which has type `Matrix^` and also appears in the capture set of the +first parameter. + +We do not report a separation error between two sets if a formal parameter's capture set explicitly names a conflicting parameter. For instance, consider a method `seq` to apply two effectful function arguments in sequence. It can be declared as follows: +```scala +def seq(f: () => Unit; g: () ->{cap, f} Unit): Unit = + f(); g() +``` +Here, the `g` parameter explicitly mentions `f` in its potential capture set. This means that the `cap` in the same capture set would not need to hide the first argument, since it already appears explicitly in the same set. Consequently, we can pass the same function twice to `compose` without violating the separation criteria: +```scala +val r = Ref(1) +val plusOne = r.set(r.get + 1) +seq(plusOne, plusOne) +``` +Without the explicit mention of parameter `f` in the capture set of parameter `g` of `seq` we'd get a separation error, since the transitive capture sets of both arguments contain `r` and are therefore not separated. + +### Checking Statement Sequences + +When a capability `x` is used at some point in a statement sequence, we check that `{x}` is separated from the hidden sets of all previous definitions. + +Example: +```scala +val a: Ref^ = Ref(1) +val b: Ref^ = a +val x = a.get // error +``` +Here, the last line violates the separation criterion since it uses in `a.get` the capability `a`, which is hidden by the definition of `b`. +Note that this check only applies when there are explicit top capabilities in play. One could very well write +```scala +val a: Ref^ = Ref(1) +val b: Ref^{a} = a +val x = a.get // ok +``` +One can also drop the explicit type of `b` and leave it to be inferred. That would +not cause a separation error either. +```scala +val a: Ref^ = Ref(0 +val b = a +val x = a.get // ok +``` + +### Checking Types + +When a type contains top capabilities we check that their hidden sets don't interfere with other parts of the same type. + +Example: +```scala +val b: (Ref^, Ref^) = (a, a) // error +val c: (Ref^, Ref^{a}) = (a, a) // error +val d: (Ref^{a}, Ref^{a}) = (a, a) // ok +``` +Here, the definition of `b` is in error since the hidden sets of the two `^`s in its type both contain `a`. Likewise, the definition of `c` is in error since the hidden set of the `^` in its type contains `a`, which is also part of a capture set somewhere else in the type. On the other hand, the definition of `d` is legal since there are no hidden sets to check. + +### Checking Return Types + +When a `cap` appears in the return type of a function it means a "fresh" top capability, different from what is known at the call site. Separation checking makes sure this is the case. For instance, the following is OK: +```scala +def newRef(): Ref^ = Ref(1) +``` +And so is this: +```scala +def newRef(): Ref^ = + val a = Ref(1) + a +``` +But the next definitions would cause a separation error: +```scala +val a = Ref(1) +def newRef(): Ref^ = a // error +``` +The rule is that the hidden set of a fresh cap in a return type cannot reference exclusive or read-only capabilities defined outside of the function. What about parameters? Here's another illegal version: +```scala +def incr(a: Ref^): Ref^ = + a.set(a.get + 1) + a +``` +These needs to be rejected because otherwise we could have set up the following bad example: +```scala +val a = Ref(1) +val b: Ref^ = incr(a) +``` +Here, `b` aliases `a` but does not hide it. If we referred to `a` afterwards we would be surprised to see that the reference has now a value of 2. +Therefore, parameters cannot appear in the hidden sets of fresh result caps either, at least not in general. An exception to this rule is described in the next section. + +### Consume Parameters + +Returning parameters in fresh result caps is safe if the actual argument to the parameter is not used afterwards. We can signal and enforce this pattern by adding a `consume` modifier to a parameter. With that new soft modifier, the following variant of `incr` is legal: +```scala +def incr(consume a: Ref^): Ref^ = + a.set(a.get + 1) + a +``` +Here, we increment the value of a reference and then return the same reference while enforcing the condition that the original reference cannot be used afterwards. Then the following is legal: +```scala +val a1 = Ref(1) +val a2 = incr(a1) +val a3 = incr(a2) +println(a3) +``` +Each reference `aᵢ` is unused after it is passed to `incr`. But the following continuation of that sequence would be in error: +```scala +val a4 = println(a2) // error +val a5 = incr(a1) // error +``` +In both of these assignments we use a capability that was consumed in an argument +of a previous application. + +Consume parameters enforce linear access to resources. This can be very useful. As an example, consider Scala's buffers such as `ListBuffer` or `ArrayBuffer`. We can treat these buffers as if they were purely functional, if we can enforce linear access. + +For instance, we can define a function `linearAdd` that adds elements to buffers in-place without violating referential transparency: +```scala +def linearAdd[T](consume buf: Buffer[T]^, elem: T): Buffer[T]^ = + buf += elem +``` +`linearAdd` returns a fresh buffer resulting from appending `elem` to `buf`. It overwrites `buf`, but that's OK since the `consume` modifier on `buf` ensures that the argument is not used after the call. + +### Consume Methods + +Buffers in Scala's standard library use a single-argument method `+=` instead of a two argument global function like `linearAdd`. We can enforce linearity in this case by adding the `consume` modifier to the method itself. +```scala +class Buffer[T] extends Mutable: + consume def +=(x: T): Buffer[T]^ = this // ok +``` +`consume` on a method implies `update`, so there's no need to label `+=` separately as an update method. Then we can write +```scala +val b = Buffer[Int]() += 1 += 2 +val c = b += 3 +// b cannot be used from here +``` +This code is equivalent to functional append with `+`, and is at the same time more efficient since it re-uses the storage of the argument buffer. + diff --git a/docs/_docs/reference/experimental/cc.md b/docs/_docs/reference/experimental/cc.md deleted file mode 100644 index 0fdcf143ae72..000000000000 --- a/docs/_docs/reference/experimental/cc.md +++ /dev/null @@ -1,887 +0,0 @@ ---- -layout: doc-page -title: "Capture Checking" -nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/cc.html ---- - -Capture checking is a research project that modifies the Scala type system to track references to capabilities in values. It can be enabled by the language import -```scala -import language.experimental.captureChecking -``` -At present, capture checking is still highly experimental and unstable, and it evolves quickly. -Before trying it out, make sure you have the latest version of Scala. - -To get an idea what capture checking can do, let's start with a small example: -```scala -def usingLogFile[T](op: FileOutputStream => T): T = - val logFile = FileOutputStream("log") - val result = op(logFile) - logFile.close() - result -``` -The `usingLogFile` method invokes a given operation with a fresh log file as parameter. Once the operation has ended, the log file is closed and the -operation's result is returned. This is a typical _try-with-resources_ pattern, similar to many other such patterns which are often supported by special language constructs in other languages. - -The problem is that `usingLogFile`'s implementation is not entirely safe. One can -undermine it by passing an operation that performs the logging at some later point -after it has terminated. For instance: -```scala -val later = usingLogFile { file => () => file.write(0) } -later() // crash -``` -When `later` is executed it tries to write to a file that is already closed, which -results in an uncaught `IOException`. - -Capture checking gives us the mechanism to prevent such errors _statically_. To -prevent unsafe usages of `usingLogFile`, we can declare it like this: -```scala -def usingLogFile[T](op: FileOutputStream^ => T): T = - // same body as before -``` -The only thing that's changed is that the `FileOutputStream` parameter of `op` is now -followed by `^`. We'll see that this turns the parameter into a _capability_ whose lifetime is tracked. - -If we now try to define the problematic value `later`, we get a static error: -``` - | val later = usingLogFile { f => () => f.write(0) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (f: java.io.FileOutputStream^'s1) ->'s2 () ->{f} Unit - | Required: java.io.FileOutputStream^ => () ->'s3 Unit - | - | Note that capability f cannot be included in outer capture set 's3. -``` -In this case, it was easy to see that the `logFile` capability escapes in the closure passed to `usingLogFile`. But capture checking also works for more complex cases. -For instance, capture checking is able to distinguish between the following safe code: -```scala -val xs = usingLogFile { f => - List(1, 2, 3).map { x => f.write(x); x * x } -} -``` -and the following unsafe one: -```scala -val xs = usingLogFile { f => - LzyList(1, 2, 3).map { x => f.write(x); x * x } -} -``` -An error would be issued in the second case, but not the first one (this assumes a capture-aware -formulation `LzyList` of lazily evaluated lists, which we will present later in this page). - -It turns out that capture checking has very broad applications. Besides the various -try-with-resources patterns, it can also be a key part to the solutions of many other long standing problems in programming languages. Among them: - - - How to have a simple and flexible system for checked exceptions. We show later - how capture checking enables a clean and fully safe system for checked exceptions in Scala. - - How to address the problem of effect polymorphism in general. - - How to solve the "what color is your function?" problem of mixing synchronous - and asynchronous computations. - - How to do region-based allocation, safely, - - How to reason about capabilities associated with memory locations. - -The following sections explain in detail how capture checking works in Scala 3. - - -## Overview - -The capture checker extension introduces a new kind of types and it enforces some rules for working with these types. - -## Capabilities and Capturing Types - -Capture checking is done in terms of _capturing types_ of the form -`T^{c₁, ..., cᵢ}`. Here `T` is a type, and `{c₁, ..., cᵢ}` is a _capture set_ consisting of references to capabilities `c₁, ..., cᵢ`. - -An _object capability_ is syntactically a method- or class-parameter, a local variable, or the `this` of an enclosing class. The type of a capability -must be a capturing type with a non-empty capture set. We also say that -variables that are capabilities are _tracked_. - -In a sense, every -capability gets its authority from some other, more sweeping capability which it captures. The recursion stops with a _universal capability_, written `cap`, from which all other capabilities are ultimately derived. -If `T` is a type, then `T^` is a shorthand for `T^{cap}`, meaning `T` can capture arbitrary capabilities. - -Here is an example: -```scala -class FileSystem - -class Logger(fs: FileSystem^): - def log(s: String): Unit = ... // Write to a log file, using `fs` - -def test(fs: FileSystem^) = - val l: Logger^{fs} = Logger(fs) - l.log("hello world!") - val xs: LzyList[Int]^{l} = - LzyList.from(1) - .map { i => - l.log(s"computing elem # $i") - i * i - } - xs -``` -Here, the `test` method takes a `FileSystem` as a parameter. `fs` is a capability since its type has a non-empty capture set. The capability is passed to the `Logger` constructor -and retained as a field in class `Logger`. Hence, the local variable `l` has type -`Logger^{fs}`: it is a `Logger` which retains the `fs` capability. - -The second variable defined in `test` is `xs`, a lazy list that is obtained from -`LzyList.from(1)` by logging and mapping consecutive numbers. Since the list is lazy, -it needs to retain the reference to the logger `l` for its computations. Hence, the -type of the list is `LzyList[Int]^{l}`. On the other hand, since `xs` only logs but does -not do other file operations, it retains the `fs` capability only indirectly. That's why -`fs` does not show up in the capture set of `xs`. - -Capturing types come with a subtype relation where types with "smaller" capture sets are subtypes of types with larger sets (the _subcapturing_ relation is defined in more detail below). If a type `T` does not have a capture set, it is called _pure_, and is a subtype of -any capturing type that adds a capture set to `T`. - -## Function Types - -The usual function type `A => B` now stands for a function that can capture arbitrary capabilities. We call such functions -_impure_. By contrast, the new single arrow function type `A -> B` stands for a function that cannot capture any capabilities, or otherwise said, is _pure_. -One can add a capture set after the arrow of an otherwise pure function. -For instance, `A ->{c, d} B` would be a function that can capture capabilities `c` and `d`, but no others. -This type is a shorthand for `(A -> B)^{c, d}`, i.e. the function type `A -> B` with possible captures `{c, d}`. - -The impure function type `A => B` is treated as an alias for `A ->{cap} B`. That is, impure functions are functions that can capture anything. - -A capture annotation `^` binds more strongly than a function arrow. So -`A -> B^{c}` is read as `A -> (B^{c})` and `A -> B^` is read as `A -> (B^{cap})`. - -Analogous conventions apply to context function types. `A ?=> B` is an impure context function, with `A ?-> B` as its pure complement. - -**Note 1:** The identifiers `->` and `?->` are now treated as soft keywords when used as infix type operators. They are -still available as regular identifiers for terms. For instance, the mapping syntax `Map("x" -> 1, "y" -> 2)` is still supported since it only applies to terms. - -**Note 2:** The distinctions between pure vs impure function types do not apply to methods. In fact, since methods are not values they never capture anything directly. References to -capabilities in a method are instead counted in the capture set of the enclosing object. - -## By-Name Parameter Types - -A convention analogous to function types also extends to by-name parameters. In -```scala -def f(x: => Int): Int -``` -the actual argument can refer to arbitrary capabilities. So the following would be OK: -```scala -f(if p(y) then throw Ex() else 1) -``` -On the other hand, if `f` was defined like this -```scala -def f(x: -> Int): Int -``` -the actual argument to `f` could not refer to any capabilities, so the call above would be rejected. -One can also allow specific capabilities like this: -```scala -def f(x: ->{c} Int): Int -``` -Here, the actual argument to `f` is allowed to use the `c` capability but no others. - -## Subtyping and Subcapturing - -Capturing influences subtyping. As usual we write `T₁ <: T₂` to express that the type -`T₁` is a subtype of the type `T₂`, or equivalently, that `T₁` conforms to `T₂`. An -analogous _subcapturing_ relation applies to capture sets. If `C₁` and `C₂` are capture sets, we write `C₁ <: C₂` to express that `C₁` _is covered by_ `C₂`, or, swapping the operands, that `C₂` _covers_ `C₁`. - -Subtyping extends as follows to capturing types: - - - Pure types are subtypes of capturing types. That is, `T <: C T`, for any type `T`, capturing set `C`. - - For capturing types, smaller capturing sets produce subtypes: `C₁ T₁ <: C₂ T₂` if - `C₁ <: C₂` and `T₁ <: T₂`. - -A subcapturing relation `C₁ <: C₂` holds if `C₂` _accounts for_ every element `c` in `C₁`. This means one of the following three conditions must be true: - - - `c ∈ C₂`, - - `c` refers to a parameter of some class `Cls` and `C₂` contains `Cls.this`, - - `c`'s type has capturing set `C` and `C₂` accounts for every element of `C` (that is, `C <: C₂`). - - -**Example 1.** Given -```scala -fs: FileSystem^ -ct: CanThrow[Exception]^ -l : Logger^{fs} -``` -we have -``` -{l} <: {fs} <: {cap} -{fs} <: {fs, ct} <: {cap} -{ct} <: {fs, ct} <: {cap} -``` -The set consisting of the root capability `{cap}` covers every other capture set. This is -a consequence of the fact that, ultimately, every capability is created from `cap`. - -**Example 2.** Consider again the FileSystem/Logger example from before. `LzyList[Int]` is a proper subtype of `LzyList[Int]^{l}`. So if the `test` method in that example -was declared with a result type `LzyList[Int]`, we'd get a type error. Here is the error message: -``` -11 |def test(using fs: FileSystem^): LzyList[Int] = { - | ^ - | Found: LzyList[Int]^{fs} - | Required: LzyList[Int] -``` -Why does it say `LzyList[Int]^{fs}` and not `LzyList[Int]^{l}`, which is, after all, the type of the returned value `xs`? The reason is that `l` is a local variable in the body of `test`, so it cannot be referred to in a type outside that body. What happens instead is that the type is _widened_ to the smallest supertype that does not mention `l`. Since `l` has capture set `fs`, we have that `{fs}` covers `{l}`, and `{fs}` is acceptable in a result type of `test`, so `{fs}` is the result of that widening. -This widening is called _avoidance_; it is not specific to capture checking but applies to all variable references in Scala types. - -## Capability Classes - -Classes like `CanThrow` or `FileSystem` have the property that their values are always intended to be capabilities. We can make this intention explicit and save boilerplate by letting these classes extend the `Capability` class defined in object `cap`. - -The capture set of a `Capability` subclass type is always `{cap}`. This means we could equivalently express the `FileSystem` and `Logger` classes as follows: -```scala -import caps.Capability - -class FileSystem extends Capability - -class Logger(using FileSystem): - def log(s: String): Unit = ??? - -def test(using fs: FileSystem) = - val l: Logger^{fs} = Logger() - ... -``` -In this version, `FileSystem` is a capability class, which means that the `{cap}` capture set is implied on the parameters of `Logger` and `test`. Writing the capture set explicitly produces a warning: -```scala -class Logger(using FileSystem^{cap}): - ^^^^^^^^^^^^^^ - redundant capture: FileSystem already accounts for cap -``` -Another, unrelated change in the version of the last example here is that the `FileSystem` capability is now passed as an implicit parameter. It is quite natural to model capabilities with implicit parameters since it greatly reduces the wiring overhead once multiple capabilities are in play. - -## Capture Checking of Closures - -If a closure refers to capabilities in its body, it captures these capabilities in its type. For instance, consider: -```scala -def test(fs: FileSystem): String ->{fs} Unit = - (x: String) => Logger(fs).log(x) -``` -Here, the body of `test` is a lambda that refers to the capability `fs`, which means that `fs` is retained in the lambda. -Consequently, the type of the lambda is `String ->{fs} Unit`. - -**Note:** Function values are always written with `=>` (or `?=>` for context functions). There is no syntactic -distinction for pure _vs_ impure function values. The distinction is only made in their types. - -A closure also captures all capabilities that are captured by the functions -it calls. For instance, in -```scala -def test(fs: FileSystem) = - def f() = g() - def g() = (x: String) => Logger(fs).log(x) - f -``` -the result of `test` has type `String ->{fs} Unit` even though function `f` itself does not refer to `fs`. - -## Capture Checking of Classes - -The principles for capture checking closures also apply to classes. For instance, consider: -```scala -class Logger(using fs: FileSystem): - def log(s: String): Unit = ... summon[FileSystem] ... - -def test(xfs: FileSystem): Logger^{xfs} = - Logger(xfs) -``` -Here, class `Logger` retains the capability `fs` as a (private) field. Hence, the result -of `test` is of type `Logger^{xfs}` - -Sometimes, a tracked capability is meant to be used only in the constructor of a class, but -is not intended to be retained as a field. This fact can be communicated to the capture -checker by declaring the parameter as `@constructorOnly`. Example: -```scala -import annotation.constructorOnly - -class NullLogger(using @constructorOnly fs: FileSystem): - ... -def test2(using fs: FileSystem): NullLogger = NullLogger() // OK -``` - -The captured references of a class include _local capabilities_ and _argument capabilities_. Local capabilities are capabilities defined outside the class and referenced from its body. Argument capabilities are passed as parameters to the primary constructor of the class. Local capabilities are inherited: -the local capabilities of a superclass are also local capabilities of its subclasses. Example: - -```scala -class Cap extends caps.Capability - -def test(a: Cap, b: Cap, c: Cap) = - class Super(y: Cap): - def f = a - class Sub(x: Cap) extends Super(x) - def g = b - Sub(c) -``` -Here class `Super` has local capability `a`, which gets inherited by class -`Sub` and is combined with `Sub`'s own local capability `b`. Class `Sub` also has an argument capability corresponding to its parameter `x`. This capability gets instantiated to `c` in the final constructor call `Sub(c)`. Hence, -the capture set of that call is `{a, b, c}`. - -The capture set of the type of `this` of a class is inferred by the capture checker, unless the type is explicitly declared with a self type annotation like this one: -```scala -class C: - self: D^{a, b} => ... -``` -The inference observes the following constraints: - - - The type of `this` of a class `C` includes all captured references of `C`. - - The type of `this` of a class `C` is a subtype of the type of `this` - of each parent class of `C`. - - The type of `this` must observe all constraints where `this` is used. - -For instance, in -```scala -class Cap extends caps.Capability -def test(c: Cap) = - class A: - val x: A = this - def f = println(c) // error -``` -we know that the type of `this` must be pure, since `this` is the right hand side of a `val` with type `A`. However, in the last line we find that the capture set of the class, and with it the capture set of `this`, would include `c`. This leads to a contradiction, and hence to a checking error: -``` -16 | def f = println(c) // error - | ^ - |(c : Cap) cannot be referenced here; it is not included in the allowed capture set {} -``` - -## Capture Tunnelling - -Consider the following simple definition of a `Pair` class: -```scala -class Pair[+A, +B](x: A, y: B): - def fst: A = x - def snd: B = y -``` -What happens if `Pair` is instantiated like this (assuming `ct` and `fs` are two capabilities in scope)? -```scala -def x: Int ->{ct} String -def y: Logger^{fs} -def p = Pair(x, y) -``` -The last line will be typed as follows: -```scala -def p: Pair[Int ->{ct} String, Logger^{fs}] = Pair(x, y) -``` -This might seem surprising. The `Pair(x, y)` value does capture capabilities `ct` and `fs`. Why don't they show up in its type at the outside? - -The answer is capture tunnelling. Once a type variable is instantiated to a capturing type, the -capture is not propagated beyond this point. On the other hand, if the type variable is instantiated -again on access, the capture information "pops out" again. For instance, even though `p` is technically pure because its capture set is empty, writing `p.fst` would record a reference to the captured capability `ct`. So if this access was put in a closure, the capability would again form part of the outer capture set. E.g. -```scala -() => p.fst : () -> Int ->{ct} String -``` -In other words, references to capabilities "tunnel through" in generic instantiations from creation to access; they do not affect the capture set of the enclosing generic data constructor applications. -This principle plays an important part in making capture checking concise and practical. - -## Escape Checking - - -Capabilities follow the usual scoping discipline, which means that capture sets -can contain only capabilities that are visible at the point where the set is defined. - -We now reconstruct how this principle produced the error in the introductory example, where -`usingLogFile` was declared like this: -```scala -def usingLogFile[T](op: FileOutputStream^ => T): T = ... -``` -The error message was: -``` - | val later = usingLogFile { f => () => f.write(0) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: (f: java.io.FileOutputStream^'s1) ->'s2 () ->{f} Unit - | Required: java.io.FileOutputStream^ => () ->'s3 Unit - | - | Note that capability f cannot be included in outer capture set 's3. -``` -This error message was produced by the following logic: - - - The `f` parameter has type `FileOutputStream^`, which makes it a capability. - - Therefore, the type of the expression `() => f.write(0)` is `() ->{f} Unit`. - - This makes the type of the whole closure passed to `usingLogFile` the dependent function type - `(f: FileOutputStream^'s1) ->'s2 () ->{f} Unit`, - for some as yet uncomputed capture sets `'s1` and `'s2`. - - The expected type of the closure is a simple, parametric, impure function type `FileOutputStream^ => T`, - for some instantiation of the type variable `T`. - - Matching with the found type, `T` must have the shape `() ->'s3 Unit`, for - some capture set `'s3` defined at the level of value `later`. - - That capture set cannot include the capability `f` since `f` is locally bound. - This causes the error. - -An analogous restriction applies to the type of a mutable variable. -Another way one could try to undermine capture checking would be to -assign a closure with a local capability to a global variable. Maybe -like this: -```scala -var loophole: () => Unit = () => () -usingLogFile { f => - loophole = () => f.write(0) -} -loophole() -``` -But this will not compile either, since the capture set of the mutable variable `loophole` cannot refer to variable `f`, which is not visible -where `loophole` is defined. - -Looking at object graphs, we observe a monotonicity property: The capture set of an object `x` covers the capture sets of all objects reachable through `x`. This property is reflected in the type system by the following _monotonicity rule_: - - - In a class `C` with a field `f`, the capture set `{this}` covers the capture set `{this.f}` as well as the capture set of any application of `this.f` to pure arguments. - -## Checked Exceptions - -Scala enables checked exceptions through a language import. Here is an example, -taken from the [safer exceptions page](./canthrow.md), and also described in a -[paper](https://infoscience.epfl.ch/record/290885) presented at the - 2021 Scala Symposium. -```scala -import language.experimental.saferExceptions - -class LimitExceeded extends Exception - -val limit = 10e+10 -def f(x: Double): Double throws LimitExceeded = - if x < limit then x * x else throw LimitExceeded() -``` -The new `throws` clause expands into an implicit parameter that provides -a `CanThrow` capability. Hence, function `f` could equivalently be written -like this: -```scala -def f(x: Double)(using CanThrow[LimitExceeded]): Double = ... -``` -If the implicit parameter is missing, an error is reported. For instance, the function definition -```scala -def g(x: Double): Double = - if x < limit then x * x else throw LimitExceeded() -``` -is rejected with this error message: -``` - | if x < limit then x * x else throw LimitExceeded() - | ^^^^^^^^^^^^^^^^^^^^^ - |The capability to throw exception LimitExceeded is missing. - |The capability can be provided by one of the following: - | - Adding a using clause `(using CanThrow[LimitExceeded])` to the definition of the enclosing method - | - Adding `throws LimitExceeded` clause after the result type of the enclosing method - | - Wrapping this piece of code with a `try` block that catches LimitExceeded -``` -`CanThrow` capabilities are required by `throw` expressions and are created -by `try` expressions. For instance, the expression -```scala -try xs.map(f).sum -catch case ex: LimitExceeded => -1 -``` -would be expanded by the compiler to something like the following: -```scala -try - erased given ctl: CanThrow[LimitExceeded] = compiletime.erasedValue - xs.map(f).sum -catch case ex: LimitExceeded => -1 -``` -(The `ctl` capability is only used for type checking but need not show up in the generated code, so it can be declared as -erased.) - -As with other capability based schemes, one needs to guard against capabilities -that are captured in results. For instance, here is a problematic use case: -```scala -def escaped(xs: Double*): (() => Double) throws LimitExceeded = - try () => xs.map(f).sum - catch case ex: LimitExceeded => () => -1 -val crasher = escaped(1, 2, 10e+11) -crasher() -``` -This code needs to be rejected since otherwise the call to `crasher()` would cause -an unhandled `LimitExceeded` exception to be thrown. - -Under the language import `language.experimental.captureChecking`, the code is indeed rejected -``` -14 | try () => xs.map(f).sum - | ^ - |The expression's type () => Double is not allowed to capture the root capability `cap`. - |This usually means that a capability persists longer than its allowed lifetime. -15 | catch case ex: LimitExceeded => () => -1 -``` -To integrate exception and capture checking, only two changes are needed: - - - `CanThrow` is declared as a class extending `Capability`, so all references to `CanThrow` instances are tracked. - - Escape checking is extended to `try` expressions. The result type of a `try` is not allowed to - capture the universal capability. - -## A Larger Example - -As a larger example, we present an implementation of lazy lists and some use cases. For simplicity, -our lists are lazy only in their tail part. This corresponds to what the Scala-2 type `Stream` did, whereas Scala 3's `LazyList` type computes strictly less since it is also lazy in the first argument. - -Here is the base trait `LzyList` for our version of lazy lists: -```scala -trait LzyList[+A]: - def isEmpty: Boolean - def head: A - def tail: LzyList[A]^{this} -``` -Note that `tail` carries a capture annotation. It says that the tail of a lazy list can -potentially capture the same references as the lazy list as a whole. - -The empty case of a `LzyList` is written as usual: -```scala -object LzyNil extends LzyList[Nothing]: - def isEmpty = true - def head = ??? - def tail = ??? -``` -Here is a formulation of the class for lazy cons nodes: -```scala -import scala.compiletime.uninitialized - -final class LzyCons[+A](hd: A, tl: () => LzyList[A]^) extends LzyList[A]: - private var forced = false - private var cache: LzyList[A]^{this} = uninitialized - private def force = - if !forced then { cache = tl(); forced = true } - cache - - def isEmpty = false - def head = hd - def tail: LzyList[A]^{this} = force -end LzyCons -``` -The `LzyCons` class takes two parameters: A head `hd` and a tail `tl`, which is a function -returning a `LzyList`. Both the function and its result can capture arbitrary capabilities. -The result of applying the function is memoized after the first dereference of `tail` in -the private mutable field `cache`. Note that the typing of the assignment `cache = tl()` relies on the monotonicity rule for `{this}` capture sets. - -Here is an extension method to define an infix cons operator `#:` for lazy lists. It is analogous -to `::` but instead of a strict list it produces a lazy list without evaluating its right operand. -```scala -extension [A](x: A) - def #:(xs1: => LzyList[A]^): LzyList[A]^{xs1} = - LzyCons(x, () => xs1) -``` -Note that `#:` takes an impure call-by-name parameter `xs1` as its right argument. The result -of `#:` is a lazy list that captures that argument. - -As an example usage of `#:`, here is a method `tabulate` that creates a lazy list -of given length with a generator function `gen`. The generator function is allowed -to have side effects. -```scala -def tabulate[A](n: Int)(gen: Int => A) = - def recur(i: Int): LzyList[A]^{gen} = - if i == n then LzyNil - else gen(i) #: recur(i + 1) - recur(0) -``` -Here is a use of `tabulate`: -```scala -class LimitExceeded extends Exception -def squares(n: Int)(using ct: CanThrow[LimitExceeded]) = - tabulate(10): i => - if i > 9 then throw LimitExceeded() - i * i -``` -The inferred result type of `squares` is `LzyList[Int]^{ct}`, i.e it is a lazy list of -`Int`s that can throw the `LimitExceeded` exception when it is elaborated by calling `tail` -one or more times. - -Here are some further extension methods for mapping, filtering, and concatenating lazy lists: -```scala -extension [A](xs: LzyList[A]^) - def map[B](f: A => B): LzyList[B]^{xs, f} = - if xs.isEmpty then LzyNil - else f(xs.head) #: xs.tail.map(f) - - def filter(p: A => Boolean): LzyList[A]^{xs, p} = - if xs.isEmpty then LzyNil - else if p(xs.head) then xs.head #: xs.tail.filter(p) - else xs.tail.filter(p) - - def concat(ys: LzyList[A]^): LzyList[A]^{xs, ys} = - if xs.isEmpty then ys - else xs.head #: xs.tail.concat(ys) - - def drop(n: Int): LzyList[A]^{xs} = - if n == 0 then xs else xs.tail.drop(n - 1) -``` -Their capture annotations are all as one would expect: - - - Mapping a lazy list produces a lazy list that captures the original list as well - as the (possibly impure) mapping function. - - Filtering a lazy list produces a lazy list that captures the original list as well - as the (possibly impure) filtering predicate. - - Concatenating two lazy lists produces a lazy list that captures both arguments. - - Dropping elements from a lazy list gives a safe approximation where the original list is captured in the result. In fact, it's only some suffix of the list that is retained at run time, but our modelling identifies lazy lists and their suffixes, so this additional knowledge would not be useful. - -Of course the function passed to `map` or `filter` could also be pure. After all, `A -> B` is a subtype of `(A -> B)^{cap}` which is the same as `A => B`. In that case, the pure function -argument will _not_ show up in the result type of `map` or `filter`. For instance: -```scala -val xs = squares(10) -val ys: LzyList[Int]^{xs} = xs.map(_ + 1) -``` -The type of the mapped list `ys` has only `xs` in its capture set. The actual function -argument does not show up since it is pure. Likewise, if the lazy list -`xs` was pure, it would not show up in any of the method results. -This demonstrates that capability-based -effect systems with capture checking are naturally _effect polymorphic_. - -This concludes our example. It's worth mentioning that an equivalent program defining and using standard, strict lists would require no capture annotations whatsoever. It would compile exactly as written now in standard Scala 3, yet one gets the capture checking for free. Essentially, `=>` already means "can capture anything" and since in a strict list side effecting operations are not retained in the result, there are no additional captures to record. A strict list could of course capture side-effecting closures in its elements but then tunnelling applies, since -these elements are represented by a type variable. This means we don't need to annotate anything there either. - -Another possibility would be a variant of lazy lists that requires all functions passed to `map`, `filter` and other operations like it to be pure. E.g. `map` on such a list would be defined like this: -```scala -extension [A](xs: LzyList[A]) - def map[B](f: A -> B): LzyList[B] = ... -``` -That variant would not require any capture annotations either. - -To summarize, there are two "sweet spots" of data structure design: strict lists in -side-effecting or resource-aware code and lazy lists in purely functional code. -Both are already correctly capture-typed without requiring any explicit annotations. Capture annotations only come into play where the semantics gets more complicated because we deal with delayed effects such as in impure lazy lists or side-effecting iterators over strict lists. This property is probably one of the greatest plus points of our approach to capture checking compared to previous techniques which tend to be more noisy. - -## Existential Capabilities - -In fact, what is written as the top type `cap` can mean different capabilities, depending on scope. For instance, consider the function type -`() -> Iterator[T]^`. This is taken to mean -```scala - () -> Exists x. Iterator[T]^x -``` -In other words, it means an unknown type bound `x` by an "existential" in the scope of the function result. A `cap` in a function result is therefore different from a `cap` at the top-level or in a function parameter. - -Internally, an existential type is represented as a kind of dependent function type. The type above would be modelled as -```scala - () -> (x: Exists) -> Iterator[T]^x -``` -Here, `Exists` is a sealed trait in the `caps` object that serves to mark -dependent functions as representations of existentials. It should be noted -that this is strictly an internal representation. It is explained here because it can show up in error messages. It is generally not recommended to use this syntax in source code. Instead one should rely on the automatic expansion of `^` and `cap` to existentials, which can be -influenced by introducing the right alias types. The rules for this expansion are as follows: - - - If a function result type contains covariant occurrences of `cap`, - we replace these occurrences with a fresh existential variable which - is bound by a quantifier scoping over the result type. - - We might want to do the same expansion in function arguments, but right now this is not done. - - Occurrences of `cap` elsewhere are not translated. They can be seen as representing an existential at the top-level scope. - -**Examples:** - - - `A => B` is an alias type that expands to `(A -> B)^`, therefore - `() -> A => B` expands to `() -> Exists c. A ->{c} B`. - - - `() -> Iterator[A => B]` expands to `() -> Exists c. Iterator[A ->{c} B]` - - - `A -> B^` expands to `A -> Exists c.B^{c}`. - - - If we define `type Fun[T] = A -> T`, then `() -> Fun[B^]` expands to `() -> Exists c.Fun[B^{c}]`, which dealiases to `() -> Exists c.A -> B^{c}`. This demonstrates how aliases can be used to force existential binders to be in some specific outer scope. - - - If we define - ```scala - type F = A -> Fun[B^] - ``` - then the type alias expands to - ```scala - type F = A -> Exists c.A -> B^{c} - ``` - -**Typing Rules:** - - - When we typecheck the body of a function or method, any covariant occurrences of `cap` in the result type are bound with a fresh existential. - - Conversely, when we typecheck the application of a function or method, - with an existential result type `Exists ex.T`, the result of the application is `T` where every occurrence of the existentially bound - variable `ex` is replaced by `cap`. - -## Reach Capabilities - -Say you have a method `f` that takes an impure function argument which gets stored in a `var`: -```scala -def f(op: A => B) - var x: A ->{op} B = op - ... -``` -This is legal even though `var`s cannot have types with `cap` or existential capabilities. The trick is that the type of the variable `x` -is not `A => B` (this would be rejected), but is the "narrowed" type -`A ->{op} B`. In other words, all capabilities retained by values of `x` -are all also referred to by `op`, which justifies the replacement of `cap` by `op`. - -A more complicated situation is if we want to store successive values -held in a list. Example: -```scala -def f(ops: List[A => B]) - var xs = ops - var x: ??? = xs.head - while xs.nonEmpty do - xs = xs.tail - x = xs.head - ... -``` -Here, `x` cannot be given a type with an `ops` capability. In fact, `ops` is pure, i.e. it's capture set is empty, so it cannot be used as the name of a capability. What we would like to express is that `x` refers to -any operation "reachable" through `ops`. This can be expressed using a -_reach capability_ `ops*`. -```scala -def f(ops: List[A => B]) - var xs = ops - var x: A ->{ops*} B = xs.head - ... -``` -Reach capabilities take the form `x*` where `x` is syntactically a regular capability. If `x: T` then `x*` stands for any capability that appears covariantly in `T` and that is accessed through `x`. The least supertype of this capability is the set of all capabilities appearing covariantly in `T`. - -## Capability Polymorphism - -It is sometimes convenient to write operations that are parameterized with a capture set of capabilities. For instance consider a type of event sources -`Source` on which `Listener`s can be registered. Listeners can hold certain capabilities, which show up as a parameter to `Source`: -```scala -class Source[X^]: - private var listeners: Set[Listener^{X}] = Set.empty - def register(x: Listener^{X}): Unit = - listeners += x - - def allListeners: Set[Listener^{X}] = listeners -``` -The type variable `X^` can be instantiated with a set of capabilities. It can occur in capture sets in its scope. For instance, in the example above -we see a variable `listeners` that has as type a `Set` of `Listeners` capturing `X`. The `register` method takes a listener of this type -and assigns it to the variable. - -Capture-set variables `X^` without user-annotated bounds by default range over the interval `>: {} <: {caps.cap}` which is the universe of capture sets instead of regular types. - -Under the hood, such capture-set variables are represented as regular type variables within the special interval - `>: CapSet <: CapSet^`. -For instance, `Source` from above could be equivalently -defined as follows: -```scala -class Source[X >: CapSet <: CapSet^]: - ... -``` -`CapSet` is a sealed trait in the `caps` object. It cannot be instantiated or inherited, so its only -purpose is to identify type variables which are capture sets. In non-capture-checked -usage contexts, the type system will treat `CapSet^{a}` and `CapSet^{a,b}` as the type `CapSet`, whereas -with capture checking enabled, it will take the annotated capture sets into account, -so that `CapSet^{a}` and `CapSet^{a,b}` are distinct. -This representation based on `CapSet` is subject to change and -its direct use is discouraged. - -Capture-set variables can be inferred like regular type variables. When they should be instantiated -explicitly one supplies a concrete capture set. For instance: -```scala -class Async extends caps.Capability - -def listener(async: Async): Listener^{async} = ??? - -def test1(async1: Async, others: List[Async]) = - val src = Source[{async1, others*}] - ... -``` -Here, `src` is created as a `Source` on which listeners can be registered that refer to the `async` capability or to any of the capabilities in list `others`. So we can continue the example code above as follows: -```scala - src.register(listener(async1)) - others.map(listener).foreach(src.register) - val ls: Set[Listener^{async, others*}] = src.allListeners -``` -A common use-case for explicit capture parameters is describing changes to the captures of mutable fields, such as concatenating -effectful iterators: -```scala -class ConcatIterator[A, C^](var iterators: mutable.List[IterableOnce[A]^{C}]): - def concat(it: IterableOnce[A]^): ConcatIterator[A, {C, it}]^{this, it} = - iterators ++= it // ^ - this // track contents of `it` in the result -``` -In such a scenario, we also should ensure that any pre-existing alias of a `ConcatIterator` object should become -inaccessible after invoking its `concat` method. This is achieved with mutation and separation tracking which are -currently in development. - - -## Capability Members - -Just as parametrization by types can be equally expressed with type members, we could -also define the `Source[X^]` class above could using a _capability member_: -```scala -class Source: - type X^ - private var listeners: Set[Listener^{this.X}] = Set.empty - ... // as before -``` -Here, we can refer to capability members using paths in capture sets (such as `{this.X}`). Similarly to type members, -capability members can be upper- and lower-bounded with capture sets: -```scala -trait Thread: - type Cap^ - def run(block: () ->{this.Cap} -> Unit): Unit - -trait GPUThread extends Thread: - type Cap^ >: {cudaMalloc, cudaFree} <: {caps.cap} -``` -Since `caps.cap` is the top element for subcapturing, we could have also left out the -upper bound: `type Cap^ >: {cudaMalloc, cudaFree}`. - - -[More Advanced Use Cases](cc-advanced.md) - -## Compilation Options - -The following options are relevant for capture checking. - - - **-Vprint:cc** Prints the program with capturing types as inferred by capture checking. - - **-Ycc-debug** Gives more detailed, implementation-oriented information about capture checking, as described in the next section. - - The implementation supporting capture checking with these options is currently in branch `cc-experiment` on dotty.epfl.ch. - -## Capture Checking Internals - -The capture checker is architected as a propagation constraint solver, which runs as a separate phase after type-checking and some initial transformations. - -Constraint variables stand for unknown capture sets. A constraint variable is introduced - - - for every part of a previously inferred type, - - for the accessed references of every method, class, anonymous function, or by-name argument, - - for the parameters passed in a class constructor call. - -Capture sets in explicitly written types are treated as constants (before capture checking, such sets are simply ignored). - -The capture checker essentially rechecks the program with the usual typing rules. Every time a subtype requirement between capturing types is checked, this translates to a subcapturing test on capture sets. If the two sets are constant, this is simply a yes/no question, where a no will produce an error message. - -If the lower set `C₁` of a comparison `C₁ <: C₂` is a variable, the set `C₂` is recorded -as a _superset_ of `C₁`. If the upper set `C₂` is a variable, the elements of `C₁` are _propagated_ to `C₂`. Propagation of an element `x` to a set `C` means that `x` is included as an element in `C`, and it is also propagated -to all known supersets of `C`. If such a superset is a constant, it is checked that `x` is included in it. If that's not the case, the original comparison `C₁ <: C₂` has no solution and an error is reported. - -The type checker also performs various maps on types, for instance when substituting actual argument types for formal parameter types in dependent functions, or mapping -member types with "as-seen-from" in a selection. Maps keep track of the variance -of positions in a type. The variance is initially covariant, it flips to -contravariant in function parameter positions, and can be either covariant, -contravariant, or nonvariant in type arguments, depending on the variance of -the type parameter. - -When capture checking, the same maps are also performed on capture sets. If a capture set is a constant, its elements (which are capabilities) are mapped as regular types. If the result of such a map is not a capability, the result is approximated according to the variance of the type. A covariant approximation replaces a type by its capture set. -A contravariant approximation replaces it with the empty capture set. A nonvariant -approximation replaces the enclosing capturing type with a range of possible types -that gets propagated and resolved further out. - -When a mapping `m` is performed on a capture set variable `C`, a new variable `Cm` is created that contains the mapped elements and that is linked with `C`. If `C` subsequently acquires further elements through propagation, these are also propagated to `Cm` after being transformed by the `m` mapping. `Cm` also gets the same supersets as `C`, mapped again using `m`. - -One interesting aspect of the capture checker concerns the implementation of capture tunnelling. The [foundational theory](https://infoscience.epfl.ch/record/290885) on which capture checking is based makes tunnelling explicit through so-called _box_ and -_unbox_ operations. Boxing hides a capture set and unboxing recovers it. The capture checker inserts virtual box and unbox operations based on actual and expected types similar to the way the type checker inserts implicit conversions. When capture set variables are first introduced, any capture set in a capturing type that is an instance of a type parameter instance is marked as "boxed". A boxing operation is -inserted if the expected type of an expression is a capturing type with -a boxed capture set variable. The effect of the insertion is that any references -to capabilities in the boxed expression are forgotten, which means that capture -propagation is stopped. Dually, if the actual type of an expression has -a boxed variable as capture set, an unbox operation is inserted, which adds all -elements of the capture set to the environment. - -Boxing and unboxing has no runtime effect, so the insertion of these operations is only simulated; the only visible effect is the retraction and insertion -of variables in the capture sets representing the environment of the currently checked expression. - -The `-Ycc-debug` option provides some insight into the workings of the capture checker. -When it is turned on, boxed sets are marked explicitly and capture set variables are printed with an ID and some information about their provenance. For instance, the string `{f, xs}33M5V` indicates a capture set -variable that is known to hold elements `f` and `xs`. The variable's ID is `33`. The `M` -indicates that the variable was created through a mapping from a variable with ID `5`. The latter is a regular variable, as indicated - by `V`. - -Generally, the string following the capture set consists of alternating numbers and letters where each number gives a variable ID and each letter gives the provenance of the variable. Possible letters are - - - `V` : a regular variable, - - `M` : a variable resulting from a _mapping_ of the variable indicated by the string to the right, - - `B` : similar to `M` but where the mapping is a _bijection_, - - `F` : a variable resulting from _filtering_ the elements of the variable indicated by the string to the right, - - `I` : a variable resulting from an _intersection_ of two capture sets, - - `D` : a variable resulting from the set _difference_ of two capture sets. - - `R` : a regular variable that _refines_ a class parameter, so that the capture - set of a constructor argument is known in the class instance type. - -At the end of a compilation run, `-Ycc-debug` will print all variable dependencies of variables referred to in previous output. Here is an example: -``` -Capture set dependencies: - {}2V :: - {}3V :: - {}4V :: - {f, xs}5V :: {f, xs}31M5V, {f, xs}32M5V - {f, xs}31M5V :: {xs, f} - {f, xs}32M5V :: -``` -This section lists all variables that appeared in previous diagnostics and their dependencies, recursively. For instance, we learn that - - - variables 2, 3, 4 are empty and have no dependencies, - - variable `5` has two dependencies: variables `31` and `32` which both result from mapping variable `5`, - - variable `31` has a constant fixed superset `{xs, f}` - - variable `32` has no dependencies. - - - diff --git a/docs/_docs/reference/experimental/overview.md b/docs/_docs/reference/experimental/overview.md index f70cf32b9c24..9242056d8405 100644 --- a/docs/_docs/reference/experimental/overview.md +++ b/docs/_docs/reference/experimental/overview.md @@ -32,4 +32,4 @@ Hence, dependent projects also have to be experimental. Some experimental language features that are still in research and development can be enabled with special compiler options. These include * [`-Yexplicit-nulls`](./explicit-nulls.md). Enable support for tracking null references in the type system. -* [`-Ycc`](./cc.md). Enable support for capture checking. +* [`-Ycc`](./capture-checking/cc.md). Enable support for capture checking. diff --git a/docs/sidebar.yml b/docs/sidebar.yml index 440cfa450849..144ad293eaea 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -162,9 +162,20 @@ subsection: - page: reference/experimental/explicit-nulls.md - page: reference/experimental/main-annotation.md - page: reference/experimental/into.md - - page: reference/experimental/cc.md - - page: reference/experimental/cc-advanced.md - hidden: true + - title: Capture Checking + index: reference/experimental/capture-checking/cc.md + subsection: + - page: reference/experimental/capture-checking/basics.md + - page: reference/experimental/capture-checking/classes.md + - page: reference/experimental/capture-checking/polymorphism.md + - page: reference/experimental/capture-checking/advanced.md + hidden: true + - page: reference/experimental/capture-checking/scoped-caps.md + - page: reference/experimental/capture-checking/classifiers.md + - page: reference/experimental/capture-checking/checked-exceptions.md + - page: reference/experimental/capture-checking/separation-checking.md + - page: reference/experimental/capture-checking/how-to-use.md + - page: reference/experimental/capture-checking/internals.md - page: reference/experimental/purefuns.md - page: reference/experimental/tupled-function.md - page: reference/experimental/modularity.md diff --git a/library/src/scala/Array.scala b/library/src/scala/Array.scala index 8daa1859cf44..bb23b79eb1a4 100644 --- a/library/src/scala/Array.scala +++ b/library/src/scala/Array.scala @@ -13,6 +13,7 @@ package scala import scala.language.`2.13` +import language.experimental.captureChecking //import scala.collection.generic._ import scala.collection.{Factory, immutable, mutable} @@ -49,7 +50,7 @@ object Array { implicit def toFactory[A : ClassTag](dummy: Array.type): Factory[A, Array[A]] = new ArrayFactory(dummy) @SerialVersionUID(3L) private class ArrayFactory[A : ClassTag](dummy: Array.type) extends Factory[A, Array[A]] with Serializable { - def fromSpecific(it: IterableOnce[A]): Array[A] = Array.from[A](it) + def fromSpecific(it: IterableOnce[A]^): Array[A] = Array.from[A](it) def newBuilder: mutable.Builder[A, Array[A]] = Array.newBuilder[A] } @@ -71,7 +72,7 @@ object Array { * @param it the iterable collection * @return an array consisting of elements of the iterable collection */ - def from[A : ClassTag](it: IterableOnce[A]): Array[A] = it match { + def from[A : ClassTag](it: IterableOnce[A]^): Array[A] = it match { case it: Iterable[A] => it.toArray[A] case _ => it.iterator.toArray[A] } @@ -659,7 +660,7 @@ object Array { * @define collectExample * @define undefinedorder */ -final class Array[T](_length: Int) extends java.io.Serializable with java.lang.Cloneable { +final class Array[T](_length: Int) extends java.io.Serializable with java.lang.Cloneable { self: Array[T] => /** The length of the array */ def length: Int = throw new Error() diff --git a/library/src/scala/Boolean.scala b/library/src/scala/Boolean.scala index 5ff37694d133..5df9184b5e30 100644 --- a/library/src/scala/Boolean.scala +++ b/library/src/scala/Boolean.scala @@ -114,7 +114,7 @@ final abstract class Boolean private extends AnyVal { object Boolean extends AnyValCompanion { - /** Transform a value type into a boxed reference type. + /** Transforms a value type into a boxed reference type. * * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToBoolean`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. * @@ -123,7 +123,7 @@ object Boolean extends AnyValCompanion { */ def box(x: Boolean): java.lang.Boolean = ??? - /** Transform a boxed type into a value type. Note that this + /** Transforms a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw * an exception if the argument is not a java.lang.Boolean. * diff --git a/library/src/scala/Byte.scala b/library/src/scala/Byte.scala index c5891be37e36..c1f0e81f746b 100644 --- a/library/src/scala/Byte.scala +++ b/library/src/scala/Byte.scala @@ -455,7 +455,7 @@ object Byte extends AnyValCompanion { /** The largest value representable as a Byte. */ final val MaxValue = java.lang.Byte.MAX_VALUE - /** Transform a value type into a boxed reference type. + /** Transforms a value type into a boxed reference type. * * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToByte`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. * @@ -464,7 +464,7 @@ object Byte extends AnyValCompanion { */ def box(x: Byte): java.lang.Byte = ??? - /** Transform a boxed type into a value type. Note that this + /** Transforms a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw * an exception if the argument is not a java.lang.Byte. * diff --git a/library/src/scala/CanEqual.scala b/library/src/scala/CanEqual.scala index 8c331bb21b43..12cfdfc6f5fd 100644 --- a/library/src/scala/CanEqual.scala +++ b/library/src/scala/CanEqual.scala @@ -1,5 +1,7 @@ package scala +import language.experimental.captureChecking + import annotation.implicitNotFound import scala.collection.{Seq, Set, Map} diff --git a/library/src/scala/CanThrow.scala b/library/src/scala/CanThrow.scala index d55d631235e7..0852ce804cd7 100644 --- a/library/src/scala/CanThrow.scala +++ b/library/src/scala/CanThrow.scala @@ -2,6 +2,8 @@ package scala import language.experimental.erasedDefinitions import annotation.{implicitNotFound, experimental, capability} +import language.experimental.captureChecking + /** A capability class that allows to throw exception `E`. When used with the * experimental.saferExceptions feature, a `throw Ex()` expression will require * a given of class `CanThrow[Ex]` to be available. diff --git a/library/src/scala/Char.scala b/library/src/scala/Char.scala index 500ffcb05412..06a6e186901b 100644 --- a/library/src/scala/Char.scala +++ b/library/src/scala/Char.scala @@ -455,7 +455,7 @@ object Char extends AnyValCompanion { /** The largest value representable as a Char. */ final val MaxValue = java.lang.Character.MAX_VALUE - /** Transform a value type into a boxed reference type. + /** Transforms a value type into a boxed reference type. * * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToCharacter`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. * @@ -464,7 +464,7 @@ object Char extends AnyValCompanion { */ def box(x: Char): java.lang.Character = ??? - /** Transform a boxed type into a value type. Note that this + /** Transforms a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw * an exception if the argument is not a java.lang.Character. * diff --git a/library/src/scala/Conversion.scala b/library/src/scala/Conversion.scala index cbae3448dab9..282566063b6e 100644 --- a/library/src/scala/Conversion.scala +++ b/library/src/scala/Conversion.scala @@ -1,5 +1,7 @@ package scala +import language.experimental.captureChecking + /** A class for implicit values that can serve as implicit conversions. * The implicit resolution algorithm will act as if there existed * the additional implicit definition: @@ -23,12 +25,13 @@ package scala */ @java.lang.FunctionalInterface abstract class Conversion[-T, +U] extends Function1[T, U]: - /** Convert value `x` of type `T` to type `U` */ - def apply(x: T): U + self => + /** Convert value `x` of type `T` to type `U` */ + def apply(x: T): U - extension (x: T) - /** `x.convert` converts a value `x` of type `T` to type `U` */ - def convert = this(x) + extension (x: T) + /** `x.convert` converts a value `x` of type `T` to type `U` */ + def convert = this(x) object Conversion: import annotation.experimental @@ -44,5 +47,4 @@ object Conversion: /** Unwrap an `into` */ extension [T](x: into[T]) @experimental def underlying: T = x - -end Conversion \ No newline at end of file +end Conversion diff --git a/library/src/scala/Double.scala b/library/src/scala/Double.scala index 08a91bf8c603..57e99999198b 100644 --- a/library/src/scala/Double.scala +++ b/library/src/scala/Double.scala @@ -230,7 +230,7 @@ object Double extends AnyValCompanion { /** The largest finite positive number representable as a Double. */ final val MaxValue = java.lang.Double.MAX_VALUE - /** Transform a value type into a boxed reference type. + /** Transforms a value type into a boxed reference type. * * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToDouble`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. * @@ -239,7 +239,7 @@ object Double extends AnyValCompanion { */ def box(x: Double): java.lang.Double = ??? - /** Transform a boxed type into a value type. Note that this + /** Transforms a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw * an exception if the argument is not a java.lang.Double. * diff --git a/library/src/scala/Float.scala b/library/src/scala/Float.scala index fae92d99e882..1f8b00886258 100644 --- a/library/src/scala/Float.scala +++ b/library/src/scala/Float.scala @@ -230,7 +230,7 @@ object Float extends AnyValCompanion { /** The largest finite positive number representable as a Float. */ final val MaxValue = java.lang.Float.MAX_VALUE - /** Transform a value type into a boxed reference type. + /** Transforms a value type into a boxed reference type. * * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToFloat`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. * @@ -239,7 +239,7 @@ object Float extends AnyValCompanion { */ def box(x: Float): java.lang.Float = ??? - /** Transform a boxed type into a value type. Note that this + /** Transforms a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw * an exception if the argument is not a java.lang.Float. * diff --git a/library/src/scala/Function0.scala b/library/src/scala/Function0.scala index af9327e6e88c..0dd571885a43 100644 --- a/library/src/scala/Function0.scala +++ b/library/src/scala/Function0.scala @@ -11,7 +11,7 @@ */ // GENERATED CODE: DO NOT EDIT. -// genprod generated these sources at: 2022-01-17T20:47:12.170348200Z +// genprod generated these sources at: 2025-08-11T16:44:44.712777821Z package scala @@ -36,7 +36,7 @@ import scala.language.`2.13` * } * }}} */ -trait Function0[@specialized(Specializable.Primitives) +R] extends AnyRef { self => +trait Function0[@specialized(Specializable.Primitives) +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ diff --git a/library/src/scala/Function1.scala b/library/src/scala/Function1.scala index b31ed60766d5..ece9d778aa7b 100644 --- a/library/src/scala/Function1.scala +++ b/library/src/scala/Function1.scala @@ -65,7 +65,7 @@ object Function1 { * is that the latter can specify inputs which it will not handle. */ @annotation.implicitNotFound(msg = "No implicit view available from ${T1} => ${R}.") -trait Function1[@specialized(Specializable.Arg) -T1, @specialized(Specializable.Return) +R] extends AnyRef { self => +trait Function1[@specialized(Specializable.Arg) -T1, @specialized(Specializable.Return) +R] extends AnyRef { /** Apply the body of this function to the argument. * @return the result of function application. */ diff --git a/library/src/scala/Function10.scala b/library/src/scala/Function10.scala index c362ea877732..823544865d54 100644 --- a/library/src/scala/Function10.scala +++ b/library/src/scala/Function10.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 10 parameters. * */ -trait Function10[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, +R] extends AnyRef { self => +trait Function10[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function10[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, +R] extends * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)).curried } /** Creates a tupled version of this function: instead of 10 arguments, * it accepts a single [[scala.Tuple10]] argument. diff --git a/library/src/scala/Function11.scala b/library/src/scala/Function11.scala index 1706470355c3..bd4b3fb394ad 100644 --- a/library/src/scala/Function11.scala +++ b/library/src/scala/Function11.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 11 parameters. * */ -trait Function11[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, +R] extends AnyRef { self => +trait Function11[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function11[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, +R] ex * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)).curried } /** Creates a tupled version of this function: instead of 11 arguments, * it accepts a single [[scala.Tuple11]] argument. diff --git a/library/src/scala/Function12.scala b/library/src/scala/Function12.scala index 5ccaa7722095..42c5ba6aa645 100644 --- a/library/src/scala/Function12.scala +++ b/library/src/scala/Function12.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 12 parameters. * */ -trait Function12[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, +R] extends AnyRef { self => +trait Function12[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function12[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12)).curried } /** Creates a tupled version of this function: instead of 12 arguments, * it accepts a single [[scala.Tuple12]] argument. diff --git a/library/src/scala/Function13.scala b/library/src/scala/Function13.scala index a92b6710bbfb..074c69b779c1 100644 --- a/library/src/scala/Function13.scala +++ b/library/src/scala/Function13.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 13 parameters. * */ -trait Function13[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, +R] extends AnyRef { self => +trait Function13[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function13[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13)).curried } /** Creates a tupled version of this function: instead of 13 arguments, * it accepts a single [[scala.Tuple13]] argument. diff --git a/library/src/scala/Function14.scala b/library/src/scala/Function14.scala index 687a3e693766..06332ea72f66 100644 --- a/library/src/scala/Function14.scala +++ b/library/src/scala/Function14.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 14 parameters. * */ -trait Function14[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, +R] extends AnyRef { self => +trait Function14[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function14[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14)).curried } /** Creates a tupled version of this function: instead of 14 arguments, * it accepts a single [[scala.Tuple14]] argument. diff --git a/library/src/scala/Function15.scala b/library/src/scala/Function15.scala index c45cae4e1a97..5b4c022bef76 100644 --- a/library/src/scala/Function15.scala +++ b/library/src/scala/Function15.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 15 parameters. * */ -trait Function15[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, +R] extends AnyRef { self => +trait Function15[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function15[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15)).curried } /** Creates a tupled version of this function: instead of 15 arguments, * it accepts a single [[scala.Tuple15]] argument. diff --git a/library/src/scala/Function16.scala b/library/src/scala/Function16.scala index 8795ccfbd546..937ee43ef768 100644 --- a/library/src/scala/Function16.scala +++ b/library/src/scala/Function16.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 16 parameters. * */ -trait Function16[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, +R] extends AnyRef { self => +trait Function16[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function16[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16)).curried } /** Creates a tupled version of this function: instead of 16 arguments, * it accepts a single [[scala.Tuple16]] argument. diff --git a/library/src/scala/Function17.scala b/library/src/scala/Function17.scala index 874ea3bcd9fc..4e054ba2833d 100644 --- a/library/src/scala/Function17.scala +++ b/library/src/scala/Function17.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 17 parameters. * */ -trait Function17[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, +R] extends AnyRef { self => +trait Function17[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function17[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16)(x17) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => T17 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17)).curried } /** Creates a tupled version of this function: instead of 17 arguments, * it accepts a single [[scala.Tuple17]] argument. diff --git a/library/src/scala/Function18.scala b/library/src/scala/Function18.scala index 4fa20a649416..cceb289ab6f9 100644 --- a/library/src/scala/Function18.scala +++ b/library/src/scala/Function18.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 18 parameters. * */ -trait Function18[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, +R] extends AnyRef { self => +trait Function18[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function18[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16)(x17)(x18) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => T17 => T18 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18)).curried } /** Creates a tupled version of this function: instead of 18 arguments, * it accepts a single [[scala.Tuple18]] argument. diff --git a/library/src/scala/Function19.scala b/library/src/scala/Function19.scala index c59e32b4ae40..88a3dd10d9be 100644 --- a/library/src/scala/Function19.scala +++ b/library/src/scala/Function19.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 19 parameters. * */ -trait Function19[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, +R] extends AnyRef { self => +trait Function19[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function19[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16)(x17)(x18)(x19) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => T17 => T18 => T19 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18, x19: T19) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18, x19: T19) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19)).curried } /** Creates a tupled version of this function: instead of 19 arguments, * it accepts a single [[scala.Tuple19]] argument. diff --git a/library/src/scala/Function2.scala b/library/src/scala/Function2.scala index ccb066fb9f3a..ecb760e4e39c 100644 --- a/library/src/scala/Function2.scala +++ b/library/src/scala/Function2.scala @@ -34,7 +34,7 @@ import scala.language.`2.13` * } * }}} */ -trait Function2[@specialized(Specializable.Args) -T1, @specialized(Specializable.Args) -T2, @specialized(Specializable.Return) +R] extends AnyRef { self => +trait Function2[@specialized(Specializable.Args) -T1, @specialized(Specializable.Args) -T2, @specialized(Specializable.Return) +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ diff --git a/library/src/scala/Function20.scala b/library/src/scala/Function20.scala index 1b445f783310..aac19a4309fc 100644 --- a/library/src/scala/Function20.scala +++ b/library/src/scala/Function20.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 20 parameters. * */ -trait Function20[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, +R] extends AnyRef { self => +trait Function20[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function20[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16)(x17)(x18)(x19)(x20) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => T17 => T18 => T19 => T20 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18, x19: T19, x20: T20) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18, x19: T19, x20: T20) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20)).curried } /** Creates a tupled version of this function: instead of 20 arguments, * it accepts a single [[scala.Tuple20]] argument. diff --git a/library/src/scala/Function21.scala b/library/src/scala/Function21.scala index e5e3047da3b3..cf5231f08939 100644 --- a/library/src/scala/Function21.scala +++ b/library/src/scala/Function21.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 21 parameters. * */ -trait Function21[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, +R] extends AnyRef { self => +trait Function21[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function21[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16)(x17)(x18)(x19)(x20)(x21) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => T17 => T18 => T19 => T20 => T21 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18, x19: T19, x20: T20, x21: T21) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18, x19: T19, x20: T20, x21: T21) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21)).curried } /** Creates a tupled version of this function: instead of 21 arguments, * it accepts a single [[scala.Tuple21]] argument. diff --git a/library/src/scala/Function22.scala b/library/src/scala/Function22.scala index 9eae1d43ce26..21e52c9d5562 100644 --- a/library/src/scala/Function22.scala +++ b/library/src/scala/Function22.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 22 parameters. * */ -trait Function22[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, -T22, +R] extends AnyRef { self => +trait Function22[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, -T22, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function22[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9)(x10)(x11)(x12)(x13)(x14)(x15)(x16)(x17)(x18)(x19)(x20)(x21)(x22) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => T10 => T11 => T12 => T13 => T14 => T15 => T16 => T17 => T18 => T19 => T20 => T21 => T22 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18, x19: T19, x20: T20, x21: T21, x22: T22) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9, x10: T10, x11: T11, x12: T12, x13: T13, x14: T14, x15: T15, x16: T16, x17: T17, x18: T18, x19: T19, x20: T20, x21: T21, x22: T22) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22)).curried } /** Creates a tupled version of this function: instead of 22 arguments, * it accepts a single [[scala.Tuple22]] argument. diff --git a/library/src/scala/Function3.scala b/library/src/scala/Function3.scala index 5c29f6e4fbbd..b7cbec633261 100644 --- a/library/src/scala/Function3.scala +++ b/library/src/scala/Function3.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 3 parameters. * */ -trait Function3[-T1, -T2, -T3, +R] extends AnyRef { self => +trait Function3[-T1, -T2, -T3, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ diff --git a/library/src/scala/Function4.scala b/library/src/scala/Function4.scala index efc3c56909eb..062a92b4abfc 100644 --- a/library/src/scala/Function4.scala +++ b/library/src/scala/Function4.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 4 parameters. * */ -trait Function4[-T1, -T2, -T3, -T4, +R] extends AnyRef { self => +trait Function4[-T1, -T2, -T3, -T4, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ diff --git a/library/src/scala/Function5.scala b/library/src/scala/Function5.scala index a0e4b082c728..75115c78379d 100644 --- a/library/src/scala/Function5.scala +++ b/library/src/scala/Function5.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 5 parameters. * */ -trait Function5[-T1, -T2, -T3, -T4, -T5, +R] extends AnyRef { self => +trait Function5[-T1, -T2, -T3, -T4, -T5, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function5[-T1, -T2, -T3, -T4, -T5, +R] extends AnyRef { self => * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5) == apply(x1, x2, x3, x4, x5)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5) => self.apply(x1, x2, x3, x4, x5)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5) => this.apply(x1, x2, x3, x4, x5)).curried } /** Creates a tupled version of this function: instead of 5 arguments, * it accepts a single [[scala.Tuple5]] argument. diff --git a/library/src/scala/Function6.scala b/library/src/scala/Function6.scala index 58d428c8e888..99f4b4a2ffda 100644 --- a/library/src/scala/Function6.scala +++ b/library/src/scala/Function6.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 6 parameters. * */ -trait Function6[-T1, -T2, -T3, -T4, -T5, -T6, +R] extends AnyRef { self => +trait Function6[-T1, -T2, -T3, -T4, -T5, -T6, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function6[-T1, -T2, -T3, -T4, -T5, -T6, +R] extends AnyRef { self => * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6) == apply(x1, x2, x3, x4, x5, x6)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6) => self.apply(x1, x2, x3, x4, x5, x6)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6) => this.apply(x1, x2, x3, x4, x5, x6)).curried } /** Creates a tupled version of this function: instead of 6 arguments, * it accepts a single [[scala.Tuple6]] argument. diff --git a/library/src/scala/Function7.scala b/library/src/scala/Function7.scala index 8f4bfa19aa9d..04f54559b230 100644 --- a/library/src/scala/Function7.scala +++ b/library/src/scala/Function7.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 7 parameters. * */ -trait Function7[-T1, -T2, -T3, -T4, -T5, -T6, -T7, +R] extends AnyRef { self => +trait Function7[-T1, -T2, -T3, -T4, -T5, -T6, -T7, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function7[-T1, -T2, -T3, -T4, -T5, -T6, -T7, +R] extends AnyRef { self => * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7) == apply(x1, x2, x3, x4, x5, x6, x7)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7) => self.apply(x1, x2, x3, x4, x5, x6, x7)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7) => this.apply(x1, x2, x3, x4, x5, x6, x7)).curried } /** Creates a tupled version of this function: instead of 7 arguments, * it accepts a single [[scala.Tuple7]] argument. diff --git a/library/src/scala/Function8.scala b/library/src/scala/Function8.scala index 384f6132d1a5..44394a42915d 100644 --- a/library/src/scala/Function8.scala +++ b/library/src/scala/Function8.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 8 parameters. * */ -trait Function8[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, +R] extends AnyRef { self => +trait Function8[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function8[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, +R] extends AnyRef { sel * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8) == apply(x1, x2, x3, x4, x5, x6, x7, x8)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8)).curried } /** Creates a tupled version of this function: instead of 8 arguments, * it accepts a single [[scala.Tuple8]] argument. diff --git a/library/src/scala/Function9.scala b/library/src/scala/Function9.scala index a60f59f0f25d..76b42793a06d 100644 --- a/library/src/scala/Function9.scala +++ b/library/src/scala/Function9.scala @@ -19,7 +19,7 @@ import scala.language.`2.13` /** A function of 9 parameters. * */ -trait Function9[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, +R] extends AnyRef { self => +trait Function9[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, +R] extends AnyRef { /** Apply the body of this function to the arguments. * @return the result of function application. */ @@ -29,7 +29,7 @@ trait Function9[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, +R] extends AnyRef * @return a function `f` such that `f(x1)(x2)(x3)(x4)(x5)(x6)(x7)(x8)(x9) == apply(x1, x2, x3, x4, x5, x6, x7, x8, x9)` */ @annotation.unspecialized def curried: T1 => T2 => T3 => T4 => T5 => T6 => T7 => T8 => T9 => R = { - (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9) => self.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9)).curried + (x1: T1) => ((x2: T2, x3: T3, x4: T4, x5: T5, x6: T6, x7: T7, x8: T8, x9: T9) => this.apply(x1, x2, x3, x4, x5, x6, x7, x8, x9)).curried } /** Creates a tupled version of this function: instead of 9 arguments, * it accepts a single [[scala.Tuple9]] argument. diff --git a/library/src/scala/IArray.scala b/library/src/scala/IArray.scala index 887ca517ef2b..281d1ef78a89 100644 --- a/library/src/scala/IArray.scala +++ b/library/src/scala/IArray.scala @@ -1,6 +1,8 @@ package scala import reflect.ClassTag +import language.experimental.captureChecking + import scala.collection.{LazyZip2, SeqView, Searching, Stepper, StepperShape} import scala.collection.immutable.ArraySeq import scala.collection.mutable.{ArrayBuilder, Builder} @@ -92,12 +94,12 @@ object IArray: /** Builds a new array by applying a function to all elements of this array * and using the elements of the resulting collections. */ - extension [T](arr: IArray[T]) def flatMap[U: ClassTag](f: T => IterableOnce[U]): IArray[U] = + extension [T](arr: IArray[T]) def flatMap[U: ClassTag](f: T => IterableOnce[U]^): IArray[U] = genericArrayOps(arr).flatMap(f) /** Flattens a two-dimensional array by concatenating all its rows * into a single array. */ - extension [T](arr: IArray[T]) def flatten[U](using asIterable: T => Iterable[U], ct: ClassTag[U]): IArray[U] = + extension [T](arr: IArray[T]) def flatten[U](using asIterable: T => Iterable[U]^, ct: ClassTag[U]): IArray[U] = genericArrayOps(arr).flatten /** Folds the elements of this array using the specified associative binary operator. */ @@ -222,7 +224,7 @@ object IArray: genericArrayOps(arr).sortWith(f) /** Sorts this array according to an Ordering. */ - extension [T](arr: IArray[T]) def sorted(using math.Ordering[T]): IArray[T] = + extension [T](arr: IArray[T]) def sorted(using math.Ordering[T]^): IArray[T] = genericArrayOps(arr).sorted /** Splits this array into a prefix/suffix pair according to a predicate. */ @@ -260,25 +262,25 @@ object IArray: def ++[U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U] = genericArrayOps(arr) ++ suffix def :+ [U >: T: ClassTag](x: U): IArray[U] = genericArrayOps(arr) :+ x def :++ [U >: T: ClassTag](suffix: IArray[U]): IArray[U] = genericArrayOps(arr) :++ suffix - def :++ [U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U] = genericArrayOps(arr) :++ suffix + def :++ [U >: T: ClassTag](suffix: IterableOnce[U]^): IArray[U] = genericArrayOps(arr) :++ suffix def appended[U >: T: ClassTag](x: U): IArray[U] = genericArrayOps(arr).appended(x) def appendedAll[U >: T: ClassTag](suffix: IArray[U]): IArray[U] = genericArrayOps(arr).appendedAll(suffix) - def appendedAll[U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U] = genericArrayOps(arr).appendedAll(suffix) - def collect[U: ClassTag](pf: PartialFunction[T, U]): IArray[U] = genericArrayOps(arr).collect(pf) - def collectFirst[U](f: PartialFunction[T, U]): Option[U] = genericArrayOps(arr).collectFirst(f) + def appendedAll[U >: T: ClassTag](suffix: IterableOnce[U]^): IArray[U] = genericArrayOps(arr).appendedAll(suffix) + def collect[U: ClassTag](pf: PartialFunction[T, U]^): IArray[U] = genericArrayOps(arr).collect(pf) + def collectFirst[U](f: PartialFunction[T, U]^): Option[U] = genericArrayOps(arr).collectFirst(f) def combinations(n: Int): Iterator[IArray[T]] = genericArrayOps(arr).combinations(n) def concat[U >: T: ClassTag](suffix: IArray[U]): IArray[U] = genericArrayOps(arr).concat(suffix) - def concat[U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U] = genericArrayOps(arr).concat(suffix) + def concat[U >: T: ClassTag](suffix: IterableOnce[U]^): IArray[U] = genericArrayOps(arr).concat(suffix) def diff[U >: T](that: IArray[U]): IArray[T] = genericArrayOps(arr).diff(that.toSeq) def diff[U >: T](that: Seq[U]): IArray[T] = genericArrayOps(arr).diff(that) def distinct: IArray[T] = genericArrayOps(arr).distinct - def distinctBy[U](f: T => U): IArray[T] = genericArrayOps(arr).distinctBy(f) + def distinctBy[U](f: T -> U): IArray[T] = genericArrayOps(arr).distinctBy(f) def startsWith[U >: T](that: IArray[U]): Boolean = genericArrayOps(arr).startsWith(that, 0) def startsWith[U >: T](that: IArray[U], offset: Int): Boolean = genericArrayOps(arr).startsWith(that, offset) - def startsWith[U >: T](that: IterableOnce[U]): Boolean = genericArrayOps(arr).startsWith(that, 0) - def startsWith[U >: T](that: IterableOnce[U], offset: Int): Boolean = genericArrayOps(arr).startsWith(that, offset) + def startsWith[U >: T](that: IterableOnce[U]^): Boolean = genericArrayOps(arr).startsWith(that, 0) + def startsWith[U >: T](that: IterableOnce[U]^, offset: Int): Boolean = genericArrayOps(arr).startsWith(that, offset) def endsWith[U >: T](that: IArray[U]): Boolean = genericArrayOps(arr).endsWith(that) - def endsWith[U >: T](that: Iterable[U]): Boolean = genericArrayOps(arr).endsWith(that) + def endsWith[U >: T](that: Iterable[U]^): Boolean = genericArrayOps(arr).endsWith(that) def groupBy[K](f: T => K): Map[K, IArray[T]] = genericArrayOps(arr).groupBy(f) def groupMap[K, U: ClassTag](key: T => K)(f: T => U): Map[K, IArray[U]] = genericArrayOps(arr).groupMap(key)(f) def grouped(size: Int): Iterator[IArray[T]] = genericArrayOps(arr).grouped(size) @@ -286,14 +288,14 @@ object IArray: def intersect[U >: T](that: IArray[U]): IArray[T] = genericArrayOps(arr).intersect(that) def intersect[U >: T](that: Seq[U]): IArray[T] = genericArrayOps(arr).intersect(that) def lazyZip[U](that: IArray[U]): LazyZip2[T, U, IArray[T]] = genericArrayOps(arr).lazyZip[U](that).asInstanceOf[LazyZip2[T, U, IArray[T]]] - def lazyZip[U](that: Iterable[U]): LazyZip2[T, U, IArray[T]] = genericArrayOps(arr).lazyZip[U](that).asInstanceOf[LazyZip2[T, U, IArray[T]]] + def lazyZip[U](that: Iterable[U]^): LazyZip2[T, U, IArray[T]] = genericArrayOps(arr).lazyZip[U](that).asInstanceOf[LazyZip2[T, U, IArray[T]]] def lengthCompare(len: Int): Int = genericArrayOps(arr).lengthCompare(len) def padTo[U >: T: ClassTag](len: Int, elem: U): IArray[U] = genericArrayOps(arr).padTo(len, elem) def partitionMap[T1: ClassTag, T2: ClassTag](f: T => Either[T1, T2]): (IArray[T1], IArray[T2]) = genericArrayOps(arr).partitionMap(f) def patch[U >: T: ClassTag](from: Int, other: IterableOnce[U], replaced: Int): IArray[U] = genericArrayOps(arr).patch(from, other, replaced) def permutations: Iterator[IArray[T]] = genericArrayOps(arr).permutations def prepended[U >: T: ClassTag](x: U): IArray[U] = genericArrayOps(arr).prepended(x) - def prependedAll[U >: T: ClassTag](prefix: IterableOnce[U]): IArray[U] = genericArrayOps(arr).prependedAll(prefix) + def prependedAll[U >: T: ClassTag](prefix: IterableOnce[U]^): IArray[U] = genericArrayOps(arr).prependedAll(prefix) def reverseIterator: Iterator[T] = genericArrayOps(arr).reverseIterator def search[U >: T](elem: U)(using Ordering[U]): Searching.SearchResult = arr.toSeq.search(elem) def search[U >: T](elem: U, from: Int, to: Int)(using Ordering[U]): Searching.SearchResult = arr.toSeq.search(elem, from, to) @@ -312,14 +314,14 @@ object IArray: def unzip3[T1, T2, T3](using asTriple: T => (T1, T2, T3), ct1: ClassTag[T1], ct2: ClassTag[T2], ct3: ClassTag[T3]): (IArray[T1], IArray[T2], IArray[T3]) = genericArrayOps(arr).unzip3 def updated[U >: T: ClassTag](index: Int, elem: U): IArray[U] = genericArrayOps(arr).updated(index, elem) def view: SeqView[T] = genericArrayOps(arr).view - def withFilter(p: T => Boolean): WithFilter[T] = new WithFilter(p, arr) + def withFilter(p: T => Boolean): WithFilter[T]^{p} = new WithFilter(p, arr) def zip[U](that: IArray[U]): IArray[(T, U)] = genericArrayOps(arr).zip(that) - def zip[U](that: IterableOnce[U]): IArray[(T, U)] = genericArrayOps(arr).zip(that) + def zip[U](that: IterableOnce[U]^): IArray[(T, U)] = genericArrayOps(arr).zip(that) def zipAll[T1 >: T, U](that: IArray[U], thisElem: T1, thatElem: U): IArray[(T1, U)] = genericArrayOps(arr).zipAll(that, thisElem, thatElem) - def zipAll[T1 >: T, U](that: Iterable[U], thisElem: T1, thatElem: U): IArray[(T1, U)] = genericArrayOps(arr).zipAll(that, thisElem, thatElem) + def zipAll[T1 >: T, U](that: Iterable[U]^, thisElem: T1, thatElem: U): IArray[(T1, U)] = genericArrayOps(arr).zipAll(that, thisElem, thatElem) def zipWithIndex: IArray[(T, Int)] = genericArrayOps(arr).zipWithIndex - extension [T, U >: T: ClassTag](prefix: IterableOnce[T]) + extension [T, U >: T: ClassTag](prefix: IterableOnce[T]^) def ++:(arr: IArray[U]): IArray[U] = genericArrayOps(arr).prependedAll(prefix) extension [T, U >: T: ClassTag](prefix: IArray[T]) @@ -444,7 +446,7 @@ object IArray: * @param it the iterable collection * @return an array consisting of elements of the iterable collection */ - def from[A : ClassTag](it: IterableOnce[A]): IArray[A] = + def from[A : ClassTag](it: IterableOnce[A]^): IArray[A] = unsafeFromArray(Array.from(it)) def newBuilder[T](using t: ClassTag[T]): Builder[T, IArray[T]] = @@ -606,8 +608,8 @@ object IArray: * @param ys an array of AnyRef * @return true if corresponding elements are equal */ - def equals(xs: IArray[AnyRef], ys: IArray[AnyRef]): Boolean = - Array.equals(xs.asInstanceOf[Array[AnyRef]], ys.asInstanceOf[Array[AnyRef]]) + def equals(xs: IArray[AnyRef^], ys: IArray[AnyRef^]): Boolean = + Array.equals(xs.asInstanceOf[Array[AnyRef^{xs}]], ys.asInstanceOf[Array[AnyRef^{ys}]]) /** Returns a decomposition of the array into a sequence. This supports * a pattern match like `{ case IArray(x,y,z) => println('3 elements')}`. @@ -660,7 +662,7 @@ object IArray: * @return a new array resulting from applying the given collection-valued function * `f` to each element of this array and concatenating the results. */ - def flatMap[U: ClassTag](f: T => IterableOnce[U]): IArray[U] = { + def flatMap[U: ClassTag](f: T => IterableOnce[U]^): IArray[U] = { val b = IArray.newBuilder[U] var i = 0 while(i < xs.length) { @@ -671,11 +673,11 @@ object IArray: b.result() } - def flatMap[BS, U](f: T => BS)(using asIterable: BS => Iterable[U], m: ClassTag[U]): IArray[U] = + def flatMap[BS, U](f: T => BS)(using asIterable: BS => Iterable[U]^, m: ClassTag[U]): IArray[U] = flatMap[U](x => asIterable(f(x))) /** Creates a new non-strict filter which combines this filter with the given predicate. */ - def withFilter(q: T => Boolean): WithFilter[T] = new WithFilter[T](a => p(a) && q(a), xs) + def withFilter(q: T => Boolean): WithFilter[T]^{p, q} = new WithFilter[T](a => p(a) && q(a), xs) end WithFilter diff --git a/library/src/scala/Int.scala b/library/src/scala/Int.scala index 335e33233541..a3fff7e991da 100644 --- a/library/src/scala/Int.scala +++ b/library/src/scala/Int.scala @@ -455,7 +455,7 @@ object Int extends AnyValCompanion { /** The largest value representable as an Int. */ final val MaxValue = java.lang.Integer.MAX_VALUE - /** Transform a value type into a boxed reference type. + /** Transforms a value type into a boxed reference type. * * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToInteger`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. * @@ -464,7 +464,7 @@ object Int extends AnyValCompanion { */ def box(x: Int): java.lang.Integer = ??? - /** Transform a boxed type into a value type. Note that this + /** Transforms a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw * an exception if the argument is not a java.lang.Integer. * diff --git a/library/src/scala/Long.scala b/library/src/scala/Long.scala index e19b7e706f96..2561c1ec74c2 100644 --- a/library/src/scala/Long.scala +++ b/library/src/scala/Long.scala @@ -452,7 +452,7 @@ object Long extends AnyValCompanion { /** The largest value representable as a Long. */ final val MaxValue = java.lang.Long.MAX_VALUE - /** Transform a value type into a boxed reference type. + /** Transforms a value type into a boxed reference type. * * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToLong`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. * @@ -461,7 +461,7 @@ object Long extends AnyValCompanion { */ def box(x: Long): java.lang.Long = ??? - /** Transform a boxed type into a value type. Note that this + /** Transforms a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw * an exception if the argument is not a java.lang.Long. * diff --git a/library/src/scala/NamedTuple.scala b/library/src/scala/NamedTuple.scala index c63ad29c56e9..f88f7760365b 100644 --- a/library/src/scala/NamedTuple.scala +++ b/library/src/scala/NamedTuple.scala @@ -1,6 +1,8 @@ package scala import compiletime.ops.boolean.* +import language.experimental.captureChecking + object NamedTuple: /** The type to which named tuples get mapped to. For instance, diff --git a/library/src/scala/PartialFunction.scala b/library/src/scala/PartialFunction.scala index af8199d6209e..31f3fbe77a83 100644 --- a/library/src/scala/PartialFunction.scala +++ b/library/src/scala/PartialFunction.scala @@ -15,6 +15,8 @@ package scala import scala.language.`2.13` import scala.annotation.nowarn +import language.experimental.captureChecking + /** A partial function of type `PartialFunction[A, B]` is a unary function * where the domain does not necessarily include all values of type `A`. * The function [[isDefinedAt]] allows to test dynamically if a value is in @@ -105,7 +107,7 @@ import scala.annotation.nowarn * may apply the first partial function and execute its side effect. * For efficiency, it is recommended to call [[applyOrElse]] instead of [[isDefinedAt]] or [[apply]]. */ -trait PartialFunction[-A, +B] extends (A => B) { self => +trait PartialFunction[-A, +B] extends Function1[A, B] { self: PartialFunction[A, B]^ => import PartialFunction._ /** Tries to extract a `B` from an `A` in a pattern matching expression. */ @@ -122,7 +124,7 @@ trait PartialFunction[-A, +B] extends (A => B) { self => * } * }}} */ - def elementWise: ElementWiseExtractor[A, B] = new ElementWiseExtractor[A, B](this) + def elementWise: ElementWiseExtractor[A, B]^{this} = new ElementWiseExtractor[A, B](this) /** Checks if a value is contained in the function's domain. * @@ -141,7 +143,7 @@ trait PartialFunction[-A, +B] extends (A => B) { self => * of this partial function and `that`. The resulting partial function * takes `x` to `this(x)` where `this` is defined, and to `that(x)` where it is not. */ - def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] = + def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]^): PartialFunction[A1, B1]^{this, that} = new OrElse[A1, B1] (this, that) //TODO: why not overload it with orElse(that: F1): F1? @@ -157,8 +159,8 @@ trait PartialFunction[-A, +B] extends (A => B) { self => * possibly narrowed by the specified function, which maps * arguments `x` to `k(this(x))`. */ - override def andThen[C](k: B => C): PartialFunction[A, C] = k match { - case pf: PartialFunction[B, C] => andThen(pf) + override def andThen[C](k: B => C): PartialFunction[A, C]^{this, k} = k match { + case pf: (PartialFunction[B, C]^{k}) => andThen(pf) case _ => new AndThen[A, B, C](this, k) } @@ -173,7 +175,7 @@ trait PartialFunction[-A, +B] extends (A => B) { self => * @return a partial function with the domain of this partial function narrowed by * other partial function, which maps arguments `x` to `k(this(x))`. */ - def andThen[C](k: PartialFunction[B, C]): PartialFunction[A, C] = + def andThen[C](k: PartialFunction[B, C]^): PartialFunction[A, C]^{this, k} = new Combined[A, B, C](this, k) /** @@ -187,7 +189,7 @@ trait PartialFunction[-A, +B] extends (A => B) { self => * @return a partial function with the domain of other partial function narrowed by * this partial function, which maps arguments `x` to `this(k(x))`. */ - def compose[R](k: PartialFunction[R, A]): PartialFunction[R, B] = + def compose[R](k: PartialFunction[R, A]^): PartialFunction[R, B]^{this, k} = new Combined[R, A, B](k, this) /** Turns this partial function into a plain function returning an `Option` result. @@ -195,7 +197,7 @@ trait PartialFunction[-A, +B] extends (A => B) { self => * @return a function that takes an argument `x` to `Some(this(x))` if `this` * is defined for `x`, and to `None` otherwise. */ - def lift: A => Option[B] = new Lifted(this) + def lift: A ->{this} Option[B] = new Lifted(this) /** Applies this partial function to the given argument when it is contained in the function domain. * Applies fallback function where this partial function is not defined. @@ -239,7 +241,7 @@ trait PartialFunction[-A, +B] extends (A => B) { self => * @return a function which maps arguments `x` to `isDefinedAt(x)`. The resulting function * runs `action(this(x))` where `this` is defined. */ - def runWith[U](action: B => U): A => Boolean = { x => + def runWith[U](action: B => U): A ->{this, action} Boolean = { x => val z = applyOrElse(x, checkFallback[B]) if (!fallbackOccurred(z)) { action(z); true } else false } @@ -259,7 +261,7 @@ trait PartialFunction[-A, +B] extends (A => B) { self => */ object PartialFunction { - final class ElementWiseExtractor[-A, +B] private[PartialFunction] (private val pf: PartialFunction[A, B]) extends AnyVal { + final class ElementWiseExtractor[-A, +B] private[PartialFunction] (private val pf: PartialFunction[A, B]^) extends AnyVal { this: ElementWiseExtractor[A, B]^ => @nowarn("cat=lint-nonlocal-return") def unapplySeq(seq: Seq[A]): Option[Seq[B]] = { Some(seq.map { @@ -271,7 +273,7 @@ object PartialFunction { /** Composite function produced by `PartialFunction#orElse` method */ - private class OrElse[-A, +B] (f1: PartialFunction[A, B], f2: PartialFunction[A, B]) + private class OrElse[-A, +B] (f1: PartialFunction[A, B]^, f2: PartialFunction[A, B]^) extends scala.runtime.AbstractPartialFunction[A, B] with Serializable { def isDefinedAt(x: A) = f1.isDefinedAt(x) || f2.isDefinedAt(x) @@ -282,16 +284,16 @@ object PartialFunction { if (!fallbackOccurred(z)) z else f2.applyOrElse(x, default) } - override def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]): OrElse[A1, B1] = + override def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]^): OrElse[A1, B1]^{this, that} = new OrElse[A1, B1] (f1, f2 orElse that) - override def andThen[C](k: B => C): OrElse[A, C] = + override def andThen[C](k: B => C): OrElse[A, C]^{this, k} = new OrElse[A, C] (f1 andThen k, f2 andThen k) } /** Composite function produced by `PartialFunction#andThen` method */ - private class AndThen[-A, B, +C] (pf: PartialFunction[A, B], k: B => C) extends PartialFunction[A, C] with Serializable { + private class AndThen[-A, B, +C] (pf: PartialFunction[A, B]^, k: B => C) extends PartialFunction[A, C] with Serializable { def isDefinedAt(x: A) = pf.isDefinedAt(x) def apply(x: A): C = k(pf(x)) @@ -304,7 +306,7 @@ object PartialFunction { /** Composite function produced by `PartialFunction#andThen` method */ - private class Combined[-A, B, +C] (pf: PartialFunction[A, B], k: PartialFunction[B, C]) extends PartialFunction[A, C] with Serializable { + private class Combined[-A, B, +C] (pf: PartialFunction[A, B]^, k: PartialFunction[B, C]^) extends PartialFunction[A, C] with Serializable { def isDefinedAt(x: A): Boolean = { val b: B = pf.applyOrElse(x, checkFallback[B]) if (!fallbackOccurred(b)) k.isDefinedAt(b) else false @@ -339,11 +341,11 @@ object PartialFunction { * * Here `fallback_fn` is used as both unique marker object and special fallback function that returns it. */ - private[this] val fallback_fn: Any => Any = _ => fallback_fn + private[this] val fallback_fn: Any -> Any = _ => fallback_fn private def checkFallback[B] = fallback_fn.asInstanceOf[Any => B] private def fallbackOccurred[B](x: B) = fallback_fn eq x.asInstanceOf[AnyRef] - private class Lifted[-A, +B] (val pf: PartialFunction[A, B]) + private class Lifted[-A, +B] (val pf: PartialFunction[A, B]^) extends scala.runtime.AbstractFunction1[A, Option[B]] with Serializable { def apply(x: A): Option[B] = { @@ -362,9 +364,9 @@ object PartialFunction { override def lift = f } - private[scala] def unlifted[A, B](f: A => Option[B]): PartialFunction[A, B] = f match { - case lf: Lifted[A, B] => lf.pf - case ff => new Unlifted(ff) + private[scala] def unlifted[A, B](f: A => Option[B]): PartialFunction[A, B]^{f} = f match { + case lf: (Lifted[A, B]^{f}) => caps.unsafe.unsafeAssumePure(lf).pf // SAFETY: lf.pf captures at most pf + case _ => new Unlifted(f) } /** Converts an ordinary function to a partial function. Note that calling `isDefinedAt(x)` on @@ -372,16 +374,16 @@ object PartialFunction { * @param f an ordinary function * @return a partial function which delegates to the ordinary function `f` */ - def fromFunction[A, B](f: A => B): PartialFunction[A, B] = { case x => f(x) } + def fromFunction[A, B](f: A => B): PartialFunction[A, B]^{f} = { case x => f(x) } - private[this] val constFalse: Any => Boolean = { _ => false} + private[this] val constFalse: Any -> Boolean = { _ => false} private[this] val empty_pf: PartialFunction[Any, Nothing] = new PartialFunction[Any, Nothing] with Serializable { def isDefinedAt(x: Any) = false def apply(x: Any) = throw new MatchError(x) - override def orElse[A1, B1](that: PartialFunction[A1, B1]) = that - override def andThen[C](k: Nothing => C): PartialFunction[Any, Nothing] = this - override val lift: Any => None.type = (x: Any) => None + override def orElse[A1, B1](that: PartialFunction[A1, B1]^) = that + override def andThen[C](k: PartialFunction[Nothing, C]^): PartialFunction[Any, C]^{k} = this + override val lift: Any -> None.type = (x: Any) => None override def runWith[U](action: Nothing => U) = constFalse } @@ -399,7 +401,7 @@ object PartialFunction { * @param pf the partial function * @return true, iff `x` is in the domain of `pf` and `pf(x) == true`. */ - def cond[A](x: A)(pf: PartialFunction[A, Boolean]): Boolean = pf.applyOrElse(x, constFalse) + def cond[A](x: A)(pf: PartialFunction[A, Boolean]^): Boolean = pf.applyOrElse(x, constFalse) /** Apply the function to the given value if defined, and return the result * in a `Some`; otherwise, return `None`. @@ -408,7 +410,7 @@ object PartialFunction { * @param pf the PartialFunction[T, U] * @return `Some(pf(x))` if `pf isDefinedAt x`, `None` otherwise. */ - def condOpt[A, B](x: A)(pf: PartialFunction[A, B]): Option[B] = { + def condOpt[A, B](x: A)(pf: PartialFunction[A, B]^): Option[B] = { val z = pf.applyOrElse(x, checkFallback[B]) if (!fallbackOccurred(z)) Some(z) else None } diff --git a/library/src/scala/PolyFunction.scala b/library/src/scala/PolyFunction.scala index 44278b6cffde..f37b1f9eab45 100644 --- a/library/src/scala/PolyFunction.scala +++ b/library/src/scala/PolyFunction.scala @@ -1,5 +1,7 @@ package scala +import language.experimental.captureChecking + /* (EXPERIMENTAL) NEW DOCUMENTATION: This should be used when we stabilize erased definitions or other generalize other function types. * * Marker trait for many (poly) function types. diff --git a/library/src/scala/Precise.scala b/library/src/scala/Precise.scala index f8a8dd6b47f4..838327c79409 100644 --- a/library/src/scala/Precise.scala +++ b/library/src/scala/Precise.scala @@ -1,4 +1,7 @@ package scala + +import language.experimental.captureChecking + import annotation.experimental import language.experimental.erasedDefinitions diff --git a/library/src/scala/Selectable.scala b/library/src/scala/Selectable.scala index 74004a350679..801ab1ea4bc7 100644 --- a/library/src/scala/Selectable.scala +++ b/library/src/scala/Selectable.scala @@ -1,5 +1,6 @@ package scala +import language.experimental.captureChecking /** A marker trait for objects that support structural selection via * `selectDynamic` and `applyDynamic` diff --git a/library/src/scala/Short.scala b/library/src/scala/Short.scala index fc58ad8640e7..03b0929ea5ce 100644 --- a/library/src/scala/Short.scala +++ b/library/src/scala/Short.scala @@ -455,7 +455,7 @@ object Short extends AnyValCompanion { /** The largest value representable as a Short. */ final val MaxValue = java.lang.Short.MAX_VALUE - /** Transform a value type into a boxed reference type. + /** Transforms a value type into a boxed reference type. * * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxToShort`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. * @@ -464,7 +464,7 @@ object Short extends AnyValCompanion { */ def box(x: Short): java.lang.Short = ??? - /** Transform a boxed type into a value type. Note that this + /** Transforms a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw * an exception if the argument is not a java.lang.Short. * diff --git a/library/src/scala/Tuple.scala b/library/src/scala/Tuple.scala index 57d1572772e2..6d6f234a817a 100644 --- a/library/src/scala/Tuple.scala +++ b/library/src/scala/Tuple.scala @@ -1,5 +1,6 @@ package scala +// import language.experimental.captureChecking import annotation.showAsInfix import compiletime.* import compiletime.ops.int.* diff --git a/library/src/scala/Unit.scala b/library/src/scala/Unit.scala index 1799f678e3fa..e7ad25073f50 100644 --- a/library/src/scala/Unit.scala +++ b/library/src/scala/Unit.scala @@ -31,7 +31,7 @@ final abstract class Unit private extends AnyVal { @scala.annotation.compileTimeOnly("`Unit` companion object is not allowed in source; instead, use `()` for the unit value") object Unit extends AnyValCompanion { - /** Transform a value type into a boxed reference type. + /** Transforms a value type into a boxed reference type. * * This method is not intended for use in source code. * The runtime representation of this value is platform specific. @@ -41,7 +41,7 @@ object Unit extends AnyValCompanion { */ def box(x: Unit): scala.runtime.BoxedUnit = scala.runtime.BoxedUnit.UNIT - /** Transform a boxed type into a value type. Note that this + /** Transforms a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw * an exception if the argument is not a scala.runtime.BoxedUnit. * diff --git a/library/src/scala/annotation/MacroAnnotation.scala b/library/src/scala/annotation/MacroAnnotation.scala index ffaa0cf88464..7f2bd56c56c1 100644 --- a/library/src/scala/annotation/MacroAnnotation.scala +++ b/library/src/scala/annotation/MacroAnnotation.scala @@ -2,6 +2,8 @@ package scala package annotation +import language.experimental.captureChecking + import scala.quoted.* /** Base trait for macro annotation implementation. diff --git a/library/src/scala/annotation/RefiningAnnotation.scala b/library/src/scala/annotation/RefiningAnnotation.scala index 65761d5234d4..6fb2960b9685 100644 --- a/library/src/scala/annotation/RefiningAnnotation.scala +++ b/library/src/scala/annotation/RefiningAnnotation.scala @@ -1,5 +1,7 @@ package scala.annotation +import language.experimental.captureChecking + /** A base trait for annotations that yield proper subtypes of the types they annotate. * Refining annotations are more "sticky" than normal ones. They are conceptually kept * around when normal refinements would also not be stripped away. diff --git a/library/src/scala/annotation/alpha.scala b/library/src/scala/annotation/alpha.scala index ac2a11c5b650..932713ce2031 100644 --- a/library/src/scala/annotation/alpha.scala +++ b/library/src/scala/annotation/alpha.scala @@ -1,5 +1,7 @@ package scala.annotation +import language.experimental.captureChecking + /** An annotation that defines an external name for a definition. * If an `alpha(extname)` annotation is given for a method or some other * definition, its implementation will use the name `extname` instead of diff --git a/library/src/scala/annotation/capability.scala b/library/src/scala/annotation/capability.scala index d3453e3c8168..63428b509b77 100644 --- a/library/src/scala/annotation/capability.scala +++ b/library/src/scala/annotation/capability.scala @@ -1,6 +1,8 @@ package scala.annotation import annotation.experimental +import language.experimental.captureChecking + /** Marks an annotated class as a capability. * If the annotation is present and -Ycc is set, any (possibly aliased * or refined) instance of the class type is implicitly augmented with diff --git a/library/src/scala/annotation/constructorOnly.scala b/library/src/scala/annotation/constructorOnly.scala index 8fefc3b012dd..4959b7c503b6 100644 --- a/library/src/scala/annotation/constructorOnly.scala +++ b/library/src/scala/annotation/constructorOnly.scala @@ -8,6 +8,8 @@ package scala.annotation +import language.experimental.captureChecking + import scala.annotation.meta.* /** An annotation that goes on parameters of classes or traits. It asserts diff --git a/library/src/scala/annotation/experimental.scala b/library/src/scala/annotation/experimental.scala index 634cfe12db7f..ee91a6408b4b 100644 --- a/library/src/scala/annotation/experimental.scala +++ b/library/src/scala/annotation/experimental.scala @@ -1,5 +1,7 @@ package scala.annotation +import language.experimental.captureChecking + /** An annotation that can be used to mark a definition as experimental. * * @see [[https://dotty.epfl.ch/docs/reference/other-new-features/experimental-defs]] diff --git a/library/src/scala/annotation/init.scala b/library/src/scala/annotation/init.scala index 7dea418664e6..c2b12041e1db 100644 --- a/library/src/scala/annotation/init.scala +++ b/library/src/scala/annotation/init.scala @@ -1,5 +1,7 @@ package scala.annotation +import language.experimental.captureChecking + /** Annotations to control the behavior of the compiler check for safe initialization of static obects. * * Programmers usually do not need to use any annotations. They are intended for complex initialization diff --git a/library/src/scala/annotation/internal/$into.scala b/library/src/scala/annotation/internal/$into.scala index bad0e399d389..565aad3f2aa7 100644 --- a/library/src/scala/annotation/internal/$into.scala +++ b/library/src/scala/annotation/internal/$into.scala @@ -1,4 +1,7 @@ package scala.annotation.internal + +import language.experimental.captureChecking + import annotation.experimental /** An internal annotation on (part of) a parameter type that serves as a marker where diff --git a/library/src/scala/annotation/internal/Alias.scala b/library/src/scala/annotation/internal/Alias.scala index 5c8f7d4b9ffa..3d3de9f27c00 100644 --- a/library/src/scala/annotation/internal/Alias.scala +++ b/library/src/scala/annotation/internal/Alias.scala @@ -1,5 +1,7 @@ package scala.annotation.internal +import language.experimental.captureChecking + import scala.annotation.Annotation /** An annotation to record a Scala2 pickled alias. diff --git a/library/src/scala/annotation/internal/AnnotationDefault.scala b/library/src/scala/annotation/internal/AnnotationDefault.scala index 5280d091c1ec..5ea16e60a76c 100644 --- a/library/src/scala/annotation/internal/AnnotationDefault.scala +++ b/library/src/scala/annotation/internal/AnnotationDefault.scala @@ -1,5 +1,7 @@ package scala.annotation.internal +import language.experimental.captureChecking + import scala.annotation.Annotation /** An annotation to tag Java annotation default values */ diff --git a/library/src/scala/annotation/internal/AssignedNonLocally.scala b/library/src/scala/annotation/internal/AssignedNonLocally.scala index 8095ba91cfee..075f2f4453c8 100644 --- a/library/src/scala/annotation/internal/AssignedNonLocally.scala +++ b/library/src/scala/annotation/internal/AssignedNonLocally.scala @@ -1,6 +1,8 @@ package scala.annotation package internal +import language.experimental.captureChecking + /** An annotation to indicate that a private `var` was assigned with a prefix * other than the `this` type of its owner. */ diff --git a/library/src/scala/annotation/internal/Body.scala b/library/src/scala/annotation/internal/Body.scala index b6aa0c0fb616..682c26388cdf 100644 --- a/library/src/scala/annotation/internal/Body.scala +++ b/library/src/scala/annotation/internal/Body.scala @@ -1,5 +1,7 @@ package scala.annotation.internal +import language.experimental.captureChecking + import scala.annotation.Annotation /** The class associated with a `BodyAnnotation`, which indicates diff --git a/library/src/scala/annotation/internal/CaptureChecked.scala b/library/src/scala/annotation/internal/CaptureChecked.scala index 0bc6d628a542..a41c6364d46d 100644 --- a/library/src/scala/annotation/internal/CaptureChecked.scala +++ b/library/src/scala/annotation/internal/CaptureChecked.scala @@ -1,6 +1,8 @@ package scala.annotation package internal +import language.experimental.captureChecking + /** A marker annotation on a toplevel class that indicates * that the class was typed with the captureChecking language import. */ diff --git a/library/src/scala/annotation/internal/Child.scala b/library/src/scala/annotation/internal/Child.scala index b0d380e2460d..a6ec1f13b699 100644 --- a/library/src/scala/annotation/internal/Child.scala +++ b/library/src/scala/annotation/internal/Child.scala @@ -1,6 +1,8 @@ package scala.annotation package internal +import language.experimental.captureChecking + /** An annotation to indicate a child class or object of the annotated class. * E.g. if we have * diff --git a/library/src/scala/annotation/internal/ContextResultCount.scala b/library/src/scala/annotation/internal/ContextResultCount.scala index ecaedb65bf4f..8469a1171cfc 100644 --- a/library/src/scala/annotation/internal/ContextResultCount.scala +++ b/library/src/scala/annotation/internal/ContextResultCount.scala @@ -1,6 +1,8 @@ package scala.annotation package internal +import language.experimental.captureChecking + /** An annotation that's automatically added for methods * that have one or more nested context closures as their right hand side. * The parameter `n` is an Int Literal that tells how many nested closures diff --git a/library/src/scala/annotation/internal/ErasedParam.scala b/library/src/scala/annotation/internal/ErasedParam.scala index a5ed192820c3..e1482292464a 100644 --- a/library/src/scala/annotation/internal/ErasedParam.scala +++ b/library/src/scala/annotation/internal/ErasedParam.scala @@ -1,5 +1,7 @@ package scala.annotation package internal +import language.experimental.captureChecking + /** An annotation produced by Namer to indicate an erased parameter */ final class ErasedParam() extends Annotation diff --git a/library/src/scala/annotation/internal/InlineParam.scala b/library/src/scala/annotation/internal/InlineParam.scala index 0b3649e89da9..a95fd8363eee 100644 --- a/library/src/scala/annotation/internal/InlineParam.scala +++ b/library/src/scala/annotation/internal/InlineParam.scala @@ -1,5 +1,7 @@ package scala.annotation.internal +import language.experimental.captureChecking + import scala.annotation.Annotation /** An annotation produced by Namer to indicate an inline parameter */ diff --git a/library/src/scala/annotation/internal/MappedAlternative.scala b/library/src/scala/annotation/internal/MappedAlternative.scala index 19bd84df633c..72a545a2898d 100644 --- a/library/src/scala/annotation/internal/MappedAlternative.scala +++ b/library/src/scala/annotation/internal/MappedAlternative.scala @@ -1,6 +1,8 @@ package scala.annotation package internal +import language.experimental.captureChecking + /** An annotation added by overloading resoluton to mapped symbols that * explore deeper into the types of the opverloaded alternatives. * Its tree is a TypeTree with two parameters which are both needed to diff --git a/library/src/scala/annotation/internal/ProvisionalSuperClass.scala b/library/src/scala/annotation/internal/ProvisionalSuperClass.scala index 147b7b087c5a..96b8d42c2240 100644 --- a/library/src/scala/annotation/internal/ProvisionalSuperClass.scala +++ b/library/src/scala/annotation/internal/ProvisionalSuperClass.scala @@ -1,6 +1,8 @@ package scala.annotation package internal +import language.experimental.captureChecking + /** An annotation to record a provisional super class */ class ProvisionalSuperClass extends StaticAnnotation diff --git a/library/src/scala/annotation/internal/Repeated.scala b/library/src/scala/annotation/internal/Repeated.scala index 75eb3bc25f1a..fd01f0512b67 100644 --- a/library/src/scala/annotation/internal/Repeated.scala +++ b/library/src/scala/annotation/internal/Repeated.scala @@ -1,5 +1,7 @@ package scala.annotation.internal +import language.experimental.captureChecking + import scala.annotation.Annotation /** An annotation produced by desugaring to indicate that a diff --git a/library/src/scala/annotation/internal/RuntimeChecked.scala b/library/src/scala/annotation/internal/RuntimeChecked.scala index ed83936dc473..6612b1a904fc 100644 --- a/library/src/scala/annotation/internal/RuntimeChecked.scala +++ b/library/src/scala/annotation/internal/RuntimeChecked.scala @@ -1,5 +1,7 @@ package scala.annotation.internal +import language.experimental.captureChecking + import scala.annotation.Annotation import scala.annotation.experimental diff --git a/library/src/scala/annotation/internal/SourceFile.scala b/library/src/scala/annotation/internal/SourceFile.scala index b203869cf383..16003869dcc7 100644 --- a/library/src/scala/annotation/internal/SourceFile.scala +++ b/library/src/scala/annotation/internal/SourceFile.scala @@ -1,5 +1,7 @@ package scala.annotation.internal +import language.experimental.captureChecking + import scala.annotation.Annotation /** An annotation to record a Scala2 pickled alias. diff --git a/library/src/scala/annotation/internal/WithPureFuns.scala b/library/src/scala/annotation/internal/WithPureFuns.scala index 979dd7c405cc..9b28959423e5 100644 --- a/library/src/scala/annotation/internal/WithPureFuns.scala +++ b/library/src/scala/annotation/internal/WithPureFuns.scala @@ -1,6 +1,8 @@ package scala.annotation package internal +import language.experimental.captureChecking + /** A marker annotation on a toplevel class that indicates * that the class was typed with the pureFunctions language import. */ diff --git a/library/src/scala/annotation/internal/WitnessNames.scala b/library/src/scala/annotation/internal/WitnessNames.scala index 3921c2083617..e2a17958f354 100644 --- a/library/src/scala/annotation/internal/WitnessNames.scala +++ b/library/src/scala/annotation/internal/WitnessNames.scala @@ -1,6 +1,8 @@ package scala.annotation package internal +import language.experimental.captureChecking + /** An annotation that is used for marking type definitions that should get * context bound companions. The scheme is as follows: * diff --git a/library/src/scala/annotation/internal/preview.scala b/library/src/scala/annotation/internal/preview.scala index a6e797d78e97..2bae60d0eee6 100644 --- a/library/src/scala/annotation/internal/preview.scala +++ b/library/src/scala/annotation/internal/preview.scala @@ -1,6 +1,7 @@ package scala.annotation package internal +import language.experimental.captureChecking /** An annotation that can be used to mark a definition as preview. * diff --git a/library/src/scala/annotation/internal/reachCapability.scala b/library/src/scala/annotation/internal/reachCapability.scala index 9f5e344adbe9..cddc19011129 100644 --- a/library/src/scala/annotation/internal/reachCapability.scala +++ b/library/src/scala/annotation/internal/reachCapability.scala @@ -1,6 +1,8 @@ package scala.annotation package internal +import language.experimental.captureChecking + /** An annotation that marks a capture ref as a reach capability. * `x*` is encoded as `x.type @reachCapability` */ diff --git a/library/src/scala/annotation/internal/readOnlyCapability.scala b/library/src/scala/annotation/internal/readOnlyCapability.scala index 8e939aea6bb9..516543dba053 100644 --- a/library/src/scala/annotation/internal/readOnlyCapability.scala +++ b/library/src/scala/annotation/internal/readOnlyCapability.scala @@ -1,6 +1,8 @@ package scala.annotation package internal +import language.experimental.captureChecking + /** An annotation that marks a capture ref as a read-only capability. * `x.rd` is encoded as `x.type @readOnlyCapability` */ diff --git a/library/src/scala/annotation/internal/requiresCapability.scala b/library/src/scala/annotation/internal/requiresCapability.scala index 56fca22d2982..94653a8b0890 100644 --- a/library/src/scala/annotation/internal/requiresCapability.scala +++ b/library/src/scala/annotation/internal/requiresCapability.scala @@ -1,6 +1,8 @@ package scala.annotation.internal import annotation.StaticAnnotation +import language.experimental.captureChecking + /** An annotation to record a required capaility in the type of a throws */ class requiresCapability(capability: Any) extends StaticAnnotation diff --git a/library/src/scala/annotation/internal/sharable.scala b/library/src/scala/annotation/internal/sharable.scala index e2d6dcc61beb..862c57a383a3 100644 --- a/library/src/scala/annotation/internal/sharable.scala +++ b/library/src/scala/annotation/internal/sharable.scala @@ -1,5 +1,7 @@ package scala.annotation.internal +import language.experimental.captureChecking + import scala.annotation.Annotation /** An annotation indicating to `-Ycheck:reentrant` that a class or val can be safely shared. diff --git a/library/src/scala/annotation/internal/unshared.scala b/library/src/scala/annotation/internal/unshared.scala index 89796eba09e3..052e57a18454 100644 --- a/library/src/scala/annotation/internal/unshared.scala +++ b/library/src/scala/annotation/internal/unshared.scala @@ -1,5 +1,7 @@ package scala.annotation.internal +import language.experimental.captureChecking + import scala.annotation.Annotation /** An annotation indicating to `-Ycheck:reentrant` that an object will not be accessed from multiple threads. diff --git a/library/src/scala/annotation/publicInBinary.scala b/library/src/scala/annotation/publicInBinary.scala index a517f085dc7a..133e7f8d06b5 100644 --- a/library/src/scala/annotation/publicInBinary.scala +++ b/library/src/scala/annotation/publicInBinary.scala @@ -1,5 +1,7 @@ package scala.annotation +import language.experimental.captureChecking + /** A binary API is a definition that is annotated with `@publicInBinary`. * This annotation can be placed on `def`, `val`, `lazy val`, `var`, class constructors, `object`, and `given` definitions. * A binary API will be publicly available in the bytecode. Tools like TASTy MiMa will take this into account to check diff --git a/library/src/scala/annotation/retains.scala b/library/src/scala/annotation/retains.scala index 05896b117796..f526a17c053e 100644 --- a/library/src/scala/annotation/retains.scala +++ b/library/src/scala/annotation/retains.scala @@ -1,5 +1,7 @@ package scala.annotation +import language.experimental.captureChecking + /** An annotation that indicates capture of a set of references under capture checking. * * T @retains[x.type | y.type | z.type] diff --git a/library/src/scala/annotation/retainsByName.scala b/library/src/scala/annotation/retainsByName.scala index acc8ec664af3..b01f890f86b9 100644 --- a/library/src/scala/annotation/retainsByName.scala +++ b/library/src/scala/annotation/retainsByName.scala @@ -1,5 +1,7 @@ package scala.annotation +import language.experimental.captureChecking + /** An annotation that indicates capture of an enclosing by-name type */ @experimental class retainsByName[Elems] extends annotation.StaticAnnotation diff --git a/library/src/scala/annotation/static.scala b/library/src/scala/annotation/static.scala index 7cee5fc160d5..dc40ab9f3ac2 100644 --- a/library/src/scala/annotation/static.scala +++ b/library/src/scala/annotation/static.scala @@ -1,5 +1,7 @@ package scala.annotation +import language.experimental.captureChecking + import scala.annotation.meta.* /** https://github.com/scala/scala.github.com/pull/491 */ diff --git a/library/src/scala/annotation/targetName.scala b/library/src/scala/annotation/targetName.scala index 56e1361c73d7..17945fa86fb7 100644 --- a/library/src/scala/annotation/targetName.scala +++ b/library/src/scala/annotation/targetName.scala @@ -1,5 +1,7 @@ package scala.annotation +import language.experimental.captureChecking + /** An annotation that defines an external name for a definition. * If an `targetName(extname)` annotation is given for a method or some other * definition, its implementation will use the name `extname` instead of diff --git a/library/src/scala/annotation/threadUnsafe.scala b/library/src/scala/annotation/threadUnsafe.scala index 36fecec941c2..dd87e74f3048 100644 --- a/library/src/scala/annotation/threadUnsafe.scala +++ b/library/src/scala/annotation/threadUnsafe.scala @@ -1,5 +1,7 @@ package scala.annotation +import language.experimental.captureChecking + /** This annotation can only be used on a field which defines a lazy val. * When this annotation is used, the initialization of the lazy val will use a * faster mechanism which is not thread-safe. diff --git a/library/src/scala/annotation/transparentTrait.scala b/library/src/scala/annotation/transparentTrait.scala index ae6cb5bb1188..b82234708881 100644 --- a/library/src/scala/annotation/transparentTrait.scala +++ b/library/src/scala/annotation/transparentTrait.scala @@ -1,5 +1,7 @@ package scala.annotation +import language.experimental.captureChecking + /** An annotation that can be used from Scala 2 to mark a trait as transparent. * Scala 3 code would use the modifier `transparent` instead. Transparent traits * are not inferred when combined with other types in an intersection. diff --git a/library/src/scala/annotation/unchecked/uncheckedCaptures.scala b/library/src/scala/annotation/unchecked/uncheckedCaptures.scala index 477ac6d742f7..e494cef28a0e 100644 --- a/library/src/scala/annotation/unchecked/uncheckedCaptures.scala +++ b/library/src/scala/annotation/unchecked/uncheckedCaptures.scala @@ -1,6 +1,8 @@ package scala.annotation package unchecked +import language.experimental.captureChecking + /** An annotation for mutable variables that are allowed to capture * the root capability `cap`. Allowing this is not capture safe since * it can cause leakage of capabilities from local scopes by assigning diff --git a/library/src/scala/annotation/unroll.scala b/library/src/scala/annotation/unroll.scala index c37b7903d605..dcc279f93798 100644 --- a/library/src/scala/annotation/unroll.scala +++ b/library/src/scala/annotation/unroll.scala @@ -1,5 +1,7 @@ package scala.annotation +import language.experimental.captureChecking + @experimental("under review as part of SIP-61") /**The `@unroll` annotation is reserved for parameters of classes and methods. * diff --git a/library/src/scala/caps/Pure.scala b/library/src/scala/caps/Pure.scala index dfb8127bcc7f..05f7e64f884d 100644 --- a/library/src/scala/caps/Pure.scala +++ b/library/src/scala/caps/Pure.scala @@ -1,9 +1,11 @@ package scala package caps +import language.experimental.captureChecking + /** A marker trait that declares that all inheriting classes are "pure" in the * sense that their values retain no capabilities including capabilities needed * to perform effects. This has formal meaning only under capture checking. */ -trait Pure: +trait Pure extends Any: this: Pure => diff --git a/library/src/scala/caps/package.scala b/library/src/scala/caps/package.scala index ff029fbbae9c..2cd75efdf197 100644 --- a/library/src/scala/caps/package.scala +++ b/library/src/scala/caps/package.scala @@ -1,6 +1,8 @@ package scala package caps +import language.experimental.captureChecking + import annotation.{experimental, compileTimeOnly, retainsCap} /** @@ -123,16 +125,6 @@ final class reserve extends annotation.StaticAnnotation @experimental final class use extends annotation.StaticAnnotation -/** An annotations on parameters and update methods. - * On a parameter it states that any capabilties passed in the argument - * are no longer available afterwards, unless they are of class `SharableCapabilitty`. - * On an update method, it states that the `this` of the enclosing class is - * consumed, which means that any capabilities of the method prefix are - * no longer available afterwards. - */ -@experimental -final class consume extends annotation.StaticAnnotation - /** A trait that used to allow expressing existential types. Replaced by * root.Result instances. */ @@ -143,6 +135,11 @@ sealed trait Exists extends Capability @experimental object internal: + /** An annotation to reflect that a parameter or method carries the `consume` + * soft modifier. + */ + final class consume extends annotation.StaticAnnotation + /** An internal annotation placed on a refinement created by capture checking. * Refinements with this annotation unconditionally override any * info from the parent type, so no intersection needs to be formed. diff --git a/library/src/scala/collection/ArrayOps.scala b/library/src/scala/collection/ArrayOps.scala index c7f5ff67bd73..40570e5c261b 100644 --- a/library/src/scala/collection/ArrayOps.scala +++ b/library/src/scala/collection/ArrayOps.scala @@ -17,6 +17,8 @@ import scala.language.`2.13` import java.lang.Math.{max, min} import java.util.Arrays +import language.experimental.captureChecking + import scala.Predef.{ // unimport all array-related implicit conversions to avoid triggering them accidentally genericArrayOps => _, booleanArrayOps => _, @@ -103,7 +105,7 @@ object ArrayOps { * @return a new array resulting from applying the given collection-valued function * `f` to each element of this array and concatenating the results. */ - def flatMap[B: ClassTag](f: A => IterableOnce[B]): Array[B] = { + def flatMap[B: ClassTag](f: A => IterableOnce[B]^): Array[B] = { val b = ArrayBuilder.make[B] var i = 0 while(i < xs.length) { @@ -118,7 +120,7 @@ object ArrayOps { flatMap[B](x => asIterable(f(x))) /** Creates a new non-strict filter which combines this filter with the given predicate. */ - def withFilter(q: A => Boolean): WithFilter[A] = new WithFilter[A](a => p(a) && q(a), xs) + def withFilter(q: A => Boolean): WithFilter[A]^{this, q} = new WithFilter[A](a => p(a) && q(a), xs) } @SerialVersionUID(3L) @@ -179,7 +181,7 @@ object ArrayOps { private final val MaxStableSortLength = 300 /** Avoid an allocation in [[collect]]. */ - private val fallback: Any => Any = _ => fallback + private val fallback: Any -> Any = _ => fallback } /** This class serves as a wrapper for `Array`s with many of the operations found in @@ -368,7 +370,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { def inits: Iterator[Array[A]] = iterateUntilEmpty(xs => new ArrayOps(xs).init) // A helper for tails and inits. - private[this] def iterateUntilEmpty(f: Array[A] => Array[A]): Iterator[Array[A]] = + private[this] def iterateUntilEmpty(f: Array[A] => Array[A]): Iterator[Array[A]]^{f} = Iterator.iterate(xs)(f).takeWhile(x => x.length != 0) ++ Iterator.single(Array.empty[A]) /** An array containing the first `n` elements of this array. */ @@ -587,7 +589,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * @return an array consisting of the elements of this array * sorted according to the ordering `ord`. */ - def sorted[B >: A](implicit ord: Ordering[B]): Array[A] = { + def sorted[B >: A](implicit ord: Ordering[B]^): Array[A] = { val len = xs.length def boxed = if(len < ArrayOps.MaxStableSortLength) { val a = xs.clone() @@ -665,7 +667,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * All these operations apply to those elements of this array * which satisfy the predicate `p`. */ - def withFilter(p: A => Boolean): ArrayOps.WithFilter[A] = new ArrayOps.WithFilter[A](p, xs) + def withFilter(p: A => Boolean): ArrayOps.WithFilter[A]^{p} = new ArrayOps.WithFilter[A](p, xs) /** Finds index of first occurrence of some value in this array after or at some start index. * @@ -965,7 +967,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * @return a new array resulting from applying the given collection-valued function * `f` to each element of this array and concatenating the results. */ - def flatMap[B : ClassTag](f: A => IterableOnce[B]): Array[B] = { + def flatMap[B : ClassTag](f: A => IterableOnce[B]^): Array[B] = { val b = ArrayBuilder.make[B] var i = 0 while(i < xs.length) { @@ -975,7 +977,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { b.result() } - def flatMap[BS, B](f: A => BS)(implicit asIterable: BS => Iterable[B], m: ClassTag[B]): Array[B] = + def flatMap[BS, B](f: A => BS)(implicit asIterable: BS => Iterable[B]^, m: ClassTag[B]): Array[B] = flatMap[B](x => asIterable(f(x))) /** Flattens a two-dimensional array by concatenating all its rows @@ -985,7 +987,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * @param asIterable A function that converts elements of this array to rows - Iterables of type `B`. * @return An array obtained by concatenating rows of this array. */ - def flatten[B](implicit asIterable: A => IterableOnce[B], m: ClassTag[B]): Array[B] = { + def flatten[B](implicit asIterable: A => IterableOnce[B]^, m: ClassTag[B]): Array[B] = { val b = ArrayBuilder.make[B] val len = xs.length var size = 0 @@ -1018,7 +1020,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * `pf` to each element on which it is defined and collecting the results. * The order of the elements is preserved. */ - def collect[B: ClassTag](pf: PartialFunction[A, B]): Array[B] = { + def collect[B: ClassTag](pf: PartialFunction[A, B]^): Array[B] = { val fallback: Any => Any = ArrayOps.fallback val b = ArrayBuilder.make[B] var i = 0 @@ -1032,7 +1034,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { /** Finds the first element of the array for which the given partial function is defined, and applies the * partial function to it. */ - def collectFirst[B](@deprecatedName("f","2.13.9") pf: PartialFunction[A, B]): Option[B] = { + def collectFirst[B](@deprecatedName("f","2.13.9") pf: PartialFunction[A, B]^): Option[B] = { val fallback: Any => Any = ArrayOps.fallback var i = 0 while (i < xs.length) { @@ -1052,7 +1054,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * @return a new array containing pairs consisting of corresponding elements of this array and `that`. * The length of the returned array is the minimum of the lengths of this array and `that`. */ - def zip[B](that: IterableOnce[B]): Array[(A, B)] = { + def zip[B](that: IterableOnce[B]^): Array[(A, B)] = { val b = new ArrayBuilder.ofRef[(A, B)]() val k = that.knownSize b.sizeHint(if(k >= 0) min(k, xs.length) else xs.length) @@ -1082,7 +1084,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * @return a decorator `LazyZip2` that allows strict operations to be performed on the lazily evaluated pairs * or chained calls to `lazyZip`. Implicit conversion to `Iterable[(A, B)]` is also supported. */ - def lazyZip[B](that: Iterable[B]): LazyZip2[A, B, Array[A]] = new LazyZip2(xs, immutable.ArraySeq.unsafeWrapArray(xs), that) + def lazyZip[B](that: Iterable[B]^): LazyZip2[A, B, Array[A]]^{that} = new LazyZip2(xs, immutable.ArraySeq.unsafeWrapArray(xs), that) /** Returns an array formed from this array and another iterable collection * by combining corresponding elements in pairs. @@ -1097,7 +1099,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * If this array is shorter than `that`, `thisElem` values are used to pad the result. * If `that` is shorter than this array, `thatElem` values are used to pad the result. */ - def zipAll[A1 >: A, B](that: Iterable[B], thisElem: A1, thatElem: B): Array[(A1, B)] = { + def zipAll[A1 >: A, B](that: Iterable[B]^, thisElem: A1, thatElem: B): Array[(A1, B)] = { val b = new ArrayBuilder.ofRef[(A1, B)]() val k = that.knownSize b.sizeHint(max(k, xs.length)) @@ -1153,7 +1155,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { @`inline` final def +: [B >: A : ClassTag](x: B): Array[B] = prepended(x) /** A copy of this array with all elements of a collection prepended. */ - def prependedAll[B >: A : ClassTag](prefix: IterableOnce[B]): Array[B] = { + def prependedAll[B >: A : ClassTag](prefix: IterableOnce[B]^): Array[B] = { val b = ArrayBuilder.make[B] val k = prefix.knownSize if(k >= 0) b.sizeHint(k + xs.length) @@ -1170,12 +1172,12 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { dest } - @`inline` final def ++: [B >: A : ClassTag](prefix: IterableOnce[B]): Array[B] = prependedAll(prefix) + @`inline` final def ++: [B >: A : ClassTag](prefix: IterableOnce[B]^): Array[B] = prependedAll(prefix) @`inline` final def ++: [B >: A : ClassTag](prefix: Array[_ <: B]): Array[B] = prependedAll(prefix) /** A copy of this array with all elements of a collection appended. */ - def appendedAll[B >: A : ClassTag](suffix: IterableOnce[B]): Array[B] = { + def appendedAll[B >: A : ClassTag](suffix: IterableOnce[B]^): Array[B] = { val b = ArrayBuilder.make[B] b.sizeHint(suffix, delta = xs.length) b.addAll(xs) @@ -1190,15 +1192,15 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { dest } - @`inline` final def :++ [B >: A : ClassTag](suffix: IterableOnce[B]): Array[B] = appendedAll(suffix) + @`inline` final def :++ [B >: A : ClassTag](suffix: IterableOnce[B]^): Array[B] = appendedAll(suffix) @`inline` final def :++ [B >: A : ClassTag](suffix: Array[_ <: B]): Array[B] = appendedAll(suffix) - @`inline` final def concat[B >: A : ClassTag](suffix: IterableOnce[B]): Array[B] = appendedAll(suffix) + @`inline` final def concat[B >: A : ClassTag](suffix: IterableOnce[B]^): Array[B] = appendedAll(suffix) @`inline` final def concat[B >: A : ClassTag](suffix: Array[_ <: B]): Array[B] = appendedAll(suffix) - @`inline` final def ++[B >: A : ClassTag](xs: IterableOnce[B]): Array[B] = appendedAll(xs) + @`inline` final def ++[B >: A : ClassTag](xs: IterableOnce[B]^): Array[B] = appendedAll(xs) @`inline` final def ++[B >: A : ClassTag](xs: Array[_ <: B]): Array[B] = appendedAll(xs) @@ -1219,7 +1221,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * @param other The patch values * @param replaced The number of values in the original array that are replaced by the patch. */ - def patch[B >: A : ClassTag](from: Int, other: IterableOnce[B], replaced: Int): Array[B] = { + def patch[B >: A : ClassTag](from: Int, other: IterableOnce[B]^, replaced: Int): Array[B] = { val b = ArrayBuilder.make[B] val k = other.knownSize val r = if(replaced < 0) 0 else replaced @@ -1347,7 +1349,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * @tparam B the type of the elements after being transformed by `f` * @return a new array consisting of all the elements of this array without duplicates. */ - def distinctBy[B](f: A => B): Array[A] = + def distinctBy[B](f: A -> B): Array[A] = ArrayBuilder.make[A].addAll(iterator.distinctBy(f)).result() /** A copy of this array with an element value appended until a given target length is reached. @@ -1655,7 +1657,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * @return `true` if the sequence `that` is contained in this array at * index `offset`, otherwise `false`. */ - def startsWith[B >: A](that: IterableOnce[B], offset: Int = 0): Boolean = mutable.ArraySeq.make(xs).startsWith(that, offset) + def startsWith[B >: A](that: IterableOnce[B]^, offset: Int = 0): Boolean = mutable.ArraySeq.make(xs).startsWith(that, offset) // we have another overload here, so we need to duplicate this method /** Tests whether this array ends with the given sequence. @@ -1663,5 +1665,5 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * @param that the sequence to test * @return `true` if this array has `that` as a suffix, `false` otherwise. */ - def endsWith[B >: A](that: Iterable[B]): Boolean = mutable.ArraySeq.make(xs).endsWith(that) + def endsWith[B >: A](that: Iterable[B]^): Boolean = mutable.ArraySeq.make(xs).endsWith(that) } diff --git a/library/src/scala/collection/BitSet.scala b/library/src/scala/collection/BitSet.scala index 98beab6ae521..dc3d3b7ab7e7 100644 --- a/library/src/scala/collection/BitSet.scala +++ b/library/src/scala/collection/BitSet.scala @@ -14,6 +14,8 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import java.io.{ObjectInputStream, ObjectOutputStream} import scala.annotation.nowarn @@ -33,8 +35,8 @@ import scala.collection.mutable.Builder * @define coll bitset * @define Coll `BitSet` */ -trait BitSet extends SortedSet[Int] with BitSetOps[BitSet] { - override protected def fromSpecific(coll: IterableOnce[Int]): BitSet = bitSetFactory.fromSpecific(coll) +trait BitSet extends SortedSet[Int] with BitSetOps[BitSet] { self: BitSet => + override protected def fromSpecific(coll: IterableOnce[Int]^): BitSet = bitSetFactory.fromSpecific(coll) override protected def newSpecificBuilder: Builder[Int, BitSet] = bitSetFactory.newBuilder override def empty: BitSet = bitSetFactory.empty @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") @@ -49,7 +51,7 @@ object BitSet extends SpecificIterableFactory[Int, BitSet] { def empty: BitSet = immutable.BitSet.empty def newBuilder: Builder[Int, BitSet] = immutable.BitSet.newBuilder - def fromSpecific(it: IterableOnce[Int]): BitSet = immutable.BitSet.fromSpecific(it) + def fromSpecific(it: IterableOnce[Int]^): BitSet = immutable.BitSet.fromSpecific(it) @SerialVersionUID(3L) private[collection] abstract class SerializationProxy(@transient protected val coll: BitSet) extends Serializable { @@ -243,7 +245,7 @@ transparent trait BitSetOps[+C <: BitSet with BitSetOps[C]] coll.fromBitMaskNoCopy(a) } - override def concat(other: collection.IterableOnce[Int]): C = other match { + override def concat(other: collection.IterableOnce[Int]^): C = other match { case otherBitset: BitSet => val len = coll.nwords max otherBitset.nwords val words = new Array[Long](len) @@ -298,9 +300,9 @@ transparent trait BitSetOps[+C <: BitSet with BitSetOps[C]] */ def map(f: Int => Int): C = fromSpecific(new View.Map(this, f)) - def flatMap(f: Int => IterableOnce[Int]): C = fromSpecific(new View.FlatMap(this, f)) + def flatMap(f: Int => IterableOnce[Int]^): C = fromSpecific(new View.FlatMap(this, f)) - def collect(pf: PartialFunction[Int, Int]): C = fromSpecific(super[SortedSetOps].collect(pf)) + def collect(pf: PartialFunction[Int, Int]^): C = fromSpecific(super[SortedSetOps].collect(pf)) override def partition(p: Int => Boolean): (C, C) = { val left = filter(p) diff --git a/library/src/scala/collection/BufferedIterator.scala b/library/src/scala/collection/BufferedIterator.scala index e933ffcd4c16..4076b9662940 100644 --- a/library/src/scala/collection/BufferedIterator.scala +++ b/library/src/scala/collection/BufferedIterator.scala @@ -13,6 +13,7 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking /** Buffered iterators are iterators which provide a method `head` * that inspects the next element without discarding it. diff --git a/library/src/scala/collection/BuildFrom.scala b/library/src/scala/collection/BuildFrom.scala index 1a623228db55..8ee65badf1a4 100644 --- a/library/src/scala/collection/BuildFrom.scala +++ b/library/src/scala/collection/BuildFrom.scala @@ -13,6 +13,8 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.implicitNotFound import scala.collection.mutable.Builder import scala.collection.immutable.WrappedString @@ -27,7 +29,7 @@ import scala.reflect.ClassTag */ @implicitNotFound(msg = "Cannot construct a collection of type ${C} with elements of type ${A} based on a collection of type ${From}.") trait BuildFrom[-From, -A, +C] extends Any { self => - def fromSpecific(from: From)(it: IterableOnce[A]): C + def fromSpecific(from: From)(it: IterableOnce[A]^): C^{it} /** Get a Builder for the collection. For non-strict collection types this will use an intermediate buffer. * Building collections with `fromSpecific` is preferred because it can be lazy for lazy collections. */ @@ -38,7 +40,7 @@ trait BuildFrom[-From, -A, +C] extends Any { self => /** Partially apply a BuildFrom to a Factory */ def toFactory(from: From): Factory[A, C] = new Factory[A, C] { - def fromSpecific(it: IterableOnce[A]): C = self.fromSpecific(from)(it) + def fromSpecific(it: IterableOnce[A]^): C^{it} = self.fromSpecific(from)(it) def newBuilder: Builder[A, C] = self.newBuilder(from) } } @@ -49,42 +51,42 @@ object BuildFrom extends BuildFromLowPriority1 { implicit def buildFromMapOps[CC[X, Y] <: Map[X, Y] with MapOps[X, Y, CC, _], K0, V0, K, V]: BuildFrom[CC[K0, V0] with Map[K0, V0], (K, V), CC[K, V] with Map[K, V]] = new BuildFrom[CC[K0, V0], (K, V), CC[K, V]] { //TODO: Reuse a prototype instance def newBuilder(from: CC[K0, V0]): Builder[(K, V), CC[K, V]] = (from: MapOps[K0, V0, CC, _]).mapFactory.newBuilder[K, V] - def fromSpecific(from: CC[K0, V0])(it: IterableOnce[(K, V)]): CC[K, V] = (from: MapOps[K0, V0, CC, _]).mapFactory.from(it) + def fromSpecific(from: CC[K0, V0])(it: IterableOnce[(K, V)]^): CC[K, V] = (from: MapOps[K0, V0, CC, _]).mapFactory.from(it) } /** Build the source collection type from a SortedMapOps */ implicit def buildFromSortedMapOps[CC[X, Y] <: SortedMap[X, Y] with SortedMapOps[X, Y, CC, _], K0, V0, K : Ordering, V]: BuildFrom[CC[K0, V0] with SortedMap[K0, V0], (K, V), CC[K, V] with SortedMap[K, V]] = new BuildFrom[CC[K0, V0], (K, V), CC[K, V]] { def newBuilder(from: CC[K0, V0]): Builder[(K, V), CC[K, V]] = (from: SortedMapOps[K0, V0, CC, _]).sortedMapFactory.newBuilder[K, V] - def fromSpecific(from: CC[K0, V0])(it: IterableOnce[(K, V)]): CC[K, V] = (from: SortedMapOps[K0, V0, CC, _]).sortedMapFactory.from(it) + def fromSpecific(from: CC[K0, V0])(it: IterableOnce[(K, V)]^): CC[K, V] = (from: SortedMapOps[K0, V0, CC, _]).sortedMapFactory.from(it) } implicit def buildFromBitSet[C <: BitSet with BitSetOps[C]]: BuildFrom[C, Int, C] = new BuildFrom[C, Int, C] { - def fromSpecific(from: C)(it: IterableOnce[Int]): C = from.bitSetFactory.fromSpecific(it) + def fromSpecific(from: C)(it: IterableOnce[Int]^): C = from.bitSetFactory.fromSpecific(it) def newBuilder(from: C): Builder[Int, C] = from.bitSetFactory.newBuilder } implicit val buildFromString: BuildFrom[String, Char, String] = new BuildFrom[String, Char, String] { - def fromSpecific(from: String)(it: IterableOnce[Char]): String = Factory.stringFactory.fromSpecific(it) + def fromSpecific(from: String)(it: IterableOnce[Char]^): String = Factory.stringFactory.fromSpecific(it) def newBuilder(from: String): Builder[Char, String] = Factory.stringFactory.newBuilder } implicit val buildFromWrappedString: BuildFrom[WrappedString, Char, WrappedString] = new BuildFrom[WrappedString, Char, WrappedString] { - def fromSpecific(from: WrappedString)(it: IterableOnce[Char]): WrappedString = WrappedString.fromSpecific(it) + def fromSpecific(from: WrappedString)(it: IterableOnce[Char]^): WrappedString = WrappedString.fromSpecific(it) def newBuilder(from: WrappedString): mutable.Builder[Char, WrappedString] = WrappedString.newBuilder } implicit def buildFromArray[A : ClassTag]: BuildFrom[Array[_], A, Array[A]] = new BuildFrom[Array[_], A, Array[A]] { - def fromSpecific(from: Array[_])(it: IterableOnce[A]): Array[A] = Factory.arrayFactory[A].fromSpecific(it) + def fromSpecific(from: Array[_])(it: IterableOnce[A]^): Array[A] = Factory.arrayFactory[A].fromSpecific(it) def newBuilder(from: Array[_]): Builder[A, Array[A]] = Factory.arrayFactory[A].newBuilder } implicit def buildFromView[A, B]: BuildFrom[View[A], B, View[B]] = new BuildFrom[View[A], B, View[B]] { - def fromSpecific(from: View[A])(it: IterableOnce[B]): View[B] = View.from(it) + def fromSpecific(from: View[A])(it: IterableOnce[B]^): View[B]^{it} = View.from(it) def newBuilder(from: View[A]): Builder[B, View[B]] = View.newBuilder } @@ -98,12 +100,12 @@ trait BuildFromLowPriority1 extends BuildFromLowPriority2 { // test in test/junit/scala/collection/BuildFromTest.scala and discussion in https://github.com/scala/scala/pull/10209 implicit def buildFromSortedSetOps[CC[X] <: SortedSet[X] with SortedSetOps[X, CC, _], A0, A : Ordering]: BuildFrom[CC[A0] with SortedSet[A0], A, CC[A] with SortedSet[A]] = new BuildFrom[CC[A0], A, CC[A]] { def newBuilder(from: CC[A0]): Builder[A, CC[A]] = (from: SortedSetOps[A0, CC, _]).sortedIterableFactory.newBuilder[A] - def fromSpecific(from: CC[A0])(it: IterableOnce[A]): CC[A] = (from: SortedSetOps[A0, CC, _]).sortedIterableFactory.from(it) + def fromSpecific(from: CC[A0])(it: IterableOnce[A]^): CC[A] = (from: SortedSetOps[A0, CC, _]).sortedIterableFactory.from(it) } implicit def fallbackStringCanBuildFrom[A]: BuildFrom[String, A, immutable.IndexedSeq[A]] = new BuildFrom[String, A, immutable.IndexedSeq[A]] { - def fromSpecific(from: String)(it: IterableOnce[A]): immutable.IndexedSeq[A] = immutable.IndexedSeq.from(it) + def fromSpecific(from: String)(it: IterableOnce[A]^): immutable.IndexedSeq[A] = immutable.IndexedSeq.from(it) def newBuilder(from: String): Builder[A, immutable.IndexedSeq[A]] = immutable.IndexedSeq.newBuilder[A] } } @@ -113,11 +115,11 @@ trait BuildFromLowPriority2 { implicit def buildFromIterableOps[CC[X] <: Iterable[X] with IterableOps[X, CC, _], A0, A]: BuildFrom[CC[A0], A, CC[A]] = new BuildFrom[CC[A0], A, CC[A]] { //TODO: Reuse a prototype instance def newBuilder(from: CC[A0]): Builder[A, CC[A]] = (from: IterableOps[A0, CC, _]).iterableFactory.newBuilder[A] - def fromSpecific(from: CC[A0])(it: IterableOnce[A]): CC[A] = (from: IterableOps[A0, CC, _]).iterableFactory.from(it) + def fromSpecific(from: CC[A0])(it: IterableOnce[A]^): CC[A]^{it} = (from: IterableOps[A0, CC, _]).iterableFactory.from(it) } implicit def buildFromIterator[A]: BuildFrom[Iterator[_], A, Iterator[A]] = new BuildFrom[Iterator[_], A, Iterator[A]] { def newBuilder(from: Iterator[_]): mutable.Builder[A, Iterator[A]] = Iterator.newBuilder - def fromSpecific(from: Iterator[_])(it: IterableOnce[A]): Iterator[A] = Iterator.from(it) + def fromSpecific(from: Iterator[_])(it: IterableOnce[A]^): Iterator[A]^{it} = Iterator.from(it) } } diff --git a/library/src/scala/collection/DefaultMap.scala b/library/src/scala/collection/DefaultMap.scala index d957a4ef245e..08549fd8f975 100644 --- a/library/src/scala/collection/DefaultMap.scala +++ b/library/src/scala/collection/DefaultMap.scala @@ -14,6 +14,7 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking /** A default map which builds a default `immutable.Map` implementation for all * transformations. diff --git a/library/src/scala/collection/Factory.scala b/library/src/scala/collection/Factory.scala index 8e40f2955853..067d9633f8e9 100644 --- a/library/src/scala/collection/Factory.scala +++ b/library/src/scala/collection/Factory.scala @@ -14,6 +14,8 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.immutable.NumericRange import scala.language.implicitConversions import scala.collection.mutable.Builder @@ -30,26 +32,31 @@ import scala.reflect.ClassTag * @tparam A Type of elements (e.g. `Int`, `Boolean`, etc.) * @tparam C Type of collection (e.g. `List[Int]`, `TreeMap[Int, String]`, etc.) */ -trait Factory[-A, +C] extends Any { +trait Factory[-A, +C] extends Any { self => /** * @return A collection of type `C` containing the same elements * as the source collection `it`. * @param it Source collection */ - def fromSpecific(it: IterableOnce[A]): C + def fromSpecific(it: IterableOnce[A]^): C^{it} /** Get a Builder for the collection. For non-strict collection types this will use an intermediate buffer. * Building collections with `fromSpecific` is preferred because it can be lazy for lazy collections. */ def newBuilder: Builder[A, C] } +/** A strict version of [[Factory]], where the returned collection from [[fromSpecific]] do not capture its input. */ +trait StrictFactory[-A, +C] extends Factory[A, C] { + override def fromSpecific(it: IterableOnce[A]^): C +} + object Factory { - implicit val stringFactory: Factory[Char, String] = new StringFactory + implicit val stringFactory: StrictFactory[Char, String] = new StringFactory @SerialVersionUID(3L) - private class StringFactory extends Factory[Char, String] with Serializable { - def fromSpecific(it: IterableOnce[Char]): String = { + private class StringFactory extends StrictFactory[Char, String] with Serializable { + def fromSpecific(it: IterableOnce[Char]^): String = { val b = new mutable.StringBuilder(scala.math.max(0, it.knownSize)) b ++= it b.result() @@ -57,10 +64,10 @@ object Factory { def newBuilder: Builder[Char, String] = new mutable.StringBuilder() } - implicit def arrayFactory[A: ClassTag]: Factory[A, Array[A]] = new ArrayFactory[A] + implicit def arrayFactory[A: ClassTag]: StrictFactory[A, Array[A]] = new ArrayFactory[A] @SerialVersionUID(3L) - private class ArrayFactory[A: ClassTag] extends Factory[A, Array[A]] with Serializable { - def fromSpecific(it: IterableOnce[A]): Array[A] = { + private class ArrayFactory[A: ClassTag] extends StrictFactory[A, Array[A]] with Serializable { + def fromSpecific(it: IterableOnce[A]^): Array[A] = { val b = newBuilder b.sizeHint(it, delta = 0) b ++= it @@ -81,7 +88,7 @@ object Factory { * @define coll collection * @define Coll `Iterable` */ -trait IterableFactory[+CC[_]] extends Serializable { +trait IterableFactory[+CC[_]] extends Serializable, caps.Pure { /** Creates a target $coll from an existing source collection * @@ -89,7 +96,7 @@ trait IterableFactory[+CC[_]] extends Serializable { * @tparam A the type of the collection’s elements * @return a new $coll with the elements of `source` */ - def from[A](source: IterableOnce[A]): CC[A] + def from[A](source: IterableOnce[A]^): CC[A]^{source} /** An empty $coll * @tparam A the type of the ${coll}'s elements @@ -110,7 +117,7 @@ trait IterableFactory[+CC[_]] extends Serializable { * @param f the function that's repeatedly applied * @return a $coll with `len` values in the sequence `start, f(start), f(f(start)), ...` */ - def iterate[A](start: A, len: Int)(f: A => A): CC[A] = from(new View.Iterate(start, len)(f)) + def iterate[A](start: A, len: Int)(f: A => A): CC[A]^{f} = from(new View.Iterate(start, len)(f)) /** Produces a $coll that uses a function `f` to produce elements of type `A` * and update an internal state of type `S`. @@ -122,7 +129,7 @@ trait IterableFactory[+CC[_]] extends Serializable { * @tparam S Type of the internal state * @return a $coll that produces elements using `f` until `f` returns `None` */ - def unfold[A, S](init: S)(f: S => Option[(A, S)]): CC[A] = from(new View.Unfold(init)(f)) + def unfold[A, S](init: S)(f: S => Option[(A, S)]): CC[A]^{f} = from(new View.Unfold(init)(f)) /** Produces a $coll containing a sequence of increasing of integers. * @@ -151,7 +158,7 @@ trait IterableFactory[+CC[_]] extends Serializable { * @param elem the element computation * @return A $coll that contains the results of `n` evaluations of `elem`. */ - def fill[A](n: Int)(elem: => A): CC[A] = from(new View.Fill(n)(elem)) + def fill[A](n: Int)(elem: => A): CC[A]^{elem} = from(new View.Fill(n)(elem)) /** Produces a two-dimensional $coll containing the results of some element computation a number of times. * @param n1 the number of elements in the 1st dimension @@ -159,7 +166,7 @@ trait IterableFactory[+CC[_]] extends Serializable { * @param elem the element computation * @return A $coll that contains the results of `n1 x n2` evaluations of `elem`. */ - def fill[A](n1: Int, n2: Int)(elem: => A): CC[CC[A] @uncheckedVariance] = fill(n1)(fill(n2)(elem)) + def fill[A](n1: Int, n2: Int)(elem: => A): CC[(CC[A]^{elem}) @uncheckedVariance]^{elem} = fill(n1)(fill(n2)(elem)) /** Produces a three-dimensional $coll containing the results of some element computation a number of times. * @param n1 the number of elements in the 1st dimension @@ -168,7 +175,7 @@ trait IterableFactory[+CC[_]] extends Serializable { * @param elem the element computation * @return A $coll that contains the results of `n1 x n2 x n3` evaluations of `elem`. */ - def fill[A](n1: Int, n2: Int, n3: Int)(elem: => A): CC[CC[CC[A]] @uncheckedVariance] = fill(n1)(fill(n2, n3)(elem)) + def fill[A](n1: Int, n2: Int, n3: Int)(elem: => A): CC[(CC[CC[A]^{elem}]^{elem}) @uncheckedVariance]^{elem} = fill(n1)(fill(n2, n3)(elem)) /** Produces a four-dimensional $coll containing the results of some element computation a number of times. * @param n1 the number of elements in the 1st dimension @@ -178,7 +185,7 @@ trait IterableFactory[+CC[_]] extends Serializable { * @param elem the element computation * @return A $coll that contains the results of `n1 x n2 x n3 x n4` evaluations of `elem`. */ - def fill[A](n1: Int, n2: Int, n3: Int, n4: Int)(elem: => A): CC[CC[CC[CC[A]]] @uncheckedVariance] = + def fill[A](n1: Int, n2: Int, n3: Int, n4: Int)(elem: => A): CC[(CC[CC[CC[A]^{elem}]^{elem}]^{elem}) @uncheckedVariance]^{elem} = fill(n1)(fill(n2, n3, n4)(elem)) /** Produces a five-dimensional $coll containing the results of some element computation a number of times. @@ -190,7 +197,7 @@ trait IterableFactory[+CC[_]] extends Serializable { * @param elem the element computation * @return A $coll that contains the results of `n1 x n2 x n3 x n4 x n5` evaluations of `elem`. */ - def fill[A](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(elem: => A): CC[CC[CC[CC[CC[A]]]] @uncheckedVariance] = + def fill[A](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(elem: => A): CC[(CC[CC[CC[CC[A]^{elem}]^{elem}]^{elem}]^{elem}) @uncheckedVariance]^{elem} = fill(n1)(fill(n2, n3, n4, n5)(elem)) /** Produces a $coll containing values of a given function over a range of integer values starting from 0. @@ -198,7 +205,7 @@ trait IterableFactory[+CC[_]] extends Serializable { * @param f The function computing element values * @return A $coll consisting of elements `f(0), ..., f(n -1)` */ - def tabulate[A](n: Int)(f: Int => A): CC[A] = from(new View.Tabulate(n)(f)) + def tabulate[A](n: Int)(f: Int => A): CC[A]^{f} = from(new View.Tabulate(n)(f)) /** Produces a two-dimensional $coll containing values of a given function over ranges of integer values starting from 0. * @param n1 the number of elements in the 1st dimension @@ -207,7 +214,7 @@ trait IterableFactory[+CC[_]] extends Serializable { * @return A $coll consisting of elements `f(i1, i2)` * for `0 <= i1 < n1` and `0 <= i2 < n2`. */ - def tabulate[A](n1: Int, n2: Int)(f: (Int, Int) => A): CC[CC[A] @uncheckedVariance] = + def tabulate[A](n1: Int, n2: Int)(f: (Int, Int) => A): CC[(CC[A]^{f}) @uncheckedVariance]^{f} = tabulate(n1)(i1 => tabulate(n2)(f(i1, _))) /** Produces a three-dimensional $coll containing values of a given function over ranges of integer values starting from 0. @@ -218,7 +225,7 @@ trait IterableFactory[+CC[_]] extends Serializable { * @return A $coll consisting of elements `f(i1, i2, i3)` * for `0 <= i1 < n1`, `0 <= i2 < n2`, and `0 <= i3 < n3`. */ - def tabulate[A](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => A): CC[CC[CC[A]] @uncheckedVariance] = + def tabulate[A](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => A): CC[(CC[CC[A]^{f}]^{f}) @uncheckedVariance]^{f} = tabulate(n1)(i1 => tabulate(n2, n3)(f(i1, _, _))) /** Produces a four-dimensional $coll containing values of a given function over ranges of integer values starting from 0. @@ -230,7 +237,7 @@ trait IterableFactory[+CC[_]] extends Serializable { * @return A $coll consisting of elements `f(i1, i2, i3, i4)` * for `0 <= i1 < n1`, `0 <= i2 < n2`, `0 <= i3 < n3`, and `0 <= i4 < n4`. */ - def tabulate[A](n1: Int, n2: Int, n3: Int, n4: Int)(f: (Int, Int, Int, Int) => A): CC[CC[CC[CC[A]]] @uncheckedVariance] = + def tabulate[A](n1: Int, n2: Int, n3: Int, n4: Int)(f: (Int, Int, Int, Int) => A): CC[(CC[CC[CC[A]^{f}]^{f}]^{f}) @uncheckedVariance]^{f} = tabulate(n1)(i1 => tabulate(n2, n3, n4)(f(i1, _, _, _))) /** Produces a five-dimensional $coll containing values of a given function over ranges of integer values starting from 0. @@ -243,7 +250,7 @@ trait IterableFactory[+CC[_]] extends Serializable { * @return A $coll consisting of elements `f(i1, i2, i3, i4, i5)` * for `0 <= i1 < n1`, `0 <= i2 < n2`, `0 <= i3 < n3`, `0 <= i4 < n4`, and `0 <= i5 < n5`. */ - def tabulate[A](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(f: (Int, Int, Int, Int, Int) => A): CC[CC[CC[CC[CC[A]]]] @uncheckedVariance] = + def tabulate[A](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(f: (Int, Int, Int, Int, Int) => A): CC[(CC[CC[CC[CC[A]^{f}]^{f}]^{f}]^{f}) @uncheckedVariance]^{f} = tabulate(n1)(i1 => tabulate(n2, n3, n4, n5)(f(i1, _, _, _, _))) /** Concatenates all argument collections into a single $coll. @@ -272,13 +279,13 @@ object IterableFactory { @SerialVersionUID(3L) private[this] class ToFactory[A, CC[_]](factory: IterableFactory[CC]) extends Factory[A, CC[A]] with Serializable { - def fromSpecific(it: IterableOnce[A]): CC[A] = factory.from[A](it) + def fromSpecific(it: IterableOnce[A]^): CC[A]^{it} = factory.from[A](it) def newBuilder: Builder[A, CC[A]] = factory.newBuilder[A] } implicit def toBuildFrom[A, CC[_]](factory: IterableFactory[CC]): BuildFrom[Any, A, CC[A]] = new BuildFrom[Any, A, CC[A]] { - def fromSpecific(from: Any)(it: IterableOnce[A]) = factory.from(it) + def fromSpecific(from: Any)(it: IterableOnce[A]^) = factory.from(it) def newBuilder(from: Any) = factory.newBuilder } @@ -286,25 +293,65 @@ object IterableFactory { class Delegate[CC[_]](delegate: IterableFactory[CC]) extends IterableFactory[CC] { override def apply[A](elems: A*): CC[A] = delegate.apply(elems: _*) def empty[A]: CC[A] = delegate.empty - def from[E](it: IterableOnce[E]): CC[E] = delegate.from(it) + def from[E](it: IterableOnce[E]^): CC[E]^{it} = delegate.from(it) def newBuilder[A]: Builder[A, CC[A]] = delegate.newBuilder[A] } } +/** IterableFactory but with strict operations that do not capture the inputs. */ +trait StrictIterableFactory[+CC[_] <: caps.Pure] extends IterableFactory[CC] { + // pure overrides of the IterableFactory methods + override def from[A](source: IterableOnce[A]^): CC[A] + + override def iterate[A](start: A, len: Int)(f: A => A): CC[A] = from(new View.Iterate(start, len)(f)) + + override def unfold[A, S](init: S)(f: S => Option[(A, S)]): CC[A] = from(new View.Unfold(init)(f)) + + override def fill[A](n: Int)(elem: => A): CC[A] = from(new View.Fill(n)(elem)) + + override def fill[A](n1: Int, n2: Int)(elem: => A): CC[CC[A] @uncheckedVariance] = fill(n1)(fill(n2)(elem)) + + override def fill[A](n1: Int, n2: Int, n3: Int)(elem: => A): CC[CC[CC[A]] @uncheckedVariance] = fill(n1)(fill(n2, n3)(elem)) + + override def fill[A](n1: Int, n2: Int, n3: Int, n4: Int)(elem: => A): CC[CC[CC[CC[A]]] @uncheckedVariance] = + fill(n1)(fill(n2, n3, n4)(elem)) + + override def fill[A](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(elem: => A): CC[CC[CC[CC[CC[A]]]] @uncheckedVariance] = + fill(n1)(fill(n2, n3, n4, n5)(elem)) + + override def tabulate[A](n: Int)(f: Int => A): CC[A] = from(new View.Tabulate(n)(f)) + + override def tabulate[A](n1: Int, n2: Int)(f: (Int, Int) => A): CC[CC[A] @uncheckedVariance] = + tabulate(n1)(i1 => tabulate(n2)(f(i1, _))) + + override def tabulate[A](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => A): CC[CC[CC[A]] @uncheckedVariance] = + tabulate(n1)(i1 => tabulate(n2, n3)(f(i1, _, _))) + + override def tabulate[A](n1: Int, n2: Int, n3: Int, n4: Int)(f: (Int, Int, Int, Int) => A): CC[CC[CC[CC[A]]] @uncheckedVariance] = + tabulate(n1)(i1 => tabulate(n2, n3, n4)(f(i1, _, _, _))) + + override def tabulate[A](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(f: (Int, Int, Int, Int, Int) => A): CC[CC[CC[CC[CC[A]]]] @uncheckedVariance] = + tabulate(n1)(i1 => tabulate(n2, n3, n4, n5)(f(i1, _, _, _, _))) + + override def concat[A](xss: Iterable[A]*): CC[A] = { + from(xss.foldLeft(View.empty[A])(_ ++ _)) + } +} + /** * @tparam CC Collection type constructor (e.g. `List`) */ -trait SeqFactory[+CC[A] <: SeqOps[A, Seq, Seq[A]]] extends IterableFactory[CC] { +trait SeqFactory[+CC[A] <: StrictSeqOps[A, Seq, Seq[A]]] extends StrictIterableFactory[CC] { import SeqFactory.UnapplySeqWrapper final def unapplySeq[A](x: CC[A] @uncheckedVariance): UnapplySeqWrapper[A] = new UnapplySeqWrapper(x) // TODO is uncheckedVariance sound here? } object SeqFactory { @SerialVersionUID(3L) - class Delegate[CC[A] <: SeqOps[A, Seq, Seq[A]]](delegate: SeqFactory[CC]) extends SeqFactory[CC] { + class Delegate[CC[A] <: StrictSeqOps[A, Seq, Seq[A]]](delegate: SeqFactory[CC]) extends SeqFactory[CC] { override def apply[A](elems: A*): CC[A] = delegate.apply(elems: _*) def empty[A]: CC[A] = delegate.empty - def from[E](it: IterableOnce[E]): CC[E] = delegate.from(it) + def from[E](it: IterableOnce[E]^): CC[E] = delegate.from(it) def newBuilder[A]: Builder[A, CC[A]] = delegate.newBuilder[A] } @@ -321,7 +368,7 @@ object SeqFactory { } } -trait StrictOptimizedSeqFactory[+CC[A] <: SeqOps[A, Seq, Seq[A]]] extends SeqFactory[CC] { +trait StrictOptimizedSeqFactory[+CC[A] <: StrictSeqOps[A, Seq, Seq[A]]] extends SeqFactory[CC] { override def fill[A](n: Int)(elem: => A): CC[A] = { val b = newBuilder[A] @@ -382,7 +429,7 @@ trait SpecificIterableFactory[-A, +C] extends Factory[A, C] { * @define coll collection * @define Coll `Iterable` */ -trait MapFactory[+CC[_, _]] extends Serializable { +trait MapFactory[+CC[_, _]] extends Serializable { self => /** * An empty Map @@ -392,7 +439,7 @@ trait MapFactory[+CC[_, _]] extends Serializable { /** * A collection of type Map generated from given iterable object. */ - def from[K, V](it: IterableOnce[(K, V)]): CC[K, V] + def from[K, V](it: IterableOnce[(K, V)]^): CC[K, V]^{it} /** * A collection of type Map that contains given key/value bindings. @@ -410,6 +457,14 @@ trait MapFactory[+CC[_, _]] extends Serializable { implicit def mapFactory[K, V]: Factory[(K, V), CC[K, V]] = MapFactory.toFactory(this) } +/** Like MapFactory, but with a strict constructor. */ +trait StrictMapFactory[+CC[_, _] <: caps.Pure] extends MapFactory[CC] { + /** + * A collection of type Map generated from given iterable object. + */ + def from[K, V](it: IterableOnce[(K, V)]^): CC[K, V] +} + object MapFactory { /** @@ -425,20 +480,20 @@ object MapFactory { @SerialVersionUID(3L) private[this] class ToFactory[K, V, CC[_, _]](factory: MapFactory[CC]) extends Factory[(K, V), CC[K, V]] with Serializable { - def fromSpecific(it: IterableOnce[(K, V)]): CC[K, V] = factory.from[K, V](it) + def fromSpecific(it: IterableOnce[(K, V)]^): CC[K, V]^{it} = factory.from[K, V](it) def newBuilder: Builder[(K, V), CC[K, V]] = factory.newBuilder[K, V] } implicit def toBuildFrom[K, V, CC[_, _]](factory: MapFactory[CC]): BuildFrom[Any, (K, V), CC[K, V]] = new BuildFrom[Any, (K, V), CC[K, V]] { - def fromSpecific(from: Any)(it: IterableOnce[(K, V)]) = factory.from(it) + def fromSpecific(from: Any)(it: IterableOnce[(K, V)]^) = factory.from(it) def newBuilder(from: Any) = factory.newBuilder[K, V] } @SerialVersionUID(3L) - class Delegate[C[_, _]](delegate: MapFactory[C]) extends MapFactory[C] { + class Delegate[C[_, _] <: caps.Pure](delegate: StrictMapFactory[C]) extends StrictMapFactory[C] { override def apply[K, V](elems: (K, V)*): C[K, V] = delegate.apply(elems: _*) - def from[K, V](it: IterableOnce[(K, V)]): C[K, V] = delegate.from(it) + def from[K, V](it: IterableOnce[(K, V)]^): C[K, V] = delegate.from(it) def empty[K, V]: C[K, V] = delegate.empty def newBuilder[K, V]: Builder[(K, V), C[K, V]] = delegate.newBuilder } @@ -455,9 +510,9 @@ object MapFactory { * @define coll collection * @define Coll `Iterable` */ -trait EvidenceIterableFactory[+CC[_], Ev[_]] extends Serializable { +trait EvidenceIterableFactory[+CC[_], Ev[_]] extends Serializable, caps.Pure { - def from[E : Ev](it: IterableOnce[E]): CC[E] + def from[E : Ev](it: IterableOnce[E]^): CC[E] def empty[A : Ev]: CC[A] @@ -518,13 +573,13 @@ object EvidenceIterableFactory { @SerialVersionUID(3L) private[this] class ToFactory[Ev[_], A: Ev, CC[_]](factory: EvidenceIterableFactory[CC, Ev]) extends Factory[A, CC[A]] with Serializable { - def fromSpecific(it: IterableOnce[A]): CC[A] = factory.from[A](it) + def fromSpecific(it: IterableOnce[A]^) = factory.from[A](it) def newBuilder: Builder[A, CC[A]] = factory.newBuilder[A] } implicit def toBuildFrom[Ev[_], A: Ev, CC[_]](factory: EvidenceIterableFactory[CC, Ev]): BuildFrom[Any, A, CC[A]] = new EvidenceIterableFactoryToBuildFrom(factory) private class EvidenceIterableFactoryToBuildFrom[Ev[_], A: Ev, CC[_]](factory: EvidenceIterableFactory[CC, Ev]) extends BuildFrom[Any, A, CC[A]] { - def fromSpecific(from: Any)(it: IterableOnce[A]): CC[A] = factory.from[A](it) + def fromSpecific(from: Any)(it: IterableOnce[A]^) = factory.from[A](it) def newBuilder(from: Any): Builder[A, CC[A]] = factory.newBuilder[A] } @@ -532,7 +587,7 @@ object EvidenceIterableFactory { class Delegate[CC[_], Ev[_]](delegate: EvidenceIterableFactory[CC, Ev]) extends EvidenceIterableFactory[CC, Ev] { override def apply[A: Ev](xs: A*): CC[A] = delegate.apply(xs: _*) def empty[A : Ev]: CC[A] = delegate.empty - def from[E : Ev](it: IterableOnce[E]): CC[E] = delegate.from(it) + def from[E : Ev](it: IterableOnce[E]^) = delegate.from(it) def newBuilder[A : Ev]: Builder[A, CC[A]] = delegate.newBuilder[A] } } @@ -669,7 +724,7 @@ object ClassTagIterableFactory { @SerialVersionUID(3L) class AnyIterableDelegate[CC[_]](delegate: ClassTagIterableFactory[CC]) extends IterableFactory[CC] { def empty[A]: CC[A] = delegate.empty(using ClassTag.Any).asInstanceOf[CC[A]] - def from[A](it: IterableOnce[A]): CC[A] = delegate.from[Any](it)(using ClassTag.Any).asInstanceOf[CC[A]] + def from[A](it: IterableOnce[A]^): CC[A] = delegate.from[Any](it)(using ClassTag.Any).asInstanceOf[CC[A]] def newBuilder[A]: Builder[A, CC[A]] = delegate.newBuilder(using ClassTag.Any).asInstanceOf[Builder[A, CC[A]]] override def apply[A](elems: A*): CC[A] = delegate.apply[Any](elems: _*)(using ClassTag.Any).asInstanceOf[CC[A]] override def iterate[A](start: A, len: Int)(f: A => A): CC[A] = delegate.iterate[A](start, len)(f)(using ClassTag.Any.asInstanceOf[ClassTag[A]]) @@ -697,7 +752,7 @@ object ClassTagSeqFactory { /** A SeqFactory that uses ClassTag.Any as the evidence for every element type. This may or may not be * sound depending on the use of the `ClassTag` by the collection implementation. */ @SerialVersionUID(3L) - class AnySeqDelegate[CC[A] <: SeqOps[A, Seq, Seq[A]]](delegate: ClassTagSeqFactory[CC]) + class AnySeqDelegate[CC[A] <: StrictSeqOps[A, Seq, Seq[A]]](delegate: ClassTagSeqFactory[CC]) extends ClassTagIterableFactory.AnyIterableDelegate[CC](delegate) with SeqFactory[CC] } @@ -734,11 +789,11 @@ trait StrictOptimizedClassTagSeqFactory[+CC[A] <: SeqOps[A, Seq, Seq[A]]] extend * @define coll collection * @define Coll `Iterable` */ -trait SortedMapFactory[+CC[_, _]] extends Serializable { +trait SortedMapFactory[+CC[_, _]] extends Serializable { this: SortedMapFactory[CC] => def empty[K : Ordering, V]: CC[K, V] - def from[K : Ordering, V](it: IterableOnce[(K, V)]): CC[K, V] + def from[K : Ordering, V](it: IterableOnce[(K, V)]^): CC[K, V] def apply[K : Ordering, V](elems: (K, V)*): CC[K, V] = from(elems) @@ -765,20 +820,20 @@ object SortedMapFactory { @SerialVersionUID(3L) private[this] class ToFactory[K : Ordering, V, CC[_, _]](factory: SortedMapFactory[CC]) extends Factory[(K, V), CC[K, V]] with Serializable { - def fromSpecific(it: IterableOnce[(K, V)]): CC[K, V] = factory.from[K, V](it) + def fromSpecific(it: IterableOnce[(K, V)]^): CC[K, V] = factory.from[K, V](it) def newBuilder: Builder[(K, V), CC[K, V]] = factory.newBuilder[K, V] } implicit def toBuildFrom[K : Ordering, V, CC[_, _]](factory: SortedMapFactory[CC]): BuildFrom[Any, (K, V), CC[K, V]] = new SortedMapFactoryToBuildFrom(factory) private class SortedMapFactoryToBuildFrom[K : Ordering, V, CC[_, _]](factory: SortedMapFactory[CC]) extends BuildFrom[Any, (K, V), CC[K, V]] { - def fromSpecific(from: Any)(it: IterableOnce[(K, V)]) = factory.from(it) + def fromSpecific(from: Any)(it: IterableOnce[(K, V)]^) = factory.from(it) def newBuilder(from: Any) = factory.newBuilder[K, V] } @SerialVersionUID(3L) class Delegate[CC[_, _]](delegate: SortedMapFactory[CC]) extends SortedMapFactory[CC] { override def apply[K: Ordering, V](elems: (K, V)*): CC[K, V] = delegate.apply(elems: _*) - def from[K : Ordering, V](it: IterableOnce[(K, V)]): CC[K, V] = delegate.from(it) + def from[K : Ordering, V](it: IterableOnce[(K, V)]^): CC[K, V] = delegate.from(it) def empty[K : Ordering, V]: CC[K, V] = delegate.empty def newBuilder[K : Ordering, V]: Builder[(K, V), CC[K, V]] = delegate.newBuilder } diff --git a/library/src/scala/collection/Hashing.scala b/library/src/scala/collection/Hashing.scala index 5ba8ad24948f..40850a524a40 100644 --- a/library/src/scala/collection/Hashing.scala +++ b/library/src/scala/collection/Hashing.scala @@ -14,6 +14,7 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking protected[collection] object Hashing { diff --git a/library/src/scala/collection/IndexedSeq.scala b/library/src/scala/collection/IndexedSeq.scala index 366fd526a022..df69f7210689 100644 --- a/library/src/scala/collection/IndexedSeq.scala +++ b/library/src/scala/collection/IndexedSeq.scala @@ -14,6 +14,8 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.{nowarn, tailrec} import scala.collection.Searching.{Found, InsertionPoint, SearchResult} import scala.collection.Stepper.EfficientSplit @@ -21,7 +23,7 @@ import scala.math.Ordering /** Base trait for indexed sequences that have efficient `apply` and `length` */ trait IndexedSeq[+A] extends Seq[A] - with IndexedSeqOps[A, IndexedSeq, IndexedSeq[A]] + with StrictIndexedSeqOps[A, IndexedSeq, IndexedSeq[A]] with IterableFactoryDefaults[A, IndexedSeq] { @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") override protected[this] def stringPrefix: String = "IndexedSeq" @@ -32,10 +34,19 @@ trait IndexedSeq[+A] extends Seq[A] @SerialVersionUID(3L) object IndexedSeq extends SeqFactory.Delegate[IndexedSeq](immutable.IndexedSeq) +transparent trait StrictIndexedSeqOps[+A, +CC[_] <: caps.Pure, +C] extends Any with StrictSeqOps[A, CC, C] with IndexedSeqOps[A, CC, C] { + override def map[B](f: A => B): CC[B] = iterableFactory.from(new IndexedSeqView.Map(this, f)) + override def sliding(size: Int, step: Int): Iterator[C] = { + require(size >= 1 && step >= 1, f"size=$size%d and step=$step%d, but both must be positive") + val it = new IndexedSeqSlidingIterator[A, CC, C](this, size, step) + it.asInstanceOf[Iterator[C]] // TODO: seems like CC cannot figure this out yet + } +} + /** Base trait for indexed Seq operations */ -transparent trait IndexedSeqOps[+A, +CC[_], +C] extends Any with SeqOps[A, CC, C] { self => +transparent trait IndexedSeqOps[+A, +CC[_], +C] extends Any with SeqOps[A, CC, C] { self: IndexedSeqOps[A, CC, C]^ => - def iterator: Iterator[A] = view.iterator + def iterator: Iterator[A]^{this} = view.iterator override def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S with EfficientSplit = { import convert.impl._ @@ -48,7 +59,7 @@ transparent trait IndexedSeqOps[+A, +CC[_], +C] extends Any with SeqOps[A, CC, C s.asInstanceOf[S with EfficientSplit] } - override def reverseIterator: Iterator[A] = view.reverseIterator + override def reverseIterator: Iterator[A]^{this} = view.reverseIterator /* TODO 2.14+ uncomment and delete related code in IterableOnce @tailrec private def foldl[B](start: Int, end: Int, z: B, op: (B, A) => B): B = @@ -68,33 +79,34 @@ transparent trait IndexedSeqOps[+A, +CC[_], +C] extends Any with SeqOps[A, CC, C //override def reduceRight[B >: A](op: (A, B) => B): B = if (length > 0) foldr(0, length - 1, apply(length - 1), op) else super.reduceRight(op) - override def view: IndexedSeqView[A] = new IndexedSeqView.Id[A](this) + override def view: IndexedSeqView[A]^{this} = new IndexedSeqView.Id[A](this) @deprecated("Use .view.slice(from, until) instead of .view(from, until)", "2.13.0") - override def view(from: Int, until: Int): IndexedSeqView[A] = view.slice(from, until) + override def view(from: Int, until: Int): IndexedSeqView[A]^{this} = view.slice(from, until) - override protected def reversed: Iterable[A] = new IndexedSeqView.Reverse(this) + override protected def reversed: Iterable[A]^{this} = new IndexedSeqView.Reverse(this) // Override transformation operations to use more efficient views than the default ones - override def prepended[B >: A](elem: B): CC[B] = iterableFactory.from(new IndexedSeqView.Prepended(elem, this)) + override def prepended[B >: A](elem: B): CC[B]^{this} = iterableFactory.from(new IndexedSeqView.Prepended(elem, this)) - override def take(n: Int): C = fromSpecific(new IndexedSeqView.Take(this, n)) + override def take(n: Int): C^{this} = fromSpecific(new IndexedSeqView.Take(this, n)) - override def takeRight(n: Int): C = fromSpecific(new IndexedSeqView.TakeRight(this, n)) + override def takeRight(n: Int): C^{this} = fromSpecific(new IndexedSeqView.TakeRight(this, n)) - override def drop(n: Int): C = fromSpecific(new IndexedSeqView.Drop(this, n)) + override def drop(n: Int): C^{this} = fromSpecific(new IndexedSeqView.Drop(this, n)) - override def dropRight(n: Int): C = fromSpecific(new IndexedSeqView.DropRight(this, n)) + override def dropRight(n: Int): C^{this} = fromSpecific(new IndexedSeqView.DropRight(this, n)) - override def map[B](f: A => B): CC[B] = iterableFactory.from(new IndexedSeqView.Map(this, f)) + override def map[B](f: A => B): CC[B]^{this, f} = iterableFactory.from(new IndexedSeqView.Map(this, f)) - override def reverse: C = fromSpecific(new IndexedSeqView.Reverse(this)) + override def reverse: C^{this} = fromSpecific(new IndexedSeqView.Reverse(this)) - override def slice(from: Int, until: Int): C = fromSpecific(new IndexedSeqView.Slice(this, from, until)) + override def slice(from: Int, until: Int): C^{this} = fromSpecific(new IndexedSeqView.Slice(this, from, until)) - override def sliding(size: Int, step: Int): Iterator[C] = { + override def sliding(size: Int, step: Int): Iterator[C^{this}]^{this} = { require(size >= 1 && step >= 1, f"size=$size%d and step=$step%d, but both must be positive") - new IndexedSeqSlidingIterator[A, CC, C](this, size, step) + val it = new IndexedSeqSlidingIterator[A, CC, C](this, size, step) + it.asInstanceOf[Iterator[Nothing]] // TODO: seems like CC cannot figure this out yet } override def head: A = @@ -123,7 +135,7 @@ transparent trait IndexedSeqOps[+A, +CC[_], +C] extends Any with SeqOps[A, CC, C override def knownSize: Int = length - override final def lengthCompare(that: Iterable[_]): Int = { + override final def lengthCompare(that: Iterable[_]^): Int = { val res = that.sizeCompare(length) // can't just invert the result, because `-Int.MinValue == Int.MinValue` if (res == Int.MinValue) 1 else -res @@ -153,8 +165,10 @@ transparent trait IndexedSeqOps[+A, +CC[_], +C] extends Any with SeqOps[A, CC, C } /** A fast sliding iterator for IndexedSeqs which uses the underlying `slice` operation. */ -private final class IndexedSeqSlidingIterator[A, CC[_], C](s: IndexedSeqOps[A, CC, C], size: Int, step: Int) - extends AbstractIterator[C] { +private final class IndexedSeqSlidingIterator[A, CC[_], C](s: IndexedSeqOps[A, CC, C]^, size: Int, step: Int) + extends AbstractIterator[C^{s}] { + // CC note: seems like the compiler cannot figure out that this class <: Iterator[C^{s}], + // so we need a cast when upcasting is needed. private[this] val len = s.length private[this] var pos = 0 @@ -165,7 +179,7 @@ private final class IndexedSeqSlidingIterator[A, CC[_], C](s: IndexedSeqOps[A, C def hasNext: Boolean = chklen && pos < len - def next(): C = if (!chklen || !hasNext) Iterator.empty.next() else { + def next(): C^{s} = if (!chklen || !hasNext) Iterator.empty.next() else { val end = { val x = pos + size; if (x < 0 || x > len) len else x } // (pos.toLong + size).min(len).toInt val slice = s.slice(pos, end) pos = diff --git a/library/src/scala/collection/IndexedSeqView.scala b/library/src/scala/collection/IndexedSeqView.scala index d03ff1b1c689..b68ae56b4b40 100644 --- a/library/src/scala/collection/IndexedSeqView.scala +++ b/library/src/scala/collection/IndexedSeqView.scala @@ -14,34 +14,36 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.nowarn /** View defined in terms of indexing a range */ -trait IndexedSeqView[+A] extends IndexedSeqOps[A, View, View[A]] with SeqView[A] { self => +trait IndexedSeqView[+A] extends IndexedSeqOps[A, View, View[A]] with SeqView[A] { - override def view: IndexedSeqView[A] = this + override def view: IndexedSeqView[A]^{this} = this @deprecated("Use .view.slice(from, until) instead of .view(from, until)", "2.13.0") - override def view(from: Int, until: Int): IndexedSeqView[A] = view.slice(from, until) - - override def iterator: Iterator[A] = new IndexedSeqView.IndexedSeqViewIterator(this) - override def reverseIterator: Iterator[A] = new IndexedSeqView.IndexedSeqViewReverseIterator(this) - - override def appended[B >: A](elem: B): IndexedSeqView[B] = new IndexedSeqView.Appended(this, elem) - override def prepended[B >: A](elem: B): IndexedSeqView[B] = new IndexedSeqView.Prepended(elem, this) - override def take(n: Int): IndexedSeqView[A] = new IndexedSeqView.Take(this, n) - override def takeRight(n: Int): IndexedSeqView[A] = new IndexedSeqView.TakeRight(this, n) - override def drop(n: Int): IndexedSeqView[A] = new IndexedSeqView.Drop(this, n) - override def dropRight(n: Int): IndexedSeqView[A] = new IndexedSeqView.DropRight(this, n) - override def map[B](f: A => B): IndexedSeqView[B] = new IndexedSeqView.Map(this, f) - override def reverse: IndexedSeqView[A] = new IndexedSeqView.Reverse(this) - override def slice(from: Int, until: Int): IndexedSeqView[A] = new IndexedSeqView.Slice(this, from, until) - override def tapEach[U](f: A => U): IndexedSeqView[A] = new IndexedSeqView.Map(this, { (a: A) => f(a); a}) - - def concat[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new IndexedSeqView.Concat(this, suffix) - def appendedAll[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new IndexedSeqView.Concat(this, suffix) - def prependedAll[B >: A](prefix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new IndexedSeqView.Concat(prefix, this) + override def view(from: Int, until: Int): IndexedSeqView[A]^{this} = view.slice(from, until) + + override def iterator: Iterator[A]^{this} = new IndexedSeqView.IndexedSeqViewIterator(this) + override def reverseIterator: Iterator[A]^{this} = new IndexedSeqView.IndexedSeqViewReverseIterator(this) + + override def appended[B >: A](elem: B): IndexedSeqView[B]^{this} = new IndexedSeqView.Appended(this, elem) + override def prepended[B >: A](elem: B): IndexedSeqView[B]^{this} = new IndexedSeqView.Prepended(elem, this) + override def take(n: Int): IndexedSeqView[A]^{this} = new IndexedSeqView.Take(this, n) + override def takeRight(n: Int): IndexedSeqView[A]^{this} = new IndexedSeqView.TakeRight(this, n) + override def drop(n: Int): IndexedSeqView[A]^{this} = new IndexedSeqView.Drop(this, n) + override def dropRight(n: Int): IndexedSeqView[A]^{this} = new IndexedSeqView.DropRight(this, n) + override def map[B](f: A => B): IndexedSeqView[B]^{this, f} = new IndexedSeqView.Map(this, f) + override def reverse: IndexedSeqView[A]^{this} = new IndexedSeqView.Reverse(this) + override def slice(from: Int, until: Int): IndexedSeqView[A]^{this} = new IndexedSeqView.Slice(this, from, until) + override def tapEach[U](f: A => U): IndexedSeqView[A]^{this, f} = new IndexedSeqView.Map(this, { (a: A) => f(a); a}) + + def concat[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]^): IndexedSeqView[B]^{this, suffix} = new IndexedSeqView.Concat(this, suffix) + def appendedAll[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]^): IndexedSeqView[B]^{this, suffix} = new IndexedSeqView.Concat(this, suffix) + def prependedAll[B >: A](prefix: IndexedSeqView.SomeIndexedSeqOps[B]^): IndexedSeqView[B]^{this, prefix} = new IndexedSeqView.Concat(prefix, this) @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") override protected[this] def stringPrefix: String = "IndexedSeqView" @@ -50,7 +52,7 @@ trait IndexedSeqView[+A] extends IndexedSeqOps[A, View, View[A]] with SeqView[A] object IndexedSeqView { @SerialVersionUID(3L) - private[collection] class IndexedSeqViewIterator[A](self: IndexedSeqView[A]) extends AbstractIterator[A] with Serializable { + private[collection] class IndexedSeqViewIterator[A](self: IndexedSeqView[A]^) extends AbstractIterator[A] with Serializable { private[this] var current = 0 private[this] var remainder = self.length override def knownSize: Int = remainder @@ -64,7 +66,7 @@ object IndexedSeqView { r } else Iterator.empty.next() - override def drop(n: Int): Iterator[A] = { + override def drop(n: Int): Iterator[A]^{this} = { if (n > 0) { current += n remainder = Math.max(0, remainder - n) @@ -72,7 +74,7 @@ object IndexedSeqView { this } - override protected def sliceIterator(from: Int, until: Int): Iterator[A] = { + override protected def sliceIterator(from: Int, until: Int): Iterator[A]^{this} = { def formatRange(value : Int) : Int = if (value < 0) 0 else if (value > remainder) remainder else value @@ -84,7 +86,7 @@ object IndexedSeqView { } } @SerialVersionUID(3L) - private[collection] class IndexedSeqViewReverseIterator[A](self: IndexedSeqView[A]) extends AbstractIterator[A] with Serializable { + private[collection] class IndexedSeqViewReverseIterator[A](self: IndexedSeqView[A]^) extends AbstractIterator[A] with Serializable { private[this] var remainder = self.length private[this] var pos = remainder - 1 @inline private[this] def _hasNext: Boolean = remainder > 0 @@ -99,7 +101,7 @@ object IndexedSeqView { // from < 0 means don't move pos, until < 0 means don't limit remainder // - override protected def sliceIterator(from: Int, until: Int): Iterator[A] = { + override protected def sliceIterator(from: Int, until: Int): Iterator[A]^{this} = { if (_hasNext) { if (remainder <= from) remainder = 0 // exhausted by big skip else if (from <= 0) { // no skip, pos is same @@ -122,43 +124,43 @@ object IndexedSeqView { type SomeIndexedSeqOps[A] = IndexedSeqOps[A, AnyConstr, _] @SerialVersionUID(3L) - class Id[+A](underlying: SomeIndexedSeqOps[A]) + class Id[+A](underlying: SomeIndexedSeqOps[A]^) extends SeqView.Id(underlying) with IndexedSeqView[A] @SerialVersionUID(3L) - class Appended[+A](underlying: SomeIndexedSeqOps[A], elem: A) + class Appended[+A](underlying: SomeIndexedSeqOps[A]^, elem: A) extends SeqView.Appended(underlying, elem) with IndexedSeqView[A] @SerialVersionUID(3L) - class Prepended[+A](elem: A, underlying: SomeIndexedSeqOps[A]) + class Prepended[+A](elem: A, underlying: SomeIndexedSeqOps[A]^) extends SeqView.Prepended(elem, underlying) with IndexedSeqView[A] @SerialVersionUID(3L) - class Concat[A](prefix: SomeIndexedSeqOps[A], suffix: SomeIndexedSeqOps[A]) + class Concat[A](prefix: SomeIndexedSeqOps[A]^, suffix: SomeIndexedSeqOps[A]^) extends SeqView.Concat[A](prefix, suffix) with IndexedSeqView[A] @SerialVersionUID(3L) - class Take[A](underlying: SomeIndexedSeqOps[A], n: Int) + class Take[A](underlying: SomeIndexedSeqOps[A]^, n: Int) extends SeqView.Take(underlying, n) with IndexedSeqView[A] @SerialVersionUID(3L) - class TakeRight[A](underlying: SomeIndexedSeqOps[A], n: Int) + class TakeRight[A](underlying: SomeIndexedSeqOps[A]^, n: Int) extends SeqView.TakeRight(underlying, n) with IndexedSeqView[A] @SerialVersionUID(3L) - class Drop[A](underlying: SomeIndexedSeqOps[A], n: Int) + class Drop[A](underlying: SomeIndexedSeqOps[A]^, n: Int) extends SeqView.Drop[A](underlying, n) with IndexedSeqView[A] @SerialVersionUID(3L) - class DropRight[A](underlying: SomeIndexedSeqOps[A], n: Int) + class DropRight[A](underlying: SomeIndexedSeqOps[A]^, n: Int) extends SeqView.DropRight[A](underlying, n) with IndexedSeqView[A] @SerialVersionUID(3L) - class Map[A, B](underlying: SomeIndexedSeqOps[A], f: A => B) + class Map[A, B](underlying: SomeIndexedSeqOps[A]^, f: A => B) extends SeqView.Map(underlying, f) with IndexedSeqView[B] @SerialVersionUID(3L) - class Reverse[A](underlying: SomeIndexedSeqOps[A]) extends SeqView.Reverse[A](underlying) with IndexedSeqView[A] { + class Reverse[A](underlying: SomeIndexedSeqOps[A]^) extends SeqView.Reverse[A](underlying) with IndexedSeqView[A] { override def reverse: IndexedSeqView[A] = underlying match { case x: IndexedSeqView[A] => x case _ => super.reverse @@ -166,7 +168,7 @@ object IndexedSeqView { } @SerialVersionUID(3L) - class Slice[A](underlying: SomeIndexedSeqOps[A], from: Int, until: Int) extends AbstractIndexedSeqView[A] { + class Slice[A](underlying: SomeIndexedSeqOps[A]^, from: Int, until: Int) extends AbstractIndexedSeqView[A] { protected val lo = from max 0 protected val hi = (until max 0) min underlying.length protected val len = (hi - lo) max 0 diff --git a/library/src/scala/collection/Iterable.scala b/library/src/scala/collection/Iterable.scala index 641b4bb4745e..7f3ed1a0a39c 100644 --- a/library/src/scala/collection/Iterable.scala +++ b/library/src/scala/collection/Iterable.scala @@ -14,6 +14,8 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.nowarn import scala.annotation.unchecked.uncheckedVariance import scala.collection.mutable.Builder @@ -95,7 +97,7 @@ trait Iterable[+A] extends IterableOnce[A] * @return a decorator `LazyZip2` that allows strict operations to be performed on the lazily evaluated pairs * or chained calls to `lazyZip`. Implicit conversion to `Iterable[(A, B)]` is also supported. */ - def lazyZip[B](that: Iterable[B]): LazyZip2[A, B, this.type] = new LazyZip2(this, this, that) + def lazyZip[B](that: Iterable[B]^): LazyZip2[A, B, this.type]^{this, that} = new LazyZip2(this, this, that) } /** Base trait for Iterable operations @@ -138,24 +140,24 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w */ // Should be `protected def asIterable`, or maybe removed altogether if it's not needed @deprecated("toIterable is internal and will be made protected; its name is similar to `toList` or `toSeq`, but it doesn't copy non-immutable collections", "2.13.7") - def toIterable: Iterable[A] + def toIterable: Iterable[A]^{this} /** Converts this $coll to an unspecified Iterable. Will return * the same collection if this instance is already Iterable. * @return An Iterable containing all elements of this $coll. */ @deprecated("toTraversable is internal and will be made protected; its name is similar to `toList` or `toSeq`, but it doesn't copy non-immutable collections", "2.13.0") - final def toTraversable: Traversable[A] = toIterable + final def toTraversable: Traversable[A]^{this} = toIterable override def isTraversableAgain: Boolean = true /** * @return This collection as a `C`. */ - protected def coll: C + protected def coll: C^{this} @deprecated("Use coll instead of repr in a collection implementation, use the collection value itself from the outside", "2.13.0") - final def repr: C = coll + final def repr: C^{this} = coll /** * Defines how to turn a given `Iterable[A]` into a collection of type `C`. @@ -175,7 +177,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * `Iterable[A]` obtained from `this` collection (as it is the case in the * implementations of operations where we use a `View[A]`), it is safe. */ - protected def fromSpecific(coll: IterableOnce[A @uncheckedVariance]): C + protected def fromSpecific(coll: IterableOnce[A @uncheckedVariance]^): C^{coll} /** The companion object of this ${coll}, providing various factory methods. * @@ -252,7 +254,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w def lastOption: Option[A] = if (isEmpty) None else Some(last) /** A view over the elements of this collection. */ - def view: View[A] = View.fromIteratorProvider(() => iterator) + def view: View[A]^{this} = View.fromIteratorProvider(() => iterator) /** Compares the size of this $coll to a test value. * @@ -301,7 +303,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * this.sizeIs > size // this.sizeCompare(size) > 0 * }}} */ - @inline final def sizeIs: IterableOps.SizeCompareOps = new IterableOps.SizeCompareOps(this) + @inline final def sizeIs: IterableOps.SizeCompareOps^{this} = new IterableOps.SizeCompareOps(caps.unsafe.unsafeAssumePure(this) /* see comment in SizeCompareOps*/) /** Compares the size of this $coll to the size of another `Iterable`. * @@ -317,7 +319,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * is `O(this.size min that.size)` instead of `O(this.size + that.size)`. * The method should be overridden if computing `size` is cheap and `knownSize` returns `-1`. */ - def sizeCompare(that: Iterable[_]): Int = { + def sizeCompare(that: Iterable[_]^): Int = { val thatKnownSize = that.knownSize if (thatKnownSize >= 0) this sizeCompare thatKnownSize @@ -342,7 +344,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w /** A view over a slice of the elements of this collection. */ @deprecated("Use .view.slice(from, until) instead of .view(from, until)", "2.13.0") - def view(from: Int, until: Int): View[A] = view.slice(from, until) + def view(from: Int, until: Int): View[A]^{this} = view.slice(from, until) /** Transposes this $coll of iterable collections into * a $coll of ${coll}s. @@ -378,9 +380,9 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * @throws IllegalArgumentException if all collections in this $coll * are not of the same size. */ - def transpose[B](implicit asIterable: A => /*<: /*<: Boolean): C = fromSpecific(new View.Filter(this, pred, isFlipped = false)) + def filter(pred: A => Boolean): C^{this, pred} = fromSpecific(new View.Filter(this, pred, isFlipped = false)) - def filterNot(pred: A => Boolean): C = fromSpecific(new View.Filter(this, pred, isFlipped = true)) + def filterNot(pred: A => Boolean): C^{this, pred} = fromSpecific(new View.Filter(this, pred, isFlipped = true)) /** Creates a non-strict filter of this $coll. * @@ -417,7 +419,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * All these operations apply to those elements of this $coll * which satisfy the predicate `p`. */ - def withFilter(p: A => Boolean): collection.WithFilter[A, CC] = new IterableOps.WithFilter(this, p) + def withFilter(p: A => Boolean): collection.WithFilter[A, CC]^{this, p} = new IterableOps.WithFilter(this, p) /** A pair of, first, all elements that satisfy predicate `p` and, second, * all elements that do not. @@ -428,15 +430,15 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * Strict collections have an overridden version of `partition` in `StrictOptimizedIterableOps`, * which requires only a single traversal. */ - def partition(p: A => Boolean): (C, C) = { + def partition(p: A => Boolean): (C^{this, p}, C^{this, p}) = { val first = new View.Filter(this, p, isFlipped = false) val second = new View.Filter(this, p, isFlipped = true) (fromSpecific(first), fromSpecific(second)) } - override def splitAt(n: Int): (C, C) = (take(n), drop(n)) + override def splitAt(n: Int): (C^{this}, C^{this}) = (take(n), drop(n)) - def take(n: Int): C = fromSpecific(new View.Take(this, n)) + def take(n: Int): C^{this} = fromSpecific(new View.Take(this, n)) /** Selects the last ''n'' elements. * $orderDependent @@ -445,7 +447,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * or else the whole $coll, if it has less than `n` elements. * If `n` is negative, returns an empty $coll. */ - def takeRight(n: Int): C = fromSpecific(new View.TakeRight(this, n)) + def takeRight(n: Int): C^{this} = fromSpecific(new View.TakeRight(this, n)) /** Takes longest prefix of elements that satisfy a predicate. * $orderDependent @@ -453,11 +455,11 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * @return the longest prefix of this $coll whose elements all satisfy * the predicate `p`. */ - def takeWhile(p: A => Boolean): C = fromSpecific(new View.TakeWhile(this, p)) + def takeWhile(p: A => Boolean): C^{this, p} = fromSpecific(new View.TakeWhile(this, p)) - def span(p: A => Boolean): (C, C) = (takeWhile(p), dropWhile(p)) + def span(p: A => Boolean): (C^{this, p}, C^{this, p}) = (takeWhile(p), dropWhile(p)) - def drop(n: Int): C = fromSpecific(new View.Drop(this, n)) + def drop(n: Int): C^{this} = fromSpecific(new View.Drop(this, n)) /** Selects all elements except last ''n'' ones. * $orderDependent @@ -465,19 +467,19 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * @return a $coll consisting of all elements of this $coll except the last `n` ones, or else the * empty $coll, if this $coll has less than `n` elements. * If `n` is negative, don't drop any elements. - */ - def dropRight(n: Int): C = fromSpecific(new View.DropRight(this, n)) + ^{this} */ + def dropRight(n: Int): C^{this} = fromSpecific(new View.DropRight(this, n)) - def dropWhile(p: A => Boolean): C = fromSpecific(new View.DropWhile(this, p)) + def dropWhile(p: A => Boolean): C^{this, p} = fromSpecific(new View.DropWhile(this, p)) /** Partitions elements in fixed size ${coll}s. * @see [[scala.collection.Iterator]], method `grouped` * - * @param size the number of elements per group + * @param si^{this}ze the number of elements per group * @return An iterator producing ${coll}s of size `size`, except the * last will be less than size `size` if the elements don't divide evenly. */ - def grouped(size: Int): Iterator[C] = + def grouped(size: Int): Iterator[C^{this}]^{this} = iterator.grouped(size).map(fromSpecific) /** Groups elements in fixed size blocks by passing a "sliding window" @@ -499,7 +501,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * @example `List(1, 2).sliding(2) = Iterator(List(1, 2))` * @example `List(1, 2, 3).sliding(2) = Iterator(List(1, 2), List(2, 3))` */ - def sliding(size: Int): Iterator[C] = sliding(size, 1) + def sliding(size: Int): Iterator[C^{this}]^{this} = sliding(size, 1) /** Groups elements in fixed size blocks by passing a "sliding window" * over them (as opposed to partitioning them, as is done in `grouped`). @@ -520,11 +522,11 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * @example `List(1, 2, 3, 4, 5).sliding(2, 2) = Iterator(List(1, 2), List(3, 4), List(5))` * @example `List(1, 2, 3, 4, 5, 6).sliding(2, 3) = Iterator(List(1, 2), List(4, 5))` */ - def sliding(size: Int, step: Int): Iterator[C] = + def sliding(size: Int, step: Int): Iterator[C^{this}]^{this} = iterator.sliding(size, step).map(fromSpecific) /** The rest of the collection without its first element. */ - def tail: C = { + def tail: C^{this} = { if (isEmpty) throw new UnsupportedOperationException drop(1) } @@ -532,12 +534,12 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w /** The initial part of the collection without its last element. * $willForceEvaluation */ - def init: C = { + def init: C^{this} = { if (isEmpty) throw new UnsupportedOperationException dropRight(1) } - def slice(from: Int, until: Int): C = + def slice(from: Int, until: Int): C^{this} = fromSpecific(new View.Drop(new View.Take(this, until), from)) /** Partitions this $coll into a map of ${coll}s according to some discriminator function. @@ -647,9 +649,9 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * * @return a new $coll containing the prefix scan of the elements in this $coll */ - def scan[B >: A](z: B)(op: (B, B) => B): CC[B] = scanLeft(z)(op) + def scan[B >: A](z: B)(op: (B, B) => B): CC[B]^{this, op} = scanLeft(z)(op) - def scanLeft[B](z: B)(op: (B, A) => B): CC[B] = iterableFactory.from(new View.ScanLeft(this, z, op)) + def scanLeft[B](z: B)(op: (B, A) => B): CC[B]^{this, op} = iterableFactory.from(new View.ScanLeft(this, z, op)) /** Produces a collection containing cumulative results of applying the operator going right to left. * The head of the collection is the last cumulative result. @@ -667,7 +669,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * @param op the binary operator applied to the intermediate result and the element * @return collection with intermediate results */ - def scanRight[B](z: B)(op: (A, B) => B): CC[B] = { + def scanRight[B](z: B)(op: (A, B) => B): CC[B]^{this, op} = { class Scanner extends runtime.AbstractFunction1[A, Unit] { var acc = z var scanned = acc :: immutable.Nil @@ -681,13 +683,13 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w iterableFactory.from(scanner.scanned) } - def map[B](f: A => B): CC[B] = iterableFactory.from(new View.Map(this, f)) + def map[B](f: A => B): CC[B]^{this, f} = iterableFactory.from(new View.Map(this, f)) - def flatMap[B](f: A => IterableOnce[B]): CC[B] = iterableFactory.from(new View.FlatMap(this, f)) + def flatMap[B](f: A => IterableOnce[B]^): CC[B]^{this, f} = iterableFactory.from(new View.FlatMap(this, f)) - def flatten[B](implicit asIterable: A => IterableOnce[B]): CC[B] = flatMap(asIterable) + def flatten[B](implicit asIterable: A -> IterableOnce[B]): CC[B]^{this} = flatMap(asIterable) - def collect[B](pf: PartialFunction[A, B]): CC[B] = + def collect[B](pf: PartialFunction[A, B]^): CC[B]^{this, pf} = iterableFactory.from(new View.Collect(this, pf)) /** Applies a function `f` to each element of the $coll and returns a pair of ${coll}s: the first one @@ -711,9 +713,9 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * @return a pair of ${coll}s: the first one made of those values returned by `f` that were wrapped in [[scala.util.Left]], * and the second one made of those wrapped in [[scala.util.Right]]. */ - def partitionMap[A1, A2](f: A => Either[A1, A2]): (CC[A1], CC[A2]) = { - val left: View[A1] = new LeftPartitionMapped(this, f) - val right: View[A2] = new RightPartitionMapped(this, f) + def partitionMap[A1, A2](f: A => Either[A1, A2]): (CC[A1]^{this, f}, CC[A2]^{this, f}) = { + val left: View[A1]^{this, f} = new LeftPartitionMapped(this, f) + val right: View[A2]^{this, f} = new RightPartitionMapped(this, f) (iterableFactory.from(left), iterableFactory.from(right)) } @@ -726,7 +728,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * @return a new $coll which contains all elements * of this $coll followed by all elements of `suffix`. */ - def concat[B >: A](suffix: IterableOnce[B]): CC[B] = iterableFactory.from { + def concat[B >: A](suffix: IterableOnce[B]^): CC[B]^{this, suffix} = iterableFactory.from { suffix match { case suffix: Iterable[B] => new View.Concat(this, suffix) case suffix => iterator ++ suffix.iterator @@ -734,7 +736,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w } /** Alias for `concat` */ - @inline final def ++ [B >: A](suffix: IterableOnce[B]): CC[B] = concat(suffix) + @inline final def ++ [B >: A](suffix: IterableOnce[B]^): CC[B]^{this, suffix} = concat(suffix) /** Returns a $ccoll formed from this $coll and another iterable collection * by combining corresponding elements in pairs. @@ -745,12 +747,12 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * @return a new $ccoll containing pairs consisting of corresponding elements of this $coll and `that`. * The length of the returned collection is the minimum of the lengths of this $coll and `that`. */ - def zip[B](that: IterableOnce[B]): CC[(A @uncheckedVariance, B)] = iterableFactory.from(that match { // sound bcs of VarianceNote + def zip[B](that: IterableOnce[B]^): CC[(A @uncheckedVariance, B)]^{this, that} = iterableFactory.from(that match { // sound bcs of VarianceNote case that: Iterable[B] => new View.Zip(this, that) case _ => iterator.zip(that) }) - def zipWithIndex: CC[(A @uncheckedVariance, Int)] = iterableFactory.from(new View.ZipWithIndex(this)) + def zipWithIndex: CC[(A @uncheckedVariance, Int)]^{this} = iterableFactory.from(new View.ZipWithIndex(this)) /** Returns a $coll formed from this $coll and another iterable collection * by combining corresponding elements in pairs. @@ -766,7 +768,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * If this $coll is shorter than `that`, `thisElem` values are used to pad the result. * If `that` is shorter than this $coll, `thatElem` values are used to pad the result. */ - def zipAll[A1 >: A, B](that: Iterable[B], thisElem: A1, thatElem: B): CC[(A1, B)] = iterableFactory.from(new View.ZipAll(this, that, thisElem, thatElem)) + def zipAll[A1 >: A, B](that: Iterable[B]^, thisElem: A1, thatElem: B): CC[(A1, B)]^{this, that} = iterableFactory.from(new View.ZipAll(this, that, thisElem, thatElem)) /** Converts this $coll of pairs into two collections of the first and second * half of each pair. @@ -787,9 +789,9 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * @return a pair of ${coll}s, containing the first, respectively second * half of each element pair of this $coll. */ - def unzip[A1, A2](implicit asPair: A => (A1, A2)): (CC[A1], CC[A2]) = { - val first: View[A1] = new View.Map[A, A1](this, asPair(_)._1) - val second: View[A2] = new View.Map[A, A2](this, asPair(_)._2) + def unzip[A1, A2](implicit asPair: A -> (A1, A2)): (CC[A1]^{this}, CC[A2]^{this}) = { + val first: View[A1]^{this} = new View.Map[A, A1](this, asPair(_)._1) + val second: View[A2]^{this} = new View.Map[A, A2](this, asPair(_)._2) (iterableFactory.from(first), iterableFactory.from(second)) } @@ -814,10 +816,10 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * @return a triple of ${coll}s, containing the first, second, respectively * third member of each element triple of this $coll. */ - def unzip3[A1, A2, A3](implicit asTriple: A => (A1, A2, A3)): (CC[A1], CC[A2], CC[A3]) = { - val first: View[A1] = new View.Map[A, A1](this, asTriple(_)._1) - val second: View[A2] = new View.Map[A, A2](this, asTriple(_)._2) - val third: View[A3] = new View.Map[A, A3](this, asTriple(_)._3) + def unzip3[A1, A2, A3](implicit asTriple: A -> (A1, A2, A3)): (CC[A1]^{this}, CC[A2]^{this}, CC[A3]^{this}) = { + val first: View[A1]^{this} = new View.Map[A, A1](this, asTriple(_)._1) + val second: View[A2]^{this} = new View.Map[A, A2](this, asTriple(_)._2) + val third: View[A3]^{this} = new View.Map[A, A3](this, asTriple(_)._3) (iterableFactory.from(first), iterableFactory.from(second), iterableFactory.from(third)) } @@ -828,7 +830,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * @return an iterator over all the tails of this $coll * @example `List(1,2,3).tails = Iterator(List(1,2,3), List(2,3), List(3), Nil)` */ - def tails: Iterator[C] = iterateUntilEmpty(_.tail) + def tails: Iterator[C^{this}]^{this} = iterateUntilEmpty(_.tail) /** Iterates over the inits of this $coll. The first value will be this * $coll and the final one will be an empty $coll, with the intervening @@ -839,12 +841,12 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w * @return an iterator over all the inits of this $coll * @example `List(1,2,3).inits = Iterator(List(1,2,3), List(1,2), List(1), Nil)` */ - def inits: Iterator[C] = iterateUntilEmpty(_.init) + def inits: Iterator[C^{this}]^{this} = iterateUntilEmpty(_.init) - override def tapEach[U](f: A => U): C = fromSpecific(new View.Map(this, { (a: A) => f(a); a })) + override def tapEach[U](f: A => U): C^{this, f} = fromSpecific(new View.Map(this, { (a: A) => f(a); a })) // A helper for tails and inits. - private[this] def iterateUntilEmpty(f: Iterable[A] => Iterable[A]): Iterator[C] = { + private[this] def iterateUntilEmpty(f: Iterable[A]^{this} => Iterable[A]^{this}): Iterator[C^{this}]^{this, f} = { // toIterable ties the knot between `this: IterableOnceOps[A, CC, C]` and `this.tail: C` // `this.tail.tail` doesn't compile as `C` is unbounded // `Iterable.from(this)` would eagerly copy non-immutable collections @@ -853,7 +855,7 @@ transparent trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] w } @deprecated("Use ++ instead of ++: for collections of type Iterable", "2.13.0") - def ++:[B >: A](that: IterableOnce[B]): CC[B] = iterableFactory.from(that match { + def ++:[B >: A](that: IterableOnce[B]^): CC[B]^{this, that} = iterableFactory.from(that match { case xs: Iterable[B] => new View.Concat(xs, this) case _ => that.iterator ++ iterator }) @@ -867,6 +869,13 @@ object IterableOps { * [[scala.collection.IterableOps!.sizeCompare(Int):Int* `sizeCompare(Int)`]] */ final class SizeCompareOps private[collection](val it: IterableOps[_, AnyConstr, _]) extends AnyVal { + // CC Problem: if we add the logically needed `^`s to the `it` parameter and the + // self type, separation checking fails in the compiler-generated equals$extends + // method of the value class. There seems to be something wrong how pattern + // matching interacts with separation checking. A minimized test case + // is pending/pos-custom-args/captures/SizeCompareOps-redux.scala. + // Without the `^`s, the `sizeIs` method needs an unsafeAssumePure. + /** Tests if the size of the collection is less than some value. */ @inline def <(size: Int): Boolean = it.sizeCompare(size) < 0 /** Tests if the size of the collection is less than or equal to some value. */ @@ -891,22 +900,22 @@ object IterableOps { */ @SerialVersionUID(3L) class WithFilter[+A, +CC[_]]( - self: IterableOps[A, CC, _], + self: IterableOps[A, CC, _]^, p: A => Boolean ) extends collection.WithFilter[A, CC] with Serializable { - protected def filtered: Iterable[A] = + protected def filtered: Iterable[A]^{this} = new View.Filter(self, p, isFlipped = false) - def map[B](f: A => B): CC[B] = + def map[B](f: A => B): CC[B]^{this, f} = self.iterableFactory.from(new View.Map(filtered, f)) - def flatMap[B](f: A => IterableOnce[B]): CC[B] = + def flatMap[B](f: A => IterableOnce[B]^): CC[B]^{this, f} = self.iterableFactory.from(new View.FlatMap(filtered, f)) def foreach[U](f: A => U): Unit = filtered.foreach(f) - def withFilter(q: A => Boolean): WithFilter[A, CC] = + def withFilter(q: A => Boolean): WithFilter[A, CC]^{this, q} = new WithFilter(self, (a: A) => p(a) && q(a)) } @@ -926,7 +935,7 @@ object Iterable extends IterableFactory.Delegate[Iterable](immutable.Iterable) { override def view: View.Single[A] = new View.Single(a) override def take(n: Int) = if (n > 0) this else Iterable.empty override def takeRight(n: Int) = if (n > 0) this else Iterable.empty - override def drop(n: Int) = if (n > 0) Iterable.empty else this + override def drop(n: Int): Iterable[A] = if (n > 0) Iterable.empty else this override def dropRight(n: Int) = if (n > 0) Iterable.empty else this override def tail: Iterable[Nothing] = Iterable.empty override def init: Iterable[Nothing] = Iterable.empty @@ -944,7 +953,7 @@ abstract class AbstractIterable[+A] extends Iterable[A] * same as `C`. */ trait IterableFactoryDefaults[+A, +CC[x] <: IterableOps[x, CC, CC[x]]] extends IterableOps[A, CC, CC[A @uncheckedVariance]] { - protected def fromSpecific(coll: IterableOnce[A @uncheckedVariance]): CC[A @uncheckedVariance] = iterableFactory.from(coll) + protected def fromSpecific(coll: IterableOnce[A @uncheckedVariance]^): CC[A @uncheckedVariance]^{coll} = iterableFactory.from(coll) protected def newSpecificBuilder: Builder[A @uncheckedVariance, CC[A @uncheckedVariance]] = iterableFactory.newBuilder[A] // overridden for efficiency, since we know CC[A] =:= C @@ -962,7 +971,7 @@ trait IterableFactoryDefaults[+A, +CC[x] <: IterableOps[x, CC, CC[x]]] extends I trait EvidenceIterableFactoryDefaults[+A, +CC[x] <: IterableOps[x, CC, CC[x]], Ev[_]] extends IterableOps[A, CC, CC[A @uncheckedVariance]] { protected def evidenceIterableFactory: EvidenceIterableFactory[CC, Ev] implicit protected def iterableEvidence: Ev[A @uncheckedVariance] - override protected def fromSpecific(coll: IterableOnce[A @uncheckedVariance]): CC[A @uncheckedVariance] = evidenceIterableFactory.from(coll) + override protected def fromSpecific(coll: IterableOnce[A @uncheckedVariance]^): CC[A @uncheckedVariance]^{coll} = evidenceIterableFactory.from(coll) override protected def newSpecificBuilder: Builder[A @uncheckedVariance, CC[A @uncheckedVariance]] = evidenceIterableFactory.newBuilder[A] override def empty: CC[A @uncheckedVariance] = evidenceIterableFactory.empty } @@ -984,11 +993,11 @@ trait SortedSetFactoryDefaults[+A, +WithFilterCC[x] <: IterableOps[x, WithFilterCC, WithFilterCC[x]] with Set[x]] extends SortedSetOps[A @uncheckedVariance, CC, CC[A @uncheckedVariance]] { self: IterableOps[A, WithFilterCC, _] => - override protected def fromSpecific(coll: IterableOnce[A @uncheckedVariance]): CC[A @uncheckedVariance] = sortedIterableFactory.from(coll)(using ordering) + override protected def fromSpecific(coll: IterableOnce[A @uncheckedVariance]^): CC[A @uncheckedVariance] = sortedIterableFactory.from(coll)(using ordering) override protected def newSpecificBuilder: mutable.Builder[A @uncheckedVariance, CC[A @uncheckedVariance]] = sortedIterableFactory.newBuilder[A](using ordering) override def empty: CC[A @uncheckedVariance] = sortedIterableFactory.empty(using ordering) - override def withFilter(p: A => Boolean): SortedSetOps.WithFilter[A, WithFilterCC, CC] = + override def withFilter(p: A => Boolean): SortedSetOps.WithFilter[A, WithFilterCC, CC]^{p} = new SortedSetOps.WithFilter[A, WithFilterCC, CC](this, p) } @@ -1008,7 +1017,8 @@ trait SortedSetFactoryDefaults[+A, trait MapFactoryDefaults[K, +V, +CC[x, y] <: IterableOps[(x, y), Iterable, Iterable[(x, y)]], +WithFilterCC[x] <: IterableOps[x, WithFilterCC, WithFilterCC[x]] with Iterable[x]] extends MapOps[K, V, CC, CC[K, V @uncheckedVariance]] with IterableOps[(K, V), WithFilterCC, CC[K, V @uncheckedVariance]] { - override protected def fromSpecific(coll: IterableOnce[(K, V @uncheckedVariance)]): CC[K, V @uncheckedVariance] = mapFactory.from(coll) + this: MapFactoryDefaults[K, V, CC, WithFilterCC] => + override protected def fromSpecific(coll: IterableOnce[(K, V @uncheckedVariance)]^): CC[K, V @uncheckedVariance]^{coll} = mapFactory.from(coll) override protected def newSpecificBuilder: mutable.Builder[(K, V @uncheckedVariance), CC[K, V @uncheckedVariance]] = mapFactory.newBuilder[K, V] override def empty: CC[K, V @uncheckedVariance] = (this: AnyRef) match { // Implemented here instead of in TreeSeqMap since overriding empty in TreeSeqMap is not forwards compatible (should be moved) @@ -1016,7 +1026,7 @@ trait MapFactoryDefaults[K, +V, case _ => mapFactory.empty } - override def withFilter(p: ((K, V)) => Boolean): MapOps.WithFilter[K, V, WithFilterCC, CC] = + override def withFilter(p: ((K, V)) => Boolean): MapOps.WithFilter[K, V, WithFilterCC, CC]^{p} = new MapOps.WithFilter[K, V, WithFilterCC, CC](this, p) } @@ -1035,13 +1045,13 @@ trait MapFactoryDefaults[K, +V, trait SortedMapFactoryDefaults[K, +V, +CC[x, y] <: Map[x, y] with SortedMapOps[x, y, CC, CC[x, y]] with UnsortedCC[x, y], +WithFilterCC[x] <: IterableOps[x, WithFilterCC, WithFilterCC[x]] with Iterable[x], - +UnsortedCC[x, y] <: Map[x, y]] extends SortedMapOps[K, V, CC, CC[K, V @uncheckedVariance]] with MapOps[K, V, UnsortedCC, CC[K, V @uncheckedVariance]] { + +UnsortedCC[x, y] <: Map[x, y]] extends SortedMapOps[K, V, CC, CC[K, V @uncheckedVariance]] with StrictMapOps[K, V, UnsortedCC, CC[K, V @uncheckedVariance]] { self: IterableOps[(K, V), WithFilterCC, _] => override def empty: CC[K, V @uncheckedVariance] = sortedMapFactory.empty(using ordering) - override protected def fromSpecific(coll: IterableOnce[(K, V @uncheckedVariance)]): CC[K, V @uncheckedVariance] = sortedMapFactory.from(coll)(using ordering) + override protected def fromSpecific(coll: IterableOnce[(K, V @uncheckedVariance)]^): CC[K, V @uncheckedVariance] = sortedMapFactory.from(coll)(using ordering) override protected def newSpecificBuilder: mutable.Builder[(K, V @uncheckedVariance), CC[K, V @uncheckedVariance]] = sortedMapFactory.newBuilder[K, V](using ordering) - override def withFilter(p: ((K, V)) => Boolean): collection.SortedMapOps.WithFilter[K, V, WithFilterCC, UnsortedCC, CC] = + override def withFilter(p: ((K, V)) => Boolean): collection.SortedMapOps.WithFilter[K, V, WithFilterCC, UnsortedCC, CC]^{p} = new collection.SortedMapOps.WithFilter[K, V, WithFilterCC, UnsortedCC, CC](this, p) } diff --git a/library/src/scala/collection/IterableOnce.scala b/library/src/scala/collection/IterableOnce.scala index 44a5eaa1ca20..e750d955e8fb 100644 --- a/library/src/scala/collection/IterableOnce.scala +++ b/library/src/scala/collection/IterableOnce.scala @@ -14,6 +14,8 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.tailrec import scala.annotation.unchecked.uncheckedVariance import scala.collection.mutable.StringBuilder @@ -43,7 +45,7 @@ import scala.runtime.{AbstractFunction1, AbstractFunction2} * @define coll collection * @define ccoll $coll */ -trait IterableOnce[+A] extends Any { +trait IterableOnce[+A] extends Any { this: IterableOnce[A]^ => /** An [[scala.collection.Iterator]] over the elements of this $coll. * @@ -51,7 +53,7 @@ trait IterableOnce[+A] extends Any { * in its current state, but if it is an [[scala.collection.Iterable]], this method always returns a new * [[scala.collection.Iterator]]. */ - def iterator: Iterator[A] + def iterator: Iterator[A]^{this} /** Returns a [[scala.collection.Stepper]] for the elements of this collection. * @@ -73,9 +75,9 @@ trait IterableOnce[+A] extends Any { * allow creating parallel streams, whereas bare Steppers can be converted only to sequential * streams. */ - def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S = { + def stepper[S <: Stepper[_]](implicit shape: StepperShape[A, S]): S^{this} = { import convert.impl._ - val s = shape.shape match { + val s: Any = shape.shape match { case StepperShape.IntShape => new IntIteratorStepper (iterator.asInstanceOf[Iterator[Int]]) case StepperShape.LongShape => new LongIteratorStepper (iterator.asInstanceOf[Iterator[Long]]) case StepperShape.DoubleShape => new DoubleIteratorStepper(iterator.asInstanceOf[Iterator[Double]]) @@ -92,7 +94,7 @@ trait IterableOnce[+A] extends Any { final class IterableOnceExtensionMethods[A](private val it: IterableOnce[A]) extends AnyVal { @deprecated("Use .iterator.withFilter(...) instead", "2.13.0") - def withFilter(f: A => Boolean): Iterator[A] = it.iterator.withFilter(f) + def withFilter(f: A => Boolean): Iterator[A]^{f} = it.iterator.withFilter(f) @deprecated("Use .iterator.reduceLeftOption(...) instead", "2.13.0") def reduceLeftOption(f: (A, A) => A): Option[A] = it.iterator.reduceLeftOption(f) @@ -110,7 +112,7 @@ final class IterableOnceExtensionMethods[A](private val it: IterableOnce[A]) ext def reduceRight(f: (A, A) => A): A = it.iterator.reduceRight(f) @deprecated("Use .iterator.maxBy(...) instead", "2.13.0") - def maxBy[B](f: A => B)(implicit cmp: Ordering[B]): A = it.iterator.maxBy(f) + def maxBy[B](f: A -> B)(implicit cmp: Ordering[B]): A = it.iterator.maxBy(f) @deprecated("Use .iterator.reduceLeft(...) instead", "2.13.0") def reduceLeft(f: (A, A) => A): A = it.iterator.reduceLeft(f) @@ -128,7 +130,7 @@ final class IterableOnceExtensionMethods[A](private val it: IterableOnce[A]) ext def reduceOption(f: (A, A) => A): Option[A] = it.iterator.reduceOption(f) @deprecated("Use .iterator.minBy(...) instead", "2.13.0") - def minBy[B](f: A => B)(implicit cmp: Ordering[B]): A = it.iterator.minBy(f) + def minBy[B](f: A -> B)(implicit cmp: Ordering[B]): A = it.iterator.minBy(f) @deprecated("Use .iterator.size instead", "2.13.0") def size: Int = it.iterator.size @@ -137,10 +139,10 @@ final class IterableOnceExtensionMethods[A](private val it: IterableOnce[A]) ext def forall(f: A => Boolean): Boolean = it.iterator.forall(f) @deprecated("Use .iterator.collectFirst(...) instead", "2.13.0") - def collectFirst[B](f: PartialFunction[A, B]): Option[B] = it.iterator.collectFirst(f) + def collectFirst[B](f: PartialFunction[A, B]^): Option[B] = it.iterator.collectFirst(f) @deprecated("Use .iterator.filter(...) instead", "2.13.0") - def filter(f: A => Boolean): Iterator[A] = it.iterator.filter(f) + def filter(f: A => Boolean): Iterator[A]^{f} = it.iterator.filter(f) @deprecated("Use .iterator.exists(...) instead", "2.13.0") def exists(f: A => Boolean): Boolean = it.iterator.exists(f) @@ -246,13 +248,13 @@ final class IterableOnceExtensionMethods[A](private val it: IterableOnce[A]) ext @`inline` def :\ [B](z: B)(op: (A, B) => B): B = foldRight[B](z)(op) @deprecated("Use .iterator.map instead or consider requiring an Iterable", "2.13.0") - def map[B](f: A => B): IterableOnce[B] = it match { - case it: Iterable[A] => it.map(f) + def map[B](f: A => B): IterableOnce[B]^{f} = it match { + case it: Iterable[A]^{f} => it.map(f) case _ => it.iterator.map(f) } @deprecated("Use .iterator.flatMap instead or consider requiring an Iterable", "2.13.0") - def flatMap[B](f: A => IterableOnce[B]): IterableOnce[B] = it match { + def flatMap[B](f: A => IterableOnce[B]^): IterableOnce[B]^{f} = it match { case it: Iterable[A] => it.flatMap(f) case _ => it.iterator.flatMap(f) } @@ -277,7 +279,7 @@ object IterableOnce { math.max(math.min(math.min(len, srcLen), destLen - start), 0) /** Calls `copyToArray` on the given collection, regardless of whether or not it is an `Iterable`. */ - @inline private[collection] def copyElemsToArray[A, B >: A](elems: IterableOnce[A], + @inline private[collection] def copyElemsToArray[A, B >: A](elems: IterableOnce[A]^, xs: Array[B], start: Int = 0, len: Int = Int.MaxValue): Int = @@ -321,7 +323,7 @@ object IterableOnce { * @define exactlyOnce * Each element appears exactly once in the computation. */ -transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => +transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A]^ => /////////////////////////////////////////////////////////////// Abstract methods that must be implemented /** Produces a $coll containing cumulative results of applying the @@ -335,7 +337,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @param op the binary operator applied to the intermediate result and the element * @return collection with intermediate results */ - def scanLeft[B](z: B)(op: (B, A) => B): CC[B] + def scanLeft[B](z: B)(op: (B, A) => B): CC[B]^{this, op} /** Selects all elements of this $coll which satisfy a predicate. * @@ -343,7 +345,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @return a new $coll consisting of all elements of this $coll that satisfy the given * predicate `p`. The order of the elements is preserved. */ - def filter(p: A => Boolean): C + def filter(p: A => Boolean): C^{this, p} /** Selects all elements of this $coll which do not satisfy a predicate. * @@ -351,7 +353,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @return a new $coll consisting of all elements of this $coll that do not satisfy the given * predicate `pred`. Their order may not be preserved. */ - def filterNot(pred: A => Boolean): C + def filterNot(pred: A => Boolean): C^{this, pred} /** Selects the first `n` elements. * $orderDependent @@ -360,7 +362,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * or else the whole $coll, if it has less than `n` elements. * If `n` is negative, returns an empty $coll. */ - def take(n: Int): C + def take(n: Int): C^{this} /** Selects the longest prefix of elements that satisfy a predicate. * @@ -386,7 +388,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @return the longest prefix of this $coll whose elements all satisfy * the predicate `p`. */ - def takeWhile(p: A => Boolean): C + def takeWhile(p: A => Boolean): C^{this, p} /** Selects all elements except the first `n` ones. * $orderDependent @@ -395,7 +397,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * empty $coll, if this $coll has less than `n` elements. * If `n` is negative, don't drop any elements. */ - def drop(n: Int): C + def drop(n: Int): C^{this} /** Selects all elements except the longest prefix that satisfies a predicate. * @@ -422,7 +424,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @return the longest suffix of this $coll whose first element * does not satisfy the predicate `p`. */ - def dropWhile(p: A => Boolean): C + def dropWhile(p: A => Boolean): C^{this, p} /** Selects an interval of elements. The returned $coll is made up * of all elements `x` which satisfy the invariant: @@ -437,7 +439,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * index `from` extending up to (but not including) index `until` * of this $coll. */ - def slice(from: Int, until: Int): C + def slice(from: Int, until: Int): C^{this} /** Builds a new $ccoll by applying a function to all elements of this $coll. * @@ -446,7 +448,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @return a new $ccoll resulting from applying the given function * `f` to each element of this $coll and collecting the results. */ - def map[B](f: A => B): CC[B] + def map[B](f: A => B): CC[B]^{this, f} /** Builds a new $ccoll by applying a function to all elements of this $coll * and using the elements of the resulting collections. @@ -479,7 +481,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @return a new $ccoll resulting from applying the given collection-valued function * `f` to each element of this $coll and concatenating the results. */ - def flatMap[B](f: A => IterableOnce[B]): CC[B] + def flatMap[B](f: A => IterableOnce[B]^): CC[B]^{this, f} /** Given that the elements of this collection are themselves iterable collections, * converts this $coll into a $ccoll comprising the elements of these iterable collections. @@ -506,7 +508,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * type of this $coll is an `Iterable`. * @return a new $ccoll resulting from concatenating all element collections. */ - def flatten[B](implicit asIterable: A => IterableOnce[B]): CC[B] + def flatten[B](implicit asIterable: A -> IterableOnce[B]): CC[B]^{this} /** Builds a new $ccoll by applying a partial function to all elements of this $coll * on which the function is defined. @@ -517,7 +519,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * `pf` to each element on which it is defined and collecting the results. * The order of the elements is preserved. */ - def collect[B](pf: PartialFunction[A, B]): CC[B] + def collect[B](pf: PartialFunction[A, B]^): CC[B]^{this, pf} /** Zips this $coll with its indices. * @@ -526,7 +528,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @example * `List("a", "b", "c").zipWithIndex == List(("a", 0), ("b", 1), ("c", 2))` */ - def zipWithIndex: CC[(A @uncheckedVariance, Int)] + def zipWithIndex: CC[(A @uncheckedVariance, Int)]^{this} /** Splits this $coll into a prefix/suffix pair according to a predicate. * @@ -539,7 +541,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @return a pair consisting of the longest prefix of this $coll whose * elements all satisfy `p`, and the rest of this $coll. */ - def span(p: A => Boolean): (C, C) + def span(p: A => Boolean): (C^{this, p}, C^{this, p}) /** Splits this $coll into a prefix/suffix pair at a given position. * @@ -551,7 +553,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @return a pair of ${coll}s consisting of the first `n` * elements of this $coll, and the other elements. */ - def splitAt(n: Int): (C, C) = { + def splitAt(n: Int): (C^{this}, C^{this}) = { class Spanner extends runtime.AbstractFunction1[A, Boolean] { var i = 0 def apply(a: A) = i < n && { i += 1 ; true } @@ -569,7 +571,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @tparam U the return type of f * @return The same logical collection as this */ - def tapEach[U](f: A => U): C + def tapEach[U](f: A => U): C^{this, f} /////////////////////////////////////////////////////////////// Concrete methods based on iterator @@ -911,7 +913,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn case _ => Some(reduceLeft(op)) } private final def reduceLeftOptionIterator[B >: A](op: (B, A) => B): Option[B] = reduceOptionIterator[A, B](iterator)(op) - private final def reduceOptionIterator[X >: A, B >: X](it: Iterator[X])(op: (B, X) => B): Option[B] = { + private final def reduceOptionIterator[X >: A, B >: X](it: Iterator[X]^)(op: (B, X) => B): Option[B] = { if (it.hasNext) { var acc: B = it.next() while (it.hasNext) @@ -1154,13 +1156,13 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @return the first element of this $coll with the largest value measured by function `f` * with respect to the ordering `cmp`. */ - def maxBy[B](f: A => B)(implicit ord: Ordering[B]): A = + def maxBy[B](f: A -> B)(implicit ord: Ordering[B]): A = knownSize match { case 0 => throw new UnsupportedOperationException("empty.maxBy") case _ => foldLeft(new Maximized[A, B]("maxBy")(f)(ord.gt))((m, a) => m(m, a)).result } - private class Maximized[X, B](descriptor: String)(f: X => B)(cmp: (B, B) => Boolean) extends AbstractFunction2[Maximized[X, B], X, Maximized[X, B]] { + private class Maximized[X, B](descriptor: String)(f: X -> B)(cmp: (B, B) -> Boolean) extends AbstractFunction2[Maximized[X, B], X, Maximized[X, B]] { var maxElem: X = null.asInstanceOf[X] var maxF: B = null.asInstanceOf[B] var nonEmpty = false @@ -1193,7 +1195,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @return an option value containing the first element of this $coll with the * largest value measured by function `f` with respect to the ordering `cmp`. */ - def maxByOption[B](f: A => B)(implicit ord: Ordering[B]): Option[A] = + def maxByOption[B](f: A -> B)(implicit ord: Ordering[B]): Option[A] = knownSize match { case 0 => None case _ => foldLeft(new Maximized[A, B]("maxBy")(f)(ord.gt))((m, a) => m(m, a)).toOption @@ -1210,7 +1212,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @return the first element of this $coll with the smallest value measured by function `f` * with respect to the ordering `cmp`. */ - def minBy[B](f: A => B)(implicit ord: Ordering[B]): A = + def minBy[B](f: A -> B)(implicit ord: Ordering[B]): A = knownSize match { case 0 => throw new UnsupportedOperationException("empty.minBy") case _ => foldLeft(new Maximized[A, B]("minBy")(f)(ord.lt))((m, a) => m(m, a)).result @@ -1227,7 +1229,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * with the smallest value measured by function `f` * with respect to the ordering `cmp`. */ - def minByOption[B](f: A => B)(implicit ord: Ordering[B]): Option[A] = + def minByOption[B](f: A -> B)(implicit ord: Ordering[B]): Option[A] = knownSize match { case 0 => None case _ => foldLeft(new Maximized[A, B]("minBy")(f)(ord.lt))((m, a) => m(m, a)).toOption @@ -1244,7 +1246,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * value for which it is defined, or `None` if none exists. * @example `Seq("a", 1, 5L).collectFirst({ case x: Int => x*10 }) = Some(10)` */ - def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = { + def collectFirst[B](pf: PartialFunction[A, B]^): Option[B] = { // Presumably the fastest way to get in and out of a partial function is for a sentinel function to return itself // (Tested to be lower-overhead than runWith. Would be better yet to not need to (formally) allocate it) val sentinel: scala.Function1[A, Any] = new AbstractFunction1[A, Any] { @@ -1286,7 +1288,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * `p(x, y)` is `true` for all corresponding elements `x` of this iterator * and `y` of `that`, otherwise `false` */ - def corresponds[B](that: IterableOnce[B])(p: (A, B) => Boolean): Boolean = { + def corresponds[B](that: IterableOnce[B]^)(p: (A, B) => Boolean): Boolean = { val a = iterator val b = that.iterator @@ -1433,10 +1435,10 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * xs.to(BitSet) // for xs: Iterable[Int] * }}} */ - def to[C1](factory: Factory[A, C1]): C1 = factory.fromSpecific(this) + def to[C1](factory: Factory[A, C1]): C1^{this} = factory.fromSpecific(this) @deprecated("Use .iterator instead of .toIterator", "2.13.0") - @`inline` final def toIterator: Iterator[A] = iterator + @`inline` final def toIterator: Iterator[A]^{this} = iterator /** Converts this $coll to a `List`. * @@ -1485,7 +1487,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn * @tparam B The type of elements of the result, a supertype of `A`. * @return This $coll as a `Buffer[B]`. */ - @inline final def toBuffer[B >: A]: mutable.Buffer[B] = mutable.Buffer.from(this) + @inline final def toBuffer[B >: A]: mutable.Buffer[B] = caps.unsafe.unsafeAssumePure(mutable.Buffer.from(this)) // TODO will go away when buffer is fixed /** Converts this $coll to an `Array`. * @@ -1504,7 +1506,7 @@ transparent trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOn else mutable.ArrayBuilder.make[B].addAll(this).result() // For internal use - protected def reversed: Iterable[A] = { + protected def reversed: Iterable[A]^{this} = { var xs: immutable.List[A] = immutable.Nil val it = iterator while (it.hasNext) xs = it.next() :: xs diff --git a/library/src/scala/collection/Iterator.scala b/library/src/scala/collection/Iterator.scala index 4fb1cc6d362b..6a6132990f76 100644 --- a/library/src/scala/collection/Iterator.scala +++ b/library/src/scala/collection/Iterator.scala @@ -13,6 +13,8 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.mutable.{ArrayBuffer, ArrayBuilder, Builder, ImmutableBuilder} import scala.annotation.tailrec import scala.annotation.unchecked.uncheckedVariance @@ -72,7 +74,8 @@ import scala.runtime.Statics * iterators as well. * @define coll iterator */ -trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Iterator[A]] { self => +trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Iterator[A]] { + self: Iterator[A]^ => /** Check if there is a next element available. * @@ -118,10 +121,11 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * @return a buffered iterator producing the same values as this iterator. * @note Reuse: $consumesAndProducesIterator */ - def buffered: BufferedIterator[A] = new AbstractIterator[A] with BufferedIterator[A] { + def buffered: BufferedIterator[A]^{this} = new AbstractIterator[A] with BufferedIterator[A] { private[this] var hd: A = _ private[this] var hdDefined: Boolean = false + def head: A = { if (!hdDefined) { hd = next() @@ -154,7 +158,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * A `GroupedIterator` is yielded by `grouped` and by `sliding`, * where the `step` may differ from the group `size`. */ - class GroupedIterator[B >: A](self: Iterator[B], size: Int, step: Int) extends AbstractIterator[immutable.Seq[B]] { + class GroupedIterator[B >: A](self: Iterator[B]^, size: Int, step: Int) extends AbstractIterator[immutable.Seq[B]] { require(size >= 1 && step >= 1, f"size=$size%d and step=$step%d, but both must be positive") @@ -163,7 +167,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite private[this] var first = true // if !first, advancing may skip ahead private[this] var filled = false // whether the buffer is "hot" private[this] var partial = true // whether to emit partial sequence - private[this] var padding: () => B = null // what to pad short sequences with + private[this] var padding: () -> B = null // what to pad short sequences with private[this] def pad = padding != null // irrespective of partial flag private[this] def newBuilder = { val b = ArrayBuilder.make[Any] @@ -186,7 +190,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * @note This method is mutually exclusive with `withPartial`. * @group Configuration */ - def withPadding(x: => B): this.type = { + def withPadding(x: -> B): this.type = { padding = () => x partial = true // redundant, as padding always results in complete segment this @@ -292,7 +296,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * all elements of this $coll followed by the minimal number of occurrences of `elem` so * that the resulting collection has a length of at least `len`. */ - def padTo[B >: A](len: Int, elem: B): Iterator[B] = new AbstractIterator[B] { + def padTo[B >: A](len: Int, elem: B): Iterator[B]^{this} = new AbstractIterator[B] { private[this] var i = 0 override def knownSize: Int = { @@ -322,7 +326,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * is the same as in the original iterator. * @note Reuse: $consumesOneAndProducesTwoIterators */ - def partition(p: A => Boolean): (Iterator[A], Iterator[A]) = { + def partition(p: A => Boolean): (Iterator[A]^{this, p}, Iterator[A]^{this, p}) = { val (a, b) = duplicate (a filter p, b filterNot p) } @@ -342,7 +346,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * * @note Reuse: $consumesAndProducesIterator */ - def grouped[B >: A](size: Int): GroupedIterator[B] = + def grouped[B >: A](size: Int): GroupedIterator[B]^{this} = new GroupedIterator[B](self, size, size) /** Returns an iterator which presents a "sliding window" view of @@ -378,13 +382,13 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * * @note Reuse: $consumesAndProducesIterator */ - def sliding[B >: A](size: Int, step: Int = 1): GroupedIterator[B] = + def sliding[B >: A](size: Int, step: Int = 1): GroupedIterator[B]^{this} = new GroupedIterator[B](self, size, step) - def scanLeft[B](z: B)(op: (B, A) => B): Iterator[B] = new AbstractIterator[B] { + def scanLeft[B](z: B)(op: (B, A) => B): Iterator[B]^{this, op} = new AbstractIterator[B] { // We use an intermediate iterator that iterates through the first element `z` // and then that will be modified to iterate through the collection - private[this] var current: Iterator[B] = + private[this] var current: Iterator[B]^{self, op} = new AbstractIterator[B] { override def knownSize = { val thisSize = self.knownSize @@ -413,7 +417,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite } @deprecated("Call scanRight on an Iterable instead.", "2.13.0") - def scanRight[B](z: B)(op: (A, B) => B): Iterator[B] = ArrayBuffer.from(this).scanRight(z)(op).iterator + def scanRight[B](z: B)(op: (A, B) => B): Iterator[B]^{this, op} = ArrayBuffer.from(this).scanRight(z)(op).iterator /** Finds index of the first element satisfying some predicate after or at some start index. * @@ -476,11 +480,11 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite @deprecatedOverriding("isEmpty is defined as !hasNext; override hasNext instead", "2.13.0") override def isEmpty: Boolean = !hasNext - def filter(p: A => Boolean): Iterator[A] = filterImpl(p, isFlipped = false) + def filter(p: A => Boolean): Iterator[A]^{this, p} = filterImpl(p, isFlipped = false) - def filterNot(p: A => Boolean): Iterator[A] = filterImpl(p, isFlipped = true) + def filterNot(p: A => Boolean): Iterator[A]^{this, p} = filterImpl(p, isFlipped = true) - private[collection] def filterImpl(p: A => Boolean, isFlipped: Boolean): Iterator[A] = new AbstractIterator[A] { + private[collection] def filterImpl(p: A => Boolean, isFlipped: Boolean): Iterator[A]^{this, p} = new AbstractIterator[A] { private[this] var hd: A = _ private[this] var hdDefined: Boolean = false @@ -514,9 +518,9 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * @return an iterator which produces those values of this iterator which satisfy the predicate `p`. * @note Reuse: $consumesAndProducesIterator */ - def withFilter(p: A => Boolean): Iterator[A] = filter(p) + def withFilter(p: A => Boolean): Iterator[A]^{this, p} = filter(p) - def collect[B](pf: PartialFunction[A, B]): Iterator[B] = new AbstractIterator[B] with (A => B) { + def collect[B](pf: PartialFunction[A, B]^): Iterator[B]^{this, pf} = new AbstractIterator[B] with (A => B) { // Manually buffer to avoid extra layer of wrapping with buffered private[this] var hd: B = _ @@ -552,7 +556,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * * @note Reuse: $consumesIterator */ - def distinct: Iterator[A] = distinctBy(identity) + def distinct: Iterator[A]^{this} = distinctBy(identity) /** * Builds a new iterator from this one without any duplicated elements as determined by `==` after applying @@ -564,7 +568,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * * @note Reuse: $consumesIterator */ - def distinctBy[B](f: A => B): Iterator[A] = new AbstractIterator[A] { + def distinctBy[B](f: A -> B): Iterator[A]^{this} = new AbstractIterator[A] { private[this] val traversedValues = mutable.HashSet.empty[B] private[this] var nextElementDefined: Boolean = false @@ -589,14 +593,14 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite } } - def map[B](f: A => B): Iterator[B] = new AbstractIterator[B] { + def map[B](f: A => B): Iterator[B]^{this, f} = new AbstractIterator[B] { override def knownSize = self.knownSize def hasNext = self.hasNext def next() = f(self.next()) } - def flatMap[B](f: A => IterableOnce[B]): Iterator[B] = new AbstractIterator[B] { - private[this] var cur: Iterator[B] = Iterator.empty + def flatMap[B](f: A => IterableOnce[B]^): Iterator[B]^{this, f} = new AbstractIterator[B] { + private[this] var cur: Iterator[B]^{f} = Iterator.empty /** Trillium logic boolean: -1 = unknown, 0 = false, 1 = true */ private[this] var _hasNext: Int = -1 @@ -630,19 +634,19 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite } } - def flatten[B](implicit ev: A => IterableOnce[B]): Iterator[B] = + def flatten[B](implicit ev: A -> IterableOnce[B]): Iterator[B]^{this} = flatMap[B](ev) - def concat[B >: A](xs: => IterableOnce[B]): Iterator[B] = new Iterator.ConcatIterator[B](self).concat(xs) + def concat[B >: A](xs: => IterableOnce[B]^): Iterator[B]^{this, xs} = new Iterator.ConcatIterator[B](self).concat(xs) - @`inline` final def ++ [B >: A](xs: => IterableOnce[B]): Iterator[B] = concat(xs) + @`inline` final def ++ [B >: A](xs: => IterableOnce[B]^): Iterator[B]^{this, xs} = concat(xs) - def take(n: Int): Iterator[A] = sliceIterator(0, n max 0) + def take(n: Int): Iterator[A]^{this} = sliceIterator(0, n max 0) - def takeWhile(p: A => Boolean): Iterator[A] = new AbstractIterator[A] { + def takeWhile(p: A => Boolean): Iterator[A]^{this, p} = new AbstractIterator[A] { private[this] var hd: A = _ private[this] var hdDefined: Boolean = false - private[this] var tail: Iterator[A] = self + private[this] var tail: Iterator[A]^{self} = self def hasNext = hdDefined || tail.hasNext && { hd = tail.next() @@ -653,9 +657,9 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite def next() = if (hasNext) { hdDefined = false; hd } else Iterator.empty.next() } - def drop(n: Int): Iterator[A] = sliceIterator(n, -1) + def drop(n: Int): Iterator[A]^{this} = sliceIterator(n, -1) - def dropWhile(p: A => Boolean): Iterator[A] = new AbstractIterator[A] { + def dropWhile(p: A => Boolean): Iterator[A]^{this, p} = new AbstractIterator[A] { // Magic value: -1 = hasn't dropped, 0 = found first, 1 = defer to parent iterator private[this] var status = -1 // Local buffering to avoid double-wrap with .buffered @@ -691,7 +695,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * * @note Reuse: $consumesOneAndProducesTwoIterators */ - def span(p: A => Boolean): (Iterator[A], Iterator[A]) = { + def span(p: A => Boolean): (Iterator[A]^{this, p}, Iterator[A]^{this, p}) = { /* * Giving a name to following iterator (as opposed to trailing) because * anonymous class is represented as a structural type that trailing @@ -790,10 +794,10 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite (leading, trailing) } - def slice(from: Int, until: Int): Iterator[A] = sliceIterator(from, until max 0) + def slice(from: Int, until: Int): Iterator[A]^{this} = sliceIterator(from, until max 0) /** Creates an optionally bounded slice, unbounded if `until` is negative. */ - protected def sliceIterator(from: Int, until: Int): Iterator[A] = { + protected def sliceIterator(from: Int, until: Int): Iterator[A]^{this} = { val lo = from max 0 val rest = if (until < 0) -1 // unbounded @@ -804,14 +808,14 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite else new Iterator.SliceIterator(this, lo, rest) } - def zip[B](that: IterableOnce[B]): Iterator[(A, B)] = new AbstractIterator[(A, B)] { + def zip[B](that: IterableOnce[B]^): Iterator[(A, B)]^{this, that} = new AbstractIterator[(A, B)] { val thatIterator = that.iterator override def knownSize = self.knownSize min thatIterator.knownSize def hasNext = self.hasNext && thatIterator.hasNext def next() = (self.next(), thatIterator.next()) } - def zipAll[A1 >: A, B](that: IterableOnce[B], thisElem: A1, thatElem: B): Iterator[(A1, B)] = new AbstractIterator[(A1, B)] { + def zipAll[A1 >: A, B](that: IterableOnce[B]^, thisElem: A1, thatElem: B): Iterator[(A1, B)]^{this, that} = new AbstractIterator[(A1, B)] { val thatIterator = that.iterator override def knownSize = { val thisSize = self.knownSize @@ -828,7 +832,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite } } - def zipWithIndex: Iterator[(A, Int)] = new AbstractIterator[(A, Int)] { + def zipWithIndex: Iterator[(A, Int)]^{this} = new AbstractIterator[(A, Int)] { var idx = 0 override def knownSize = self.knownSize def hasNext = self.hasNext @@ -846,7 +850,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * @tparam B the type of the elements of collection `that`. * @return `true` if both collections contain equal elements in the same order, `false` otherwise. */ - def sameElements[B >: A](that: IterableOnce[B]): Boolean = { + def sameElements[B >: A](that: IterableOnce[B]^): Boolean = { val those = that.iterator while (hasNext) { if (!those.hasNext) return false @@ -868,7 +872,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * iterated by one iterator but not yet by the other. * @note Reuse: $consumesOneAndProducesTwoIterators */ - def duplicate: (Iterator[A], Iterator[A]) = { + def duplicate: (Iterator[A]^{this}, Iterator[A]^{this}) = { val gap = new scala.collection.mutable.Queue[A] var ahead: Iterator[A] = null class Partner extends AbstractIterator[A] { @@ -912,7 +916,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite * @param replaced The number of values in the original iterator that are replaced by the patch. * @note Reuse: $consumesTwoAndProducesOneIterator */ - def patch[B >: A](from: Int, patchElems: Iterator[B], replaced: Int): Iterator[B] = + def patch[B >: A](from: Int, patchElems: Iterator[B]^, replaced: Int): Iterator[B]^{this, patchElems} = new AbstractIterator[B] { private[this] var origElems = self // > 0 => that many more elems from `origElems` before switching to `patchElems` @@ -952,7 +956,7 @@ trait Iterator[+A] extends IterableOnce[A] with IterableOnceOps[A, Iterator, Ite } } - override def tapEach[U](f: A => U): Iterator[A] = new AbstractIterator[A] { + override def tapEach[U](f: A => U): Iterator[A]^{this, f} = new AbstractIterator[A] { override def knownSize = self.knownSize override def hasNext = self.hasNext override def next() = { @@ -989,7 +993,7 @@ object Iterator extends IterableFactory[Iterator] { * @tparam A the type of the collection’s elements * @return a new $coll with the elements of `source` */ - override def from[A](source: IterableOnce[A]): Iterator[A] = source.iterator + override def from[A](source: IterableOnce[A]^): Iterator[A]^{source} = source.iterator /** The iterator which produces no values. */ @`inline` final def empty[T]: Iterator[T] = _empty @@ -1020,7 +1024,7 @@ object Iterator extends IterableFactory[Iterator] { * @param elem the element computation * @return An iterator that produces the results of `n` evaluations of `elem`. */ - override def fill[A](len: Int)(elem: => A): Iterator[A] = new AbstractIterator[A] { + override def fill[A](len: Int)(elem: => A): Iterator[A]^{elem} = new AbstractIterator[A] { private[this] var i = 0 override def knownSize: Int = (len - i) max 0 def hasNext: Boolean = i < len @@ -1035,7 +1039,7 @@ object Iterator extends IterableFactory[Iterator] { * @param f The function computing element values * @return An iterator that produces the values `f(0), ..., f(n -1)`. */ - override def tabulate[A](end: Int)(f: Int => A): Iterator[A] = new AbstractIterator[A] { + override def tabulate[A](end: Int)(f: Int => A): Iterator[A]^{f} = new AbstractIterator[A] { private[this] var i = 0 override def knownSize: Int = (end - i) max 0 def hasNext: Boolean = i < end @@ -1108,7 +1112,7 @@ object Iterator extends IterableFactory[Iterator] { * @param f the function that's repeatedly applied * @return the iterator producing the infinite sequence of values `start, f(start), f(f(start)), ...` */ - def iterate[T](start: T)(f: T => T): Iterator[T] = new AbstractIterator[T] { + def iterate[T](start: T)(f: T => T): Iterator[T]^{f} = new AbstractIterator[T] { private[this] var first = true private[this] var acc = start def hasNext: Boolean = true @@ -1130,7 +1134,7 @@ object Iterator extends IterableFactory[Iterator] { * @tparam S Type of the internal state * @return an Iterator that produces elements using `f` until `f` returns `None` */ - override def unfold[A, S](init: S)(f: S => Option[(A, S)]): Iterator[A] = new UnfoldIterator(init)(f) + override def unfold[A, S](init: S)(f: S => Option[(A, S)]): Iterator[A]^{f} = new UnfoldIterator(init)(f) /** Creates an infinite-length iterator returning the results of evaluating an expression. * The expression is recomputed for every element. @@ -1138,7 +1142,7 @@ object Iterator extends IterableFactory[Iterator] { * @param elem the element computation. * @return the iterator containing an infinite number of results of evaluating `elem`. */ - def continually[A](elem: => A): Iterator[A] = new AbstractIterator[A] { + def continually[A](elem: => A): Iterator[A]^{elem} = new AbstractIterator[A] { def hasNext = true def next() = elem } @@ -1146,7 +1150,8 @@ object Iterator extends IterableFactory[Iterator] { /** Creates an iterator to which other iterators can be appended efficiently. * Nested ConcatIterators are merged to avoid blowing the stack. */ - private final class ConcatIterator[+A](private var current: Iterator[A @uncheckedVariance]) extends AbstractIterator[A] { + private final class ConcatIterator[+A](val from: Iterator[A @uncheckedVariance]^) extends AbstractIterator[A] { + private var current: Iterator[A]^{from} = from private var tail: ConcatIteratorCell[A @uncheckedVariance] = null private var last: ConcatIteratorCell[A @uncheckedVariance] = null private var currentHasNextChecked = false @@ -1162,7 +1167,7 @@ object Iterator extends IterableFactory[Iterator] { // If we advanced the current iterator to a ConcatIterator, merge it into this one @tailrec def merge(): Unit = if (current.isInstanceOf[ConcatIterator[_]]) { - val c = current.asInstanceOf[ConcatIterator[A]] + val c = current.asInstanceOf[ConcatIterator[A]^{from}] current = c.current currentHasNextChecked = c.currentHasNextChecked if (c.tail != null) { @@ -1202,7 +1207,7 @@ object Iterator extends IterableFactory[Iterator] { current.next() } else Iterator.empty.next() - override def concat[B >: A](that: => IterableOnce[B]): Iterator[B] = { + override def concat[B >: A](that: => IterableOnce[B]^): Iterator[B]^{this, that} = { val c = new ConcatIteratorCell[B](that, null).asInstanceOf[ConcatIteratorCell[A]] if (tail == null) { tail = c @@ -1217,14 +1222,14 @@ object Iterator extends IterableFactory[Iterator] { } } - private[this] final class ConcatIteratorCell[A](head: => IterableOnce[A], var tail: ConcatIteratorCell[A]) { - def headIterator: Iterator[A] = head.iterator + private[this] final class ConcatIteratorCell[A](head: => IterableOnce[A]^, var tail: ConcatIteratorCell[A]^) { + def headIterator: Iterator[A]^{this} = head.iterator } /** Creates a delegating iterator capped by a limit count. Negative limit means unbounded. * Lazily skip to start on first evaluation. Avoids daisy-chained iterators due to slicing. */ - private[scala] final class SliceIterator[A](val underlying: Iterator[A], start: Int, limit: Int) extends AbstractIterator[A] { + private[scala] final class SliceIterator[A](val underlying: Iterator[A]^, start: Int, limit: Int) extends AbstractIterator[A] { private[this] var remaining = limit private[this] var dropping = start @inline private def unbounded = remaining < 0 @@ -1255,7 +1260,7 @@ object Iterator extends IterableFactory[Iterator] { else if (unbounded) underlying.next() else empty.next() } - override protected def sliceIterator(from: Int, until: Int): Iterator[A] = { + override protected def sliceIterator(from: Int, until: Int): Iterator[A]^{this} = { val lo = from max 0 def adjustedBound = if (unbounded) -1 diff --git a/library/src/scala/collection/JavaConverters.scala b/library/src/scala/collection/JavaConverters.scala index b49e472c04b2..8d1503200188 100644 --- a/library/src/scala/collection/JavaConverters.scala +++ b/library/src/scala/collection/JavaConverters.scala @@ -13,6 +13,8 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking + import java.util.{concurrent => juc} import java.{lang => jl, util => ju} diff --git a/library/src/scala/collection/LazyZipOps.scala b/library/src/scala/collection/LazyZipOps.scala index dba5b6432def..9a7dc3173926 100644 --- a/library/src/scala/collection/LazyZipOps.scala +++ b/library/src/scala/collection/LazyZipOps.scala @@ -14,6 +14,7 @@ package scala.collection import scala.language.`2.13` import scala.language.implicitConversions +import language.experimental.captureChecking /** Decorator representing lazily zipped pairs. * @@ -22,7 +23,7 @@ import scala.language.implicitConversions * * Note: will not terminate for infinite-sized collections. */ -final class LazyZip2[+El1, +El2, C1] private[collection](src: C1, coll1: Iterable[El1], coll2: Iterable[El2]) { +final class LazyZip2[+El1, +El2, C1] private[collection](src: C1, coll1: Iterable[El1]^, coll2: Iterable[El2]^) { /** Zips `that` iterable collection with an existing `LazyZip2`. The elements in each collection are * not consumed until a strict operation is invoked on the returned `LazyZip3` decorator. @@ -32,11 +33,11 @@ final class LazyZip2[+El1, +El2, C1] private[collection](src: C1, coll1: Iterabl * @return a decorator `LazyZip3` that allows strict operations to be performed on the lazily evaluated tuples or * chained calls to `lazyZip`. Implicit conversion to `Iterable[(El1, El2, B)]` is also supported. */ - def lazyZip[B](that: Iterable[B]): LazyZip3[El1, El2, B, C1] = new LazyZip3(src, coll1, coll2, that) + def lazyZip[B](that: Iterable[B]^): LazyZip3[El1, El2, B, C1]^{this, that} = new LazyZip3(src, coll1, coll2, that) - def map[B, C](f: (El1, El2) => B)(implicit bf: BuildFrom[C1, B, C]): C = { + def map[B, C](f: (El1, El2) => B)(implicit bf: BuildFrom[C1, B, C]): C^{this, f} = { bf.fromSpecific(src)(new AbstractView[B] { - def iterator: AbstractIterator[B] = new AbstractIterator[B] { + def iterator: AbstractIterator[B]^{this, f} = new AbstractIterator[B] { private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator def hasNext = elems1.hasNext && elems2.hasNext @@ -47,12 +48,12 @@ final class LazyZip2[+El1, +El2, C1] private[collection](src: C1, coll1: Iterabl }) } - def flatMap[B, C](f: (El1, El2) => Iterable[B])(implicit bf: BuildFrom[C1, B, C]): C = { + def flatMap[B, C](f: (El1, El2) => Iterable[B]^)(implicit bf: BuildFrom[C1, B, C]): C^{this, f} = { bf.fromSpecific(src)(new AbstractView[B] { - def iterator: AbstractIterator[B] = new AbstractIterator[B] { + def iterator: AbstractIterator[B]^{this, f} = new AbstractIterator[B] { private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator - private[this] var _current: Iterator[B] = Iterator.empty + private[this] var _current: Iterator[B]^{this, f} = Iterator.empty private def current = { while (!_current.hasNext && elems1.hasNext && elems2.hasNext) _current = f(elems1.next(), elems2.next()).iterator @@ -66,9 +67,9 @@ final class LazyZip2[+El1, +El2, C1] private[collection](src: C1, coll1: Iterabl }) } - def filter[C](p: (El1, El2) => Boolean)(implicit bf: BuildFrom[C1, (El1, El2), C]): C = { + def filter[C](p: (El1, El2) => Boolean)(implicit bf: BuildFrom[C1, (El1, El2), C]): C^{this, p} = { bf.fromSpecific(src)(new AbstractView[(El1, El2)] { - def iterator: AbstractIterator[(El1, El2)] = new AbstractIterator[(El1, El2)] { + def iterator: AbstractIterator[(El1, El2)]^{this, p} = new AbstractIterator[(El1, El2)] { private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator private[this] var _current: (El1, El2) = _ @@ -113,8 +114,8 @@ final class LazyZip2[+El1, +El2, C1] private[collection](src: C1, coll1: Iterabl while (elems1.hasNext && elems2.hasNext) f(elems1.next(), elems2.next()) } - private def toIterable: View[(El1, El2)] = new AbstractView[(El1, El2)] { - def iterator: AbstractIterator[(El1, El2)] = new AbstractIterator[(El1, El2)] { + private def toIterable: View[(El1, El2)]^{this} = new AbstractView[(El1, El2)] { + def iterator: AbstractIterator[(El1, El2)]^{this} = new AbstractIterator[(El1, El2)] { private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator def hasNext = elems1.hasNext && elems2.hasNext @@ -136,7 +137,7 @@ final class LazyZip2[+El1, +El2, C1] private[collection](src: C1, coll1: Iterabl } object LazyZip2 { - implicit def lazyZip2ToIterable[El1, El2](zipped2: LazyZip2[El1, El2, _]): View[(El1, El2)] = zipped2.toIterable + implicit def lazyZip2ToIterable[El1, El2](zipped2: LazyZip2[El1, El2, _]^): View[(El1, El2)]^{zipped2} = zipped2.toIterable } @@ -148,9 +149,9 @@ object LazyZip2 { * Note: will not terminate for infinite-sized collections. */ final class LazyZip3[+El1, +El2, +El3, C1] private[collection](src: C1, - coll1: Iterable[El1], - coll2: Iterable[El2], - coll3: Iterable[El3]) { + coll1: Iterable[El1]^, + coll2: Iterable[El2]^, + coll3: Iterable[El3]^) { /** Zips `that` iterable collection with an existing `LazyZip3`. The elements in each collection are * not consumed until a strict operation is invoked on the returned `LazyZip4` decorator. @@ -160,11 +161,11 @@ final class LazyZip3[+El1, +El2, +El3, C1] private[collection](src: C1, * @return a decorator `LazyZip4` that allows strict operations to be performed on the lazily evaluated tuples. * Implicit conversion to `Iterable[(El1, El2, El3, B)]` is also supported. */ - def lazyZip[B](that: Iterable[B]): LazyZip4[El1, El2, El3, B, C1] = new LazyZip4(src, coll1, coll2, coll3, that) + def lazyZip[B](that: Iterable[B]^): LazyZip4[El1, El2, El3, B, C1]^{this, that} = new LazyZip4(src, coll1, coll2, coll3, that) - def map[B, C](f: (El1, El2, El3) => B)(implicit bf: BuildFrom[C1, B, C]): C = { + def map[B, C](f: (El1, El2, El3) => B)(implicit bf: BuildFrom[C1, B, C]): C^{this, f} = { bf.fromSpecific(src)(new AbstractView[B] { - def iterator: AbstractIterator[B] = new AbstractIterator[B] { + def iterator: AbstractIterator[B]^{this, f} = new AbstractIterator[B] { private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator private[this] val elems3 = coll3.iterator @@ -176,13 +177,13 @@ final class LazyZip3[+El1, +El2, +El3, C1] private[collection](src: C1, }) } - def flatMap[B, C](f: (El1, El2, El3) => Iterable[B])(implicit bf: BuildFrom[C1, B, C]): C = { + def flatMap[B, C](f: (El1, El2, El3) => Iterable[B]^)(implicit bf: BuildFrom[C1, B, C]): C^{this, f} = { bf.fromSpecific(src)(new AbstractView[B] { - def iterator: AbstractIterator[B] = new AbstractIterator[B] { + def iterator: AbstractIterator[B]^{this, f} = new AbstractIterator[B] { private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator private[this] val elems3 = coll3.iterator - private[this] var _current: Iterator[B] = Iterator.empty + private[this] var _current: Iterator[B]^{this, f} = Iterator.empty private def current = { while (!_current.hasNext && elems1.hasNext && elems2.hasNext && elems3.hasNext) _current = f(elems1.next(), elems2.next(), elems3.next()).iterator @@ -196,9 +197,9 @@ final class LazyZip3[+El1, +El2, +El3, C1] private[collection](src: C1, }) } - def filter[C](p: (El1, El2, El3) => Boolean)(implicit bf: BuildFrom[C1, (El1, El2, El3), C]): C = { + def filter[C](p: (El1, El2, El3) => Boolean)(implicit bf: BuildFrom[C1, (El1, El2, El3), C]): C^{this, p} = { bf.fromSpecific(src)(new AbstractView[(El1, El2, El3)] { - def iterator: AbstractIterator[(El1, El2, El3)] = new AbstractIterator[(El1, El2, El3)] { + def iterator: AbstractIterator[(El1, El2, El3)]^{this, p} = new AbstractIterator[(El1, El2, El3)] { private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator private[this] val elems3 = coll3.iterator @@ -249,8 +250,8 @@ final class LazyZip3[+El1, +El2, +El3, C1] private[collection](src: C1, f(elems1.next(), elems2.next(), elems3.next()) } - private def toIterable: View[(El1, El2, El3)] = new AbstractView[(El1, El2, El3)] { - def iterator: AbstractIterator[(El1, El2, El3)] = new AbstractIterator[(El1, El2, El3)] { + private def toIterable: View[(El1, El2, El3)]^{this} = new AbstractView[(El1, El2, El3)] { + def iterator: AbstractIterator[(El1, El2, El3)]^{this} = new AbstractIterator[(El1, El2, El3)] { private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator private[this] val elems3 = coll3.iterator @@ -276,7 +277,7 @@ final class LazyZip3[+El1, +El2, +El3, C1] private[collection](src: C1, } object LazyZip3 { - implicit def lazyZip3ToIterable[El1, El2, El3](zipped3: LazyZip3[El1, El2, El3, _]): View[(El1, El2, El3)] = zipped3.toIterable + implicit def lazyZip3ToIterable[El1, El2, El3](zipped3: LazyZip3[El1, El2, El3, _]^): View[(El1, El2, El3)]^{zipped3} = zipped3.toIterable } @@ -289,14 +290,14 @@ object LazyZip3 { * Note: will not terminate for infinite-sized collections. */ final class LazyZip4[+El1, +El2, +El3, +El4, C1] private[collection](src: C1, - coll1: Iterable[El1], - coll2: Iterable[El2], - coll3: Iterable[El3], - coll4: Iterable[El4]) { + coll1: Iterable[El1]^, + coll2: Iterable[El2]^, + coll3: Iterable[El3]^, + coll4: Iterable[El4]^) { - def map[B, C](f: (El1, El2, El3, El4) => B)(implicit bf: BuildFrom[C1, B, C]): C = { + def map[B, C](f: (El1, El2, El3, El4) => B)(implicit bf: BuildFrom[C1, B, C]): C^{this, f} = { bf.fromSpecific(src)(new AbstractView[B] { - def iterator: AbstractIterator[B] = new AbstractIterator[B] { + def iterator: AbstractIterator[B]^{this, f} = new AbstractIterator[B] { private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator private[this] val elems3 = coll3.iterator @@ -309,14 +310,14 @@ final class LazyZip4[+El1, +El2, +El3, +El4, C1] private[collection](src: C1, }) } - def flatMap[B, C](f: (El1, El2, El3, El4) => Iterable[B])(implicit bf: BuildFrom[C1, B, C]): C = { + def flatMap[B, C](f: (El1, El2, El3, El4) => Iterable[B]^)(implicit bf: BuildFrom[C1, B, C]): C^{this, f} = { bf.fromSpecific(src)(new AbstractView[B] { - def iterator: AbstractIterator[B] = new AbstractIterator[B] { + def iterator: AbstractIterator[B]^{this, f} = new AbstractIterator[B] { private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator private[this] val elems3 = coll3.iterator private[this] val elems4 = coll4.iterator - private[this] var _current: Iterator[B] = Iterator.empty + private[this] var _current: Iterator[B]^{this, f} = Iterator.empty private def current = { while (!_current.hasNext && elems1.hasNext && elems2.hasNext && elems3.hasNext && elems4.hasNext) _current = f(elems1.next(), elems2.next(), elems3.next(), elems4.next()).iterator @@ -330,9 +331,9 @@ final class LazyZip4[+El1, +El2, +El3, +El4, C1] private[collection](src: C1, }) } - def filter[C](p: (El1, El2, El3, El4) => Boolean)(implicit bf: BuildFrom[C1, (El1, El2, El3, El4), C]): C = { + def filter[C](p: (El1, El2, El3, El4) => Boolean)(implicit bf: BuildFrom[C1, (El1, El2, El3, El4), C]): C^{this, p} = { bf.fromSpecific(src)(new AbstractView[(El1, El2, El3, El4)] { - def iterator: AbstractIterator[(El1, El2, El3, El4)] = new AbstractIterator[(El1, El2, El3, El4)] { + def iterator: AbstractIterator[(El1, El2, El3, El4)]^{this, p} = new AbstractIterator[(El1, El2, El3, El4)] { private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator private[this] val elems3 = coll3.iterator @@ -387,7 +388,7 @@ final class LazyZip4[+El1, +El2, +El3, +El4, C1] private[collection](src: C1, f(elems1.next(), elems2.next(), elems3.next(), elems4.next()) } - private def toIterable: View[(El1, El2, El3, El4)] = new AbstractView[(El1, El2, El3, El4)] { + private def toIterable: View[(El1, El2, El3, El4)]^{this} = new AbstractView[(El1, El2, El3, El4)] { def iterator: AbstractIterator[(El1, El2, El3, El4)] = new AbstractIterator[(El1, El2, El3, El4)] { private[this] val elems1 = coll1.iterator private[this] val elems2 = coll2.iterator @@ -418,6 +419,6 @@ final class LazyZip4[+El1, +El2, +El3, +El4, C1] private[collection](src: C1, } object LazyZip4 { - implicit def lazyZip4ToIterable[El1, El2, El3, El4](zipped4: LazyZip4[El1, El2, El3, El4, _]): View[(El1, El2, El3, El4)] = + implicit def lazyZip4ToIterable[El1, El2, El3, El4](zipped4: LazyZip4[El1, El2, El3, El4, _]^): View[(El1, El2, El3, El4)]^{zipped4} = zipped4.toIterable } diff --git a/library/src/scala/collection/LinearSeq.scala b/library/src/scala/collection/LinearSeq.scala index f6ae57168fcd..2fa512e32ef9 100644 --- a/library/src/scala/collection/LinearSeq.scala +++ b/library/src/scala/collection/LinearSeq.scala @@ -14,6 +14,8 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.{nowarn, tailrec} /** Base trait for linearly accessed sequences that have efficient `head` and @@ -33,7 +35,7 @@ trait LinearSeq[+A] extends Seq[A] object LinearSeq extends SeqFactory.Delegate[LinearSeq](immutable.LinearSeq) /** Base trait for linear Seq operations */ -transparent trait LinearSeqOps[+A, +CC[X] <: LinearSeq[X], +C <: LinearSeq[A] with LinearSeqOps[A, CC, C]] extends Any with SeqOps[A, CC, C] { +transparent trait LinearSeqOps[+A, +CC[X] <: LinearSeq[X], +C <: LinearSeq[A] with LinearSeqOps[A, CC, C]] extends Any with StrictSeqOps[A, CC, C] { self => /** @inheritdoc * @@ -97,7 +99,7 @@ transparent trait LinearSeqOps[+A, +CC[X] <: LinearSeq[X], +C <: LinearSeq[A] wi else loop(0, coll) } - override def lengthCompare(that: Iterable[_]): Int = { + override def lengthCompare(that: Iterable[_]^): Int = { val thatKnownSize = that.knownSize if (thatKnownSize >= 0) this lengthCompare thatKnownSize @@ -187,7 +189,7 @@ transparent trait LinearSeqOps[+A, +CC[X] <: LinearSeq[X], +C <: LinearSeq[A] wi acc } - override def sameElements[B >: A](that: IterableOnce[B]): Boolean = { + override def sameElements[B >: A](that: IterableOnce[B]^): Boolean = { @tailrec def linearSeqEq(a: LinearSeq[B], b: LinearSeq[B]): Boolean = (a eq b) || { if (a.nonEmpty && b.nonEmpty && a.head == b.head) { @@ -260,7 +262,7 @@ transparent trait LinearSeqOps[+A, +CC[X] <: LinearSeq[X], +C <: LinearSeq[A] wi } } -transparent trait StrictOptimizedLinearSeqOps[+A, +CC[X] <: LinearSeq[X], +C <: LinearSeq[A] with StrictOptimizedLinearSeqOps[A, CC, C]] extends Any with LinearSeqOps[A, CC, C] with StrictOptimizedSeqOps[A, CC, C] { +transparent trait StrictOptimizedLinearSeqOps[+A, +CC[X] <: LinearSeq[X], +C <: LinearSeq[A] with StrictOptimizedLinearSeqOps[A, CC, C]] extends Any with LinearSeqOps[A, CC, C] with StrictOptimizedSeqOps[A, CC, C] { self => // A more efficient iterator implementation than the default LinearSeqIterator override def iterator: Iterator[A] = new AbstractIterator[A] { private[this] var current = StrictOptimizedLinearSeqOps.this diff --git a/library/src/scala/collection/Map.scala b/library/src/scala/collection/Map.scala index 4d448f96c7cb..a2b0ec84be82 100644 --- a/library/src/scala/collection/Map.scala +++ b/library/src/scala/collection/Map.scala @@ -14,6 +14,9 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking +import scala.annotation.unchecked.uncheckedVariance + import scala.annotation.nowarn import scala.collection.generic.DefaultSerializable import scala.collection.mutable.StringBuilder @@ -22,11 +25,12 @@ import scala.util.hashing.MurmurHash3 /** Base Map type */ trait Map[K, +V] extends Iterable[(K, V)] - with MapOps[K, V, Map, Map[K, V]] + with StrictMapOps[K, V, Map, Map[K, V]] with MapFactoryDefaults[K, V, Map, Iterable] - with Equals { + with Equals + with caps.Pure { - def mapFactory: scala.collection.MapFactory[Map] = Map + def mapFactory: scala.collection.StrictMapFactory[Map] = Map def canEqual(that: Any): Boolean = true @@ -86,6 +90,46 @@ trait Map[K, +V] override def toString(): String = super[Iterable].toString() // Because `Function1` overrides `toString` too } +/** Map operations for strict maps. */ +transparent trait StrictMapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _] with caps.Pure, +C] + extends MapOps[K, V, CC, C] with caps.Pure { + + override def mapFactory: StrictMapFactory[CC] + override protected def fromSpecific(coll: IterableOnce[(K, V) @uncheckedVariance]^): C + + @`inline` override protected def mapFromIterable[K2, V2](it: Iterable[(K2, V2)]^): CC[K2, V2] = mapFactory.from(it) + + override def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = mapFactory.from(new View.Map(this, f)) + + override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]^): CC[K2, V2] = + mapFactory.from(new View.Collect(this, pf)) + + override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]^): CC[K2, V2] = mapFactory.from(new View.FlatMap(this, f)) + + override def concat[V2 >: V](suffix: collection.IterableOnce[(K, V2)]^): CC[K, V2] = mapFactory.from(suffix match { + case it: Iterable[(K, V2)] => new View.Concat(this, it) + case _ => iterator.concat(suffix.iterator) + }) + + override def ++ [V2 >: V](xs: collection.IterableOnce[(K, V2)]^): CC[K, V2] = concat(xs) + + @`inline` override def -- (keys: IterableOnce[K]^): C = { + lazy val keysSet = keys.iterator.to(immutable.Set) + fromSpecific(this.view.filterKeys(k => !keysSet.contains(k))) + } + + @deprecated("Use ++ instead of ++: for collections of type Iterable", "2.13.0") + override def ++: [V1 >: V](that: IterableOnce[(K,V1)]^): CC[K,V1] = { + val thatIterable: Iterable[(K, V1)]^{that} = that match { + case that: Iterable[(K, V1)] => that + case that => View.from(that) + } + mapFactory.from(new View.Concat(thatIterable, this)) + } + + +} + /** Base Map implementation type * * @tparam K Type of keys @@ -101,9 +145,9 @@ trait Map[K, +V] // erase CC to IterableOps instead of Object transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] extends IterableOps[(K, V), Iterable, C] - with PartialFunction[K, V] { + with PartialFunction[K, V] { self: MapOps[K, V, CC, C]^ => - override def view: MapView[K, V] = new MapView.Id(this) + override def view: MapView[K, V]^{this} = new MapView.Id(this) /** Returns a [[Stepper]] for the keys of this map. See method [[stepper]]. */ def keyStepper[S <: Stepper[_]](implicit shape: StepperShape[K, S]): S = { @@ -132,7 +176,7 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] /** Similar to `fromIterable`, but returns a Map collection type. * Note that the return type is now `CC[K2, V2]`. */ - @`inline` protected final def mapFromIterable[K2, V2](it: Iterable[(K2, V2)]): CC[K2, V2] = mapFactory.from(it) + @`inline` protected def mapFromIterable[K2, V2](it: Iterable[(K2, V2)]^): CC[K2, V2]^{it} = mapFactory.from(it) /** The companion object of this map, providing various factory methods. * @@ -200,7 +244,10 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] /** A generic trait that is reused by keyset implementations */ protected trait GenKeySet { this: Set[K] => - def iterator: Iterator[K] = MapOps.this.keysIterator + def iterator: Iterator[K] = + // CC note: this is unavoidable to make the KeySet pure. + // If you need a generic, capturing KeySet, create a View from keysIterator + MapOps.this.keysIterator.toSet.iterator def contains(key: K): Boolean = MapOps.this.contains(key) override def size: Int = MapOps.this.size override def knownSize: Int = MapOps.this.knownSize @@ -218,22 +265,22 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] * @return an [[Iterable]] collection of the keys contained by this map */ @deprecatedOverriding("This method should be an alias for keySet", since="2.13.13") - def keys: Iterable[K] = keySet + def keys: Iterable[K]^{this} = keySet /** Collects all values of this map in an iterable collection. * * @return the values of this map as an iterable. */ - def values: Iterable[V] = new AbstractIterable[V] with DefaultSerializable { + def values: Iterable[V]^{this} = new AbstractIterable[V] with DefaultSerializable { override def knownSize: Int = MapOps.this.knownSize - override def iterator: Iterator[V] = valuesIterator + override def iterator: Iterator[V]^{self} = valuesIterator } /** An [[Iterator]] of the keys contained by this map. * * @return an [[Iterator]] of the keys contained by this map */ - def keysIterator: Iterator[K] = new AbstractIterator[K] { + def keysIterator: Iterator[K]^{this} = new AbstractIterator[K] { val iter = MapOps.this.iterator def hasNext = iter.hasNext def next() = iter.next()._1 @@ -243,7 +290,7 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] * * @return an iterator over all values that are associated with some key in this map. */ - def valuesIterator: Iterator[V] = new AbstractIterator[V] { + def valuesIterator: Iterator[V]^{this} = new AbstractIterator[V] { val iter = MapOps.this.iterator def hasNext = iter.hasNext def next() = iter.next()._2 @@ -266,7 +313,7 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] * the predicate `p`. The resulting map wraps the original map without copying any elements. */ @deprecated("Use .view.filterKeys(f). A future version will include a strict version of this method (for now, .view.filterKeys(p).toMap).", "2.13.0") - def filterKeys(p: K => Boolean): MapView[K, V] = new MapView.FilterKeys(this, p) + def filterKeys(p: K => Boolean): MapView[K, V]^{this, p} = new MapView.FilterKeys(this, p) /** Transforms this map by applying a function to every retrieved value. * @param f the function used to transform values of this map. @@ -274,7 +321,7 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] * to `f(this(key))`. The resulting map wraps the original map without copying any elements. */ @deprecated("Use .view.mapValues(f). A future version will include a strict version of this method (for now, .view.mapValues(f).toMap).", "2.13.0") - def mapValues[W](f: V => W): MapView[K, W] = new MapView.MapValues(this, f) + def mapValues[W](f: V => W): MapView[K, W]^{this, f} = new MapView.MapValues(this, f) /** Defines the default value computation for the map, * returned when a key is not found. @@ -312,7 +359,7 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] * @return a new $coll resulting from applying the given function * `f` to each element of this $coll and collecting the results. */ - def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = mapFactory.from(new View.Map(this, f)) + def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2]^{this, f} = mapFactory.from(new View.Map(this, f)) /** Builds a new collection by applying a partial function to all elements of this $coll * on which the function is defined. @@ -324,7 +371,7 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] * `pf` to each element on which it is defined and collecting the results. * The order of the elements is preserved. */ - def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]): CC[K2, V2] = + def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]^): CC[K2, V2]^{this, pf} = mapFactory.from(new View.Collect(this, pf)) /** Builds a new map by applying a function to all elements of this $coll @@ -334,7 +381,7 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] * @return a new $coll resulting from applying the given collection-valued function * `f` to each element of this $coll and concatenating the results. */ - def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC[K2, V2] = mapFactory.from(new View.FlatMap(this, f)) + def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]^): CC[K2, V2]^{this, f} = mapFactory.from(new View.FlatMap(this, f)) /** Returns a new $coll containing the elements from the left hand operand followed by the elements from the * right hand operand. The element type of the $coll is the most specific superclass encompassing @@ -344,7 +391,7 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] * @return a new $coll which contains all elements * of this $coll followed by all elements of `suffix`. */ - def concat[V2 >: V](suffix: collection.IterableOnce[(K, V2)]): CC[K, V2] = mapFactory.from(suffix match { + def concat[V2 >: V](suffix: collection.IterableOnce[(K, V2)]^): CC[K, V2]^{this, suffix} = mapFactory.from(suffix match { case it: Iterable[(K, V2)] => new View.Concat(this, it) case _ => iterator.concat(suffix.iterator) }) @@ -352,28 +399,28 @@ transparent trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C] // Not final because subclasses refine the result type, e.g. in SortedMap, the result type is // SortedMap's CC, while Map's CC is fixed to Map /** Alias for `concat` */ - /*@`inline` final*/ def ++ [V2 >: V](xs: collection.IterableOnce[(K, V2)]): CC[K, V2] = concat(xs) + /*@`inline` final*/ def ++ [V2 >: V](xs: collection.IterableOnce[(K, V2)]^): CC[K, V2]^{this, xs} = concat(xs) override def addString(sb: StringBuilder, start: String, sep: String, end: String): sb.type = iterator.map { case (k, v) => s"$k -> $v" }.addString(sb, start, sep, end) @deprecated("Consider requiring an immutable Map or fall back to Map.concat.", "2.13.0") - def + [V1 >: V](kv: (K, V1)): CC[K, V1] = + def + [V1 >: V](kv: (K, V1)): CC[K, V1]^{this} = mapFactory.from(new View.Appended(this, kv)) @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") - def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): CC[K, V1] = + def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): CC[K, V1]^{this} = mapFactory.from(new View.Concat(new View.Appended(new View.Appended(this, elem1), elem2), elems)) @deprecated("Consider requiring an immutable Map.", "2.13.0") - @`inline` def -- (keys: IterableOnce[K]): C = { + @`inline` def -- (keys: IterableOnce[K]^): C^{this, keys} = { lazy val keysSet = keys.iterator.to(immutable.Set) fromSpecific(this.view.filterKeys(k => !keysSet.contains(k))) } @deprecated("Use ++ instead of ++: for collections of type Iterable", "2.13.0") - def ++: [V1 >: V](that: IterableOnce[(K,V1)]): CC[K,V1] = { - val thatIterable: Iterable[(K, V1)] = that match { + def ++: [V1 >: V](that: IterableOnce[(K,V1)]^): CC[K,V1]^{this, that} = { + val thatIterable: Iterable[(K, V1)]^{that} = that match { case that: Iterable[(K, V1)] => that case that => View.from(that) } @@ -389,17 +436,17 @@ object MapOps { */ @SerialVersionUID(3L) class WithFilter[K, +V, +IterableCC[_], +CC[_, _] <: IterableOps[_, AnyConstr, _]]( - self: MapOps[K, V, CC, _] with IterableOps[(K, V), IterableCC, _], + self: (MapOps[K, V, CC, _] with IterableOps[(K, V), IterableCC, _])^, p: ((K, V)) => Boolean ) extends IterableOps.WithFilter[(K, V), IterableCC](self, p) with Serializable { - def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = + def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2]^{this, f} = self.mapFactory.from(new View.Map(filtered, f)) - def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC[K2, V2] = + def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]^): CC[K2, V2]^{this, f} = self.mapFactory.from(new View.FlatMap(filtered, f)) - override def withFilter(q: ((K, V)) => Boolean): WithFilter[K, V, IterableCC, CC] = + override def withFilter(q: ((K, V)) => Boolean): WithFilter[K, V, IterableCC, CC]^{this, q} = new WithFilter[K, V, IterableCC, CC](self, (kv: (K, V)) => p(kv) && q(kv)) } diff --git a/library/src/scala/collection/MapView.scala b/library/src/scala/collection/MapView.scala index a058f3f79514..bfc3906760dd 100644 --- a/library/src/scala/collection/MapView.scala +++ b/library/src/scala/collection/MapView.scala @@ -13,6 +13,8 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.nowarn import scala.collection.MapView.SomeMapOps import scala.collection.mutable.Builder @@ -21,7 +23,7 @@ trait MapView[K, +V] extends MapOps[K, V, ({ type l[X, Y] = View[(X, Y)] })#l, View[(K, V)]] with View[(K, V)] { - override def view: MapView[K, V] = this + override def view: MapView[K, V]^{this} = this // Ideally this returns a `View`, but bincompat /** Creates a view over all keys of this map. @@ -29,42 +31,42 @@ trait MapView[K, +V] * @return the keys of this map as a view. */ @nowarn("msg=overriding method keys") - override def keys: Iterable[K] = new MapView.Keys(this) + override def keys: Iterable[K]^{this} = new MapView.Keys(this) // Ideally this returns a `View`, but bincompat /** Creates a view over all values of this map. * * @return the values of this map as a view. */ - override def values: Iterable[V] = new MapView.Values(this) + override def values: Iterable[V]^{this} = new MapView.Values(this) /** Filters this map by retaining only keys satisfying a predicate. * @param p the predicate used to test keys * @return an immutable map consisting only of those key value pairs of this map where the key satisfies * the predicate `p`. The resulting map wraps the original map without copying any elements. */ - override def filterKeys(p: K => Boolean): MapView[K, V] = new MapView.FilterKeys(this, p) + override def filterKeys(p: K => Boolean): MapView[K, V]^{this, p} = new MapView.FilterKeys(this, p) /** Transforms this map by applying a function to every retrieved value. * @param f the function used to transform values of this map. * @return a map view which maps every key of this map * to `f(this(key))`. The resulting map wraps the original map without copying any elements. */ - override def mapValues[W](f: V => W): MapView[K, W] = new MapView.MapValues(this, f) + override def mapValues[W](f: V => W): MapView[K, W]^{this, f} = new MapView.MapValues(this, f) - override def filter(pred: ((K, V)) => Boolean): MapView[K, V] = new MapView.Filter(this, isFlipped = false, pred) + override def filter(pred: ((K, V)) => Boolean): MapView[K, V]^{this, pred} = new MapView.Filter(this, isFlipped = false, pred) - override def filterNot(pred: ((K, V)) => Boolean): MapView[K, V] = new MapView.Filter(this, isFlipped = true, pred) + override def filterNot(pred: ((K, V)) => Boolean): MapView[K, V]^{this, pred} = new MapView.Filter(this, isFlipped = true, pred) - override def partition(p: ((K, V)) => Boolean): (MapView[K, V], MapView[K, V]) = (filter(p), filterNot(p)) + override def partition(p: ((K, V)) => Boolean): (MapView[K, V]^{this, p}, MapView[K, V]^{this, p}) = (filter(p), filterNot(p)) - override def tapEach[U](f: ((K, V)) => U): MapView[K, V] = new MapView.TapEach(this, f) + override def tapEach[U](f: ((K, V)) => U): MapView[K, V]^{this, f} = new MapView.TapEach(this, f) def mapFactory: MapViewFactory = MapView override def empty: MapView[K, V] = mapFactory.empty - override def withFilter(p: ((K, V)) => Boolean): MapOps.WithFilter[K, V, View, ({ type l[X, Y] = View[(X, Y)] })#l] = new MapOps.WithFilter(this, p) + override def withFilter(p: ((K, V)) => Boolean): MapOps.WithFilter[K, V, View, ({ type l[X, Y] = View[(X, Y)] })#l]^{this, p} = new MapOps.WithFilter(this, p) override def toString: String = super[View].toString @@ -86,55 +88,55 @@ object MapView extends MapViewFactory { override def knownSize: Int = 0 override def isEmpty: Boolean = true override def filterKeys(p: Any => Boolean): MapView[Any, Nothing] = this - override def mapValues[W](f: Nothing => W): MapView[Any, Nothing] = this + override def mapValues[W](f: Nothing => W): MapView[Any, W] = this override def filter(pred: ((Any, Nothing)) => Boolean): MapView[Any, Nothing] = this override def filterNot(pred: ((Any, Nothing)) => Boolean): MapView[Any, Nothing] = this override def partition(p: ((Any, Nothing)) => Boolean): (MapView[Any, Nothing], MapView[Any, Nothing]) = (this, this) } @SerialVersionUID(3L) - class Id[K, +V](underlying: SomeMapOps[K, V]) extends AbstractMapView[K, V] { + class Id[K, +V](underlying: SomeMapOps[K, V]^) extends AbstractMapView[K, V] { def get(key: K): Option[V] = underlying.get(key) - def iterator: Iterator[(K, V)] = underlying.iterator + def iterator: Iterator[(K, V)]^{this} = underlying.iterator override def knownSize: Int = underlying.knownSize override def isEmpty: Boolean = underlying.isEmpty } // Ideally this is public, but bincompat @SerialVersionUID(3L) - private class Keys[K](underlying: SomeMapOps[K, _]) extends AbstractView[K] { - def iterator: Iterator[K] = underlying.keysIterator + private class Keys[K](underlying: SomeMapOps[K, _]^) extends AbstractView[K] { + def iterator: Iterator[K]^{this} = underlying.keysIterator override def knownSize: Int = underlying.knownSize override def isEmpty: Boolean = underlying.isEmpty } // Ideally this is public, but bincompat @SerialVersionUID(3L) - private class Values[+V](underlying: SomeMapOps[_, V]) extends AbstractView[V] { - def iterator: Iterator[V] = underlying.valuesIterator + private class Values[+V](underlying: SomeMapOps[_, V]^) extends AbstractView[V] { + def iterator: Iterator[V]^{this} = underlying.valuesIterator override def knownSize: Int = underlying.knownSize override def isEmpty: Boolean = underlying.isEmpty } @SerialVersionUID(3L) - class MapValues[K, +V, +W](underlying: SomeMapOps[K, V], f: V => W) extends AbstractMapView[K, W] { - def iterator: Iterator[(K, W)] = underlying.iterator.map(kv => (kv._1, f(kv._2))) + class MapValues[K, +V, +W](underlying: SomeMapOps[K, V]^, f: V => W) extends AbstractMapView[K, W] { + def iterator: Iterator[(K, W)]^{this} = underlying.iterator.map(kv => (kv._1, f(kv._2))) def get(key: K): Option[W] = underlying.get(key).map(f) override def knownSize: Int = underlying.knownSize override def isEmpty: Boolean = underlying.isEmpty } @SerialVersionUID(3L) - class FilterKeys[K, +V](underlying: SomeMapOps[K, V], p: K => Boolean) extends AbstractMapView[K, V] { - def iterator: Iterator[(K, V)] = underlying.iterator.filter { case (k, _) => p(k) } + class FilterKeys[K, +V](underlying: SomeMapOps[K, V]^, p: K => Boolean) extends AbstractMapView[K, V] { + def iterator: Iterator[(K, V)]^{this} = underlying.iterator.filter { case (k, _) => p(k) } def get(key: K): Option[V] = if (p(key)) underlying.get(key) else None override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize override def isEmpty: Boolean = iterator.isEmpty } @SerialVersionUID(3L) - class Filter[K, +V](underlying: SomeMapOps[K, V], isFlipped: Boolean, p: ((K, V)) => Boolean) extends AbstractMapView[K, V] { - def iterator: Iterator[(K, V)] = underlying.iterator.filterImpl(p, isFlipped) + class Filter[K, +V](underlying: SomeMapOps[K, V]^, isFlipped: Boolean, p: ((K, V)) => Boolean) extends AbstractMapView[K, V] { + def iterator: Iterator[(K, V)]^{this} = underlying.iterator.filterImpl(p, isFlipped) def get(key: K): Option[V] = underlying.get(key) match { case s @ Some(v) if p((key, v)) != isFlipped => s case _ => None @@ -144,7 +146,7 @@ object MapView extends MapViewFactory { } @SerialVersionUID(3L) - class TapEach[K, +V, +U](underlying: SomeMapOps[K, V], f: ((K, V)) => U) extends AbstractMapView[K, V] { + class TapEach[K, +V, +U](underlying: SomeMapOps[K, V]^, f: ((K, V)) => U) extends AbstractMapView[K, V] { override def get(key: K): Option[V] = { underlying.get(key) match { case s @ Some(v) => @@ -153,7 +155,7 @@ object MapView extends MapViewFactory { case None => None } } - override def iterator: Iterator[(K, V)] = underlying.iterator.tapEach(f) + override def iterator: Iterator[(K, V)]^{this} = underlying.iterator.tapEach(f) override def knownSize: Int = underlying.knownSize override def isEmpty: Boolean = underlying.isEmpty } @@ -162,7 +164,7 @@ object MapView extends MapViewFactory { override def empty[K, V]: MapView[K, V] = EmptyMapView.asInstanceOf[MapView[K, V]] - override def from[K, V](it: IterableOnce[(K, V)]): View[(K, V)] = View.from(it) + override def from[K, V](it: IterableOnce[(K, V)]^): View[(K, V)]^{it} = View.from(it) override def from[K, V](it: SomeMapOps[K, V]): MapView[K, V] = it match { case mv: MapView[K, V] => mv diff --git a/library/src/scala/collection/Searching.scala b/library/src/scala/collection/Searching.scala index 7148f54606bf..cd5842f81057 100644 --- a/library/src/scala/collection/Searching.scala +++ b/library/src/scala/collection/Searching.scala @@ -14,6 +14,8 @@ package scala.collection import scala.language.`2.13` import scala.language.implicitConversions +import language.experimental.captureChecking + import scala.collection.generic.IsSeq object Searching { diff --git a/library/src/scala/collection/Seq.scala b/library/src/scala/collection/Seq.scala index 2f960a47f54f..05d65442ef6d 100644 --- a/library/src/scala/collection/Seq.scala +++ b/library/src/scala/collection/Seq.scala @@ -13,6 +13,8 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.immutable.Range import scala.util.hashing.MurmurHash3 import Searching.{Found, InsertionPoint, SearchResult} @@ -25,9 +27,10 @@ import scala.annotation.nowarn trait Seq[+A] extends Iterable[A] with PartialFunction[Int, A] - with SeqOps[A, Seq, Seq[A]] + with StrictSeqOps[A, Seq, Seq[A]] with IterableFactoryDefaults[A, Seq] - with Equals { + with Equals + with caps.Pure { override def iterableFactory: SeqFactory[Seq] = Seq @@ -55,6 +58,32 @@ trait Seq[+A] @SerialVersionUID(3L) object Seq extends SeqFactory.Delegate[Seq](immutable.Seq) +/** Base trait for strict Seq operations. + * + * Contains strict overrides of SeqOps operations, where the parameters are also captured. + */ +transparent trait StrictSeqOps[+A, +CC[B] <: caps.Pure, +C] extends Any with SeqOps[A, CC, C] with caps.Pure { + override def iterableFactory: StrictIterableFactory[CC] + + override def prependedAll[B >: A](prefix: IterableOnce[B]^): CC[B] = iterableFactory.from(prefix match { + case prefix: Iterable[B] => new View.Concat(prefix, this) + case _ => prefix.iterator ++ iterator + }) + + @inline override final def ++: [B >: A](prefix: IterableOnce[B]^): CC[B] = prependedAll(prefix) + + override def appendedAll[B >: A](suffix: IterableOnce[B]^): CC[B] = super.concat(suffix) + + @inline override final def :++ [B >: A](suffix: IterableOnce[B]^): CC[B] = appendedAll(suffix) + + @inline override final def concat[B >: A](suffix: IterableOnce[B]^): CC[B] = appendedAll(suffix) + + override def reverseMap[B](f: A => B): CC[B] = iterableFactory.from(new View.Map(View.fromIteratorProvider(() => reverseIterator), f)) + + override def patch[B >: A](from: Int, other: IterableOnce[B]^, replaced: Int): CC[B] = + iterableFactory.from(new View.Patched(this, from, other, replaced)) +} + /** Base trait for Seq operations * * @tparam A the element type of the collection @@ -76,9 +105,9 @@ object Seq extends SeqFactory.Delegate[Seq](immutable.Seq) * @define Coll `Seq` */ transparent trait SeqOps[+A, +CC[_], +C] extends Any - with IterableOps[A, CC, C] { self => + with IterableOps[A, CC, C] { self: SeqOps[A, CC, C]^ => - override def view: SeqView[A] = new SeqView.Id[A](this) + override def view: SeqView[A]^{this} = new SeqView.Id[A](this) /** Gets the element at the specified index. This operation is provided for convenience in `Seq`. It should * not be assumed to be efficient unless you have an `IndexedSeq`. */ @@ -110,14 +139,14 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * @return a new $coll consisting of `value` followed * by all elements of this $coll. */ - def prepended[B >: A](elem: B): CC[B] = iterableFactory.from(new View.Prepended(elem, this)) + def prepended[B >: A](elem: B): CC[B]^{this} = iterableFactory.from(new View.Prepended(elem, this)) /** Alias for `prepended`. * * Note that :-ending operators are right associative (see example). * A mnemonic for `+:` vs. `:+` is: the COLon goes on the COLlection side. */ - @`inline` final def +: [B >: A](elem: B): CC[B] = prepended(elem) + @`inline` final def +: [B >: A](elem: B): CC[B]^{this} = prepended(elem) /** A copy of this $coll with an element appended. * @@ -140,14 +169,14 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * @return a new $coll consisting of * all elements of this $coll followed by `value`. */ - def appended[B >: A](elem: B): CC[B] = iterableFactory.from(new View.Appended(this, elem)) + def appended[B >: A](elem: B): CC[B]^{this} = iterableFactory.from(new View.Appended(this, elem)) /** Alias for `appended`. * * Note that :-ending operators are right associative (see example). * A mnemonic for `+:` vs. `:+` is: the COLon goes on the COLlection side. */ - @`inline` final def :+ [B >: A](elem: B): CC[B] = appended(elem) + @`inline` final def :+ [B >: A](elem: B): CC[B]^{this} = appended(elem) /** As with `:++`, returns a new collection containing the elements from the left operand followed by the * elements from the right operand. @@ -161,13 +190,13 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * @return a new $coll which contains all elements of `prefix` followed * by all the elements of this $coll. */ - def prependedAll[B >: A](prefix: IterableOnce[B]): CC[B] = iterableFactory.from(prefix match { + def prependedAll[B >: A](prefix: IterableOnce[B]^): CC[B]^{this, prefix} = iterableFactory.from(prefix match { case prefix: Iterable[B] => new View.Concat(prefix, this) case _ => prefix.iterator ++ iterator }) /** Alias for `prependedAll`. */ - @`inline` override final def ++: [B >: A](prefix: IterableOnce[B]): CC[B] = prependedAll(prefix) + @inline override def ++: [B >: A](prefix: IterableOnce[B]^): CC[B]^{this, prefix} = prependedAll(prefix) /** Returns a new $coll containing the elements from the left hand operand followed by the elements from the * right hand operand. The element type of the $coll is the most specific superclass encompassing @@ -178,14 +207,14 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * @return a new collection of type `CC[B]` which contains all elements * of this $coll followed by all elements of `suffix`. */ - def appendedAll[B >: A](suffix: IterableOnce[B]): CC[B] = super.concat(suffix) + def appendedAll[B >: A](suffix: IterableOnce[B]^): CC[B]^{this, suffix} = super.concat(suffix) /** Alias for `appendedAll`. */ - @inline final def :++ [B >: A](suffix: IterableOnce[B]): CC[B] = appendedAll(suffix) + @inline def :++ [B >: A](suffix: IterableOnce[B]^): CC[B]^{this, suffix} = appendedAll(suffix) // Make `concat` an alias for `appendedAll` so that it benefits from performance // overrides of this method - @inline final override def concat[B >: A](suffix: IterableOnce[B]): CC[B] = appendedAll(suffix) + @inline override def concat[B >: A](suffix: IterableOnce[B]^): CC[B]^{this, suffix} = appendedAll(suffix) /** Produces a new sequence which contains all elements of this $coll and also all elements of * a given sequence. `xs union ys` is equivalent to `xs ++ ys`. @@ -196,7 +225,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * followed by all elements of `that`. */ @deprecated("Use `concat` instead", "2.13.0") - @inline final def union[B >: A](that: Seq[B]): CC[B] = concat(that) + @inline final def union[B >: A](that: Seq[B]): CC[B]^{this} = concat(that) final override def size: Int = length @@ -204,7 +233,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * * @return a new $coll consisting of all the elements of this $coll without duplicates. */ - def distinct: C = distinctBy(identity) + def distinct: C^{this} = distinctBy(identity) /** Selects all the elements of this $coll ignoring the duplicates as determined by `==` after applying * the transforming function `f`. @@ -213,7 +242,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * @tparam B the type of the elements after being transformed by `f` * @return a new $coll consisting of all the elements of this $coll without duplicates. */ - def distinctBy[B](f: A => B): C = fromSpecific(new View.DistinctBy(this, f)) + def distinctBy[B](f: A -> B): C^{this} = fromSpecific(new View.DistinctBy(this, f)) /** Returns a new $coll with the elements of this $coll in reverse order. * @@ -222,7 +251,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * * @return a new $coll with all elements of this $coll in reverse order. */ - def reverse: C = fromSpecific(reversed) + def reverse: C^{this} = fromSpecific(reversed) /** An iterator yielding the elements of this $coll in reverse order. * @@ -232,7 +261,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * * @return an iterator yielding the elements of this $coll in reverse order. */ - def reverseIterator: Iterator[A] = reversed.iterator + def reverseIterator: Iterator[A]^{this} = reversed.iterator /** Tests whether this $coll contains the given sequence at a given index. * @@ -244,7 +273,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * @return `true` if the sequence `that` is contained in this $coll at * index `offset`, otherwise `false`. */ - def startsWith[B >: A](that: IterableOnce[B], offset: Int = 0): Boolean = { + def startsWith[B >: A](that: IterableOnce[B]^, offset: Int = 0): Boolean = { val i = iterator drop offset val j = that.iterator while (j.hasNext && i.hasNext) @@ -259,7 +288,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * @param that the sequence to test * @return `true` if this $coll has `that` as a suffix, `false` otherwise. */ - def endsWith[B >: A](that: Iterable[B]): Boolean = { + def endsWith[B >: A](that: Iterable[B]^): Boolean = { if (that.isEmpty) true else { val i = iterator.drop(length - that.size) @@ -291,7 +320,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * all elements of this $coll followed by the minimal number of occurrences of `elem` so * that the resulting collection has a length of at least `len`. */ - def padTo[B >: A](len: Int, elem: B): CC[B] = iterableFactory.from(new View.PadTo(this, len, elem)) + def padTo[B >: A](len: Int, elem: B): CC[B]^{this} = iterableFactory.from(new View.PadTo(this, len, elem)) /** Computes the length of the longest segment that starts from the first element * and whose elements all satisfy some predicate. @@ -527,7 +556,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any def contains[A1 >: A](elem: A1): Boolean = exists (_ == elem) @deprecated("Use .reverseIterator.map(f).to(...) instead of .reverseMap(f)", "2.13.0") - def reverseMap[B](f: A => B): CC[B] = iterableFactory.from(new View.Map(View.fromIteratorProvider(() => reverseIterator), f)) + def reverseMap[B](f: A => B): CC[B]^{this, f} = iterableFactory.from(new View.Map(View.fromIteratorProvider(() => reverseIterator), f)) /** Iterates over distinct permutations of elements. * @@ -541,7 +570,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * // List(b, b, a) * }}} */ - def permutations: Iterator[C] = + def permutations: Iterator[C^{this}]^{this} = if (isEmpty) Iterator.single(coll) else new PermutationsItr @@ -582,7 +611,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * // List(b, a) * }}} */ - def combinations(n: Int): Iterator[C] = + def combinations(n: Int): Iterator[C^{this}]^{this} = if (n < 0 || n > size) Iterator.empty else new CombinationsItr(n) @@ -717,7 +746,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * @return a $coll consisting of the elements of this $coll * sorted according to the ordering `ord`. */ - def sorted[B >: A](implicit ord: Ordering[B]): C = { + def sorted[B >: A](implicit ord: Ordering[B]): C^{this} = { val len = this.length val b = newSpecificBuilder if (len == 1) b += head @@ -754,7 +783,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * List("Bobby", "Bob", "John", "Steve", "Tom") * }}} */ - def sortWith(lt: (A, A) => Boolean): C = sorted(Ordering.fromLessThan(lt)) + def sortWith(lt: (A, A) => Boolean): C^{this} = sorted(Ordering.fromLessThan(lt)) /** Sorts this $coll according to the Ordering which results from transforming * an implicitly given Ordering with a transformation function. @@ -781,7 +810,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * res0: Array[String] = Array(The, dog, fox, the, lazy, over, brown, quick, jumped) * }}} */ - def sortBy[B](f: A => B)(implicit ord: Ordering[B]): C = sorted(ord on f) + def sortBy[B](f: A => B)(implicit ord: Ordering[B]): C^{this} = sorted(ord on f) /** Produces the range of all indices of this sequence. * $willForceEvaluation @@ -809,7 +838,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any */ def lengthCompare(len: Int): Int = super.sizeCompare(len) - override final def sizeCompare(that: Iterable[_]): Int = lengthCompare(that) + override final def sizeCompare(that: Iterable[_]^): Int = lengthCompare(that) /** Compares the length of this $coll to the size of another `Iterable`. * @@ -824,7 +853,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * is `O(this.length min that.size)` instead of `O(this.length + that.size)`. * The method should be overridden if computing `size` is cheap and `knownSize` returns `-1`. */ - def lengthCompare(that: Iterable[_]): Int = super.sizeCompare(that) + def lengthCompare(that: Iterable[_]^): Int = super.sizeCompare(that) /** Returns a value class containing operations for comparing the length of this $coll to a test value. * @@ -840,7 +869,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * this.lengthIs > len // this.lengthCompare(len) > 0 * }}} */ - @inline final def lengthIs: IterableOps.SizeCompareOps = new IterableOps.SizeCompareOps(this) + @inline final def lengthIs: IterableOps.SizeCompareOps^{this} = new IterableOps.SizeCompareOps(caps.unsafe.unsafeAssumePure(this) /* see comment in SizeCompareOps*/) override def isEmpty: Boolean = lengthCompare(0) == 0 @@ -851,7 +880,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * @tparam B the type of the elements of collection `that`. * @return `true` if both collections contain equal elements in the same order, `false` otherwise. */ - def sameElements[B >: A](that: IterableOnce[B]): Boolean = { + def sameElements[B >: A](that: IterableOnce[B]^): Boolean = { val thisKnownSize = knownSize if (thisKnownSize != -1) { val thatKnownSize = that.knownSize @@ -891,7 +920,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * ''n'' times in `that`, then the first ''n'' occurrences of `x` will not form * part of the result, but any following occurrences will. */ - def diff[B >: A](that: Seq[B]): C = { + def diff[B >: A](that: Seq[B]): C^{this} = { val occ = occCounts(that) fromSpecific(iterator.filter { x => var include = false @@ -916,7 +945,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained * in the result, but any following occurrences will be omitted. */ - def intersect[B >: A](that: Seq[B]): C = { + def intersect[B >: A](that: Seq[B]): C^{this} = { val occ = occCounts(that) fromSpecific(iterator.filter { x => var include = true @@ -946,7 +975,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * except that `replaced` elements starting from `from` are replaced * by all the elements of `other`. */ - def patch[B >: A](from: Int, other: IterableOnce[B], replaced: Int): CC[B] = + def patch[B >: A](from: Int, other: IterableOnce[B]^, replaced: Int): CC[B]^{this, other} = iterableFactory.from(new View.Patched(this, from, other, replaced)) /** A copy of this $coll with one single replaced element. @@ -958,7 +987,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any * lazy collection this exception may be thrown at a later time or not at * all (if the end of the collection is never evaluated). */ - def updated[B >: A](index: Int, elem: B): CC[B] = { + def updated[B >: A](index: Int, elem: B): CC[B]^{this} = { if(index < 0) throw new IndexOutOfBoundsException(index.toString) val k = knownSize if(k >= 0 && index >= k) throw new IndexOutOfBoundsException(index.toString) @@ -1020,7 +1049,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any def search[B >: A](elem: B, from: Int, to: Int) (implicit ord: Ordering[B]): SearchResult = linearSearch(view.slice(from, to), elem, math.max(0, from))(ord) - private[this] def linearSearch[B >: A](c: View[A], elem: B, offset: Int) + private[this] def linearSearch[B >: A](c: View[A]^, elem: B, offset: Int) (implicit ord: Ordering[B]): SearchResult = { var idx = offset val it = c.iterator @@ -1170,7 +1199,7 @@ object SeqOps { * @param wlen Just in case we're only IndexedSeq and not IndexedSeqOptimized * @return KMP jump table for target sequence */ - private def kmpJumpTable[B](Wopt: IndexedSeqView[B], wlen: Int) = { + private def kmpJumpTable[B](Wopt: IndexedSeqView[B]^, wlen: Int) = { val arr = new Array[Int](wlen) var pos = 2 var cnd = 0 diff --git a/library/src/scala/collection/SeqMap.scala b/library/src/scala/collection/SeqMap.scala index 17d187975af2..27e5ddf00a68 100644 --- a/library/src/scala/collection/SeqMap.scala +++ b/library/src/scala/collection/SeqMap.scala @@ -13,6 +13,8 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.nowarn /** @@ -29,12 +31,12 @@ import scala.annotation.nowarn */ trait SeqMap[K, +V] extends Map[K, V] - with MapOps[K, V, SeqMap, SeqMap[K, V]] + with StrictMapOps[K, V, SeqMap, SeqMap[K, V]] with MapFactoryDefaults[K, V, SeqMap, Iterable] { @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") override protected[this] def stringPrefix: String = "SeqMap" - override def mapFactory: MapFactory[SeqMap] = SeqMap + override def mapFactory: StrictMapFactory[SeqMap] = SeqMap } object SeqMap extends MapFactory.Delegate[immutable.SeqMap](immutable.SeqMap) diff --git a/library/src/scala/collection/SeqView.scala b/library/src/scala/collection/SeqView.scala index 3f6a4c5c3ea4..960de6f1d752 100644 --- a/library/src/scala/collection/SeqView.scala +++ b/library/src/scala/collection/SeqView.scala @@ -14,28 +14,30 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.nowarn import scala.collection.generic.CommonErrors trait SeqView[+A] extends SeqOps[A, View, View[A]] with View[A] { - override def view: SeqView[A] = this + override def view: SeqView[A]^{this} = this - override def map[B](f: A => B): SeqView[B] = new SeqView.Map(this, f) - override def appended[B >: A](elem: B): SeqView[B] = new SeqView.Appended(this, elem) - override def prepended[B >: A](elem: B): SeqView[B] = new SeqView.Prepended(elem, this) - override def reverse: SeqView[A] = new SeqView.Reverse(this) - override def take(n: Int): SeqView[A] = new SeqView.Take(this, n) - override def drop(n: Int): SeqView[A] = new SeqView.Drop(this, n) - override def takeRight(n: Int): SeqView[A] = new SeqView.TakeRight(this, n) - override def dropRight(n: Int): SeqView[A] = new SeqView.DropRight(this, n) - override def tapEach[U](f: A => U): SeqView[A] = new SeqView.Map(this, { (a: A) => f(a); a }) + override def map[B](f: A => B): SeqView[B]^{this, f} = new SeqView.Map(this, f) + override def appended[B >: A](elem: B): SeqView[B]^{this} = new SeqView.Appended(this, elem) + override def prepended[B >: A](elem: B): SeqView[B]^{this} = new SeqView.Prepended(elem, this) + override def reverse: SeqView[A]^{this} = new SeqView.Reverse(this) + override def take(n: Int): SeqView[A]^{this} = new SeqView.Take(this, n) + override def drop(n: Int): SeqView[A]^{this} = new SeqView.Drop(this, n) + override def takeRight(n: Int): SeqView[A]^{this} = new SeqView.TakeRight(this, n) + override def dropRight(n: Int): SeqView[A]^{this} = new SeqView.DropRight(this, n) + override def tapEach[U](f: A => U): SeqView[A]^{this, f} = new SeqView.Map(this, { (a: A) => f(a); a }) - def concat[B >: A](suffix: SeqView.SomeSeqOps[B]): SeqView[B] = new SeqView.Concat(this, suffix) - def appendedAll[B >: A](suffix: SeqView.SomeSeqOps[B]): SeqView[B] = new SeqView.Concat(this, suffix) - def prependedAll[B >: A](prefix: SeqView.SomeSeqOps[B]): SeqView[B] = new SeqView.Concat(prefix, this) + def concat[B >: A](suffix: SeqView.SomeSeqOps[B]^): SeqView[B]^{this, suffix} = new SeqView.Concat(this, suffix) + def appendedAll[B >: A](suffix: SeqView.SomeSeqOps[B]^): SeqView[B]^{this, suffix} = new SeqView.Concat(this, suffix) + def prependedAll[B >: A](prefix: SeqView.SomeSeqOps[B]^): SeqView[B]^{this, prefix} = new SeqView.Concat(prefix, this) - override def sorted[B >: A](implicit ord: Ordering[B]): SeqView[A] = new SeqView.Sorted(this, ord) + override def sorted[B >: A](implicit ord: Ordering[B]): SeqView[A]^{this} = new SeqView.Sorted(this, ord) @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") override protected[this] def stringPrefix: String = "SeqView" @@ -48,34 +50,34 @@ object SeqView { /** A view that doesn’t apply any transformation to an underlying sequence */ @SerialVersionUID(3L) - class Id[+A](underlying: SomeSeqOps[A]) extends AbstractSeqView[A] { + class Id[+A](underlying: SomeSeqOps[A]^) extends AbstractSeqView[A] { def apply(idx: Int): A = underlying.apply(idx) def length: Int = underlying.length - def iterator: Iterator[A] = underlying.iterator + def iterator: Iterator[A]^{this} = underlying.iterator override def knownSize: Int = underlying.knownSize override def isEmpty: Boolean = underlying.isEmpty } @SerialVersionUID(3L) - class Map[+A, +B](underlying: SomeSeqOps[A], f: A => B) extends View.Map[A, B](underlying, f) with SeqView[B] { + class Map[+A, +B](underlying: SomeSeqOps[A]^, f: A => B) extends View.Map[A, B](underlying, f) with SeqView[B] { def apply(idx: Int): B = f(underlying(idx)) def length: Int = underlying.length } @SerialVersionUID(3L) - class Appended[+A](underlying: SomeSeqOps[A], elem: A) extends View.Appended(underlying, elem) with SeqView[A] { + class Appended[+A](underlying: SomeSeqOps[A]^, elem: A) extends View.Appended(underlying, elem) with SeqView[A] { def apply(idx: Int): A = if (idx == underlying.length) elem else underlying(idx) def length: Int = underlying.length + 1 } @SerialVersionUID(3L) - class Prepended[+A](elem: A, underlying: SomeSeqOps[A]) extends View.Prepended(elem, underlying) with SeqView[A] { + class Prepended[+A](elem: A, underlying: SomeSeqOps[A]^) extends View.Prepended(elem, underlying) with SeqView[A] { def apply(idx: Int): A = if (idx == 0) elem else underlying(idx - 1) def length: Int = underlying.length + 1 } @SerialVersionUID(3L) - class Concat[A](prefix: SomeSeqOps[A], suffix: SomeSeqOps[A]) extends View.Concat[A](prefix, suffix) with SeqView[A] { + class Concat[A](prefix: SomeSeqOps[A]^, suffix: SomeSeqOps[A]^) extends View.Concat[A](prefix, suffix) with SeqView[A] { def apply(idx: Int): A = { val l = prefix.length if (idx < l) prefix(idx) else suffix(idx - l) @@ -84,16 +86,16 @@ object SeqView { } @SerialVersionUID(3L) - class Reverse[A](underlying: SomeSeqOps[A]) extends AbstractSeqView[A] { + class Reverse[A](underlying: SomeSeqOps[A]^) extends AbstractSeqView[A] { def apply(i: Int) = underlying.apply(size - 1 - i) def length = underlying.size - def iterator: Iterator[A] = underlying.reverseIterator + def iterator: Iterator[A]^{this} = underlying.reverseIterator override def knownSize: Int = underlying.knownSize override def isEmpty: Boolean = underlying.isEmpty } @SerialVersionUID(3L) - class Take[+A](underlying: SomeSeqOps[A], n: Int) extends View.Take(underlying, n) with SeqView[A] { + class Take[+A](underlying: SomeSeqOps[A]^, n: Int) extends View.Take(underlying, n) with SeqView[A] { def apply(idx: Int): A = if (idx < n) { underlying(idx) } else { @@ -106,7 +108,7 @@ object SeqView { } @SerialVersionUID(3L) - class TakeRight[+A](underlying: SomeSeqOps[A], n: Int) extends View.TakeRight(underlying, n) with SeqView[A] { + class TakeRight[+A](underlying: SomeSeqOps[A]^, n: Int) extends View.TakeRight(underlying, n) with SeqView[A] { private[this] val delta = (underlying.size - (n max 0)) max 0 def length = underlying.size - delta @throws[IndexOutOfBoundsException] @@ -114,15 +116,15 @@ object SeqView { } @SerialVersionUID(3L) - class Drop[A](underlying: SomeSeqOps[A], n: Int) extends View.Drop[A](underlying, n) with SeqView[A] { + class Drop[A](underlying: SomeSeqOps[A]^, n: Int) extends View.Drop[A](underlying, n) with SeqView[A] { def length = (underlying.size - normN) max 0 @throws[IndexOutOfBoundsException] def apply(i: Int) = underlying.apply(i + normN) - override def drop(n: Int): SeqView[A] = new Drop(underlying, this.n + n) + override def drop(n: Int): SeqView[A]^{this} = new Drop(underlying, this.n + n) } @SerialVersionUID(3L) - class DropRight[A](underlying: SomeSeqOps[A], n: Int) extends View.DropRight[A](underlying, n) with SeqView[A] { + class DropRight[A](underlying: SomeSeqOps[A]^, n: Int) extends View.DropRight[A](underlying, n) with SeqView[A] { private[this] val len = (underlying.size - (n max 0)) max 0 def length = len @throws[IndexOutOfBoundsException] @@ -130,15 +132,17 @@ object SeqView { } @SerialVersionUID(3L) - class Sorted[A, B >: A] private (private[this] var underlying: SomeSeqOps[A], + class Sorted[A, B >: A] private (underlying_ : SomeSeqOps[A]^, private[this] val len: Int, ord: Ordering[B]) extends SeqView[A] { - outer => + outer: Sorted[A, B]^ => + + private var underlying = underlying_ // force evaluation immediately by calling `length` so infinite collections // hang on `sorted`/`sortWith`/`sortBy` rather than on arbitrary method calls - def this(underlying: SomeSeqOps[A], ord: Ordering[B]) = this(underlying, underlying.length, ord) + def this(underlying: SomeSeqOps[A]^, ord: Ordering[B]) = this(underlying, underlying.length, ord) @SerialVersionUID(3L) private[this] class ReverseSorted extends SeqView[A] { @@ -150,10 +154,10 @@ object SeqView { override def knownSize: Int = len override def isEmpty: Boolean = len == 0 override def to[C1](factory: Factory[A, C1]): C1 = _reversed.to(factory) - override def reverse: SeqView[A] = outer - override protected def reversed: Iterable[A] = outer + override def reverse: SeqView[A]^{this} = outer + override protected def reversed: Iterable[A]^{outer} = outer - override def sorted[B1 >: A](implicit ord1: Ordering[B1]): SeqView[A] = + override def sorted[B1 >: A](implicit ord1: Ordering[B1]): SeqView[A]^{this} = if (ord1 == Sorted.this.ord) outer else if (ord1.isReverseOf(Sorted.this.ord)) this else new Sorted(elems, len, ord1) @@ -187,14 +191,14 @@ object SeqView { res } - private[this] def elems: SomeSeqOps[A] = { - val orig = underlying + private[this] def elems: SomeSeqOps[A]^{this} = { + val orig: SomeSeqOps[A]^{this} = underlying if (evaluated) _sorted else orig } def apply(i: Int): A = _sorted.apply(i) def length: Int = len - def iterator: Iterator[A] = Iterator.empty ++ _sorted.iterator // very lazy + def iterator: Iterator[A]^{this} = Iterator.empty ++ _sorted.iterator // very lazy override def knownSize: Int = len override def isEmpty: Boolean = len == 0 override def to[C1](factory: Factory[A, C1]): C1 = _sorted.to(factory) @@ -203,7 +207,7 @@ object SeqView { // so this is acceptable for `reversed` override protected def reversed: Iterable[A] = new ReverseSorted - override def sorted[B1 >: A](implicit ord1: Ordering[B1]): SeqView[A] = + override def sorted[B1 >: A](implicit ord1: Ordering[B1]): SeqView[A]^{this} = if (ord1 == this.ord) this else if (ord1.isReverseOf(this.ord)) reverse else new Sorted(elems, len, ord1) diff --git a/library/src/scala/collection/Set.scala b/library/src/scala/collection/Set.scala index 78bf6351c84c..c5f879dc1644 100644 --- a/library/src/scala/collection/Set.scala +++ b/library/src/scala/collection/Set.scala @@ -14,6 +14,8 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.util.hashing.MurmurHash3 import java.lang.String @@ -25,7 +27,8 @@ trait Set[A] extends Iterable[A] with SetOps[A, Set, Set[A]] with Equals - with IterableFactoryDefaults[A, Set] { + with IterableFactoryDefaults[A, Set] + with caps.Pure { def canEqual(that: Any) = true @@ -88,7 +91,8 @@ trait Set[A] */ transparent trait SetOps[A, +CC[_], +C <: SetOps[A, CC, C]] extends IterableOps[A, CC, C] - with (A => Boolean) { + with (A => Boolean) + with caps.Pure { def contains(elem: A): Boolean @@ -200,7 +204,7 @@ transparent trait SetOps[A, +CC[_], +C <: SetOps[A, CC, C]] @`inline` final def &~ (that: Set[A]): C = this diff that @deprecated("Consider requiring an immutable Set", "2.13.0") - def -- (that: IterableOnce[A]): C = { + def -- (that: IterableOnce[A]^): C = { val toRemove = that.iterator.to(immutable.Set) fromSpecific(view.filterNot(toRemove)) } @@ -222,7 +226,7 @@ transparent trait SetOps[A, +CC[_], +C <: SetOps[A, CC, C]] * @param that the collection containing the elements to add. * @return a new $coll with the given elements added, omitting duplicates. */ - def concat(that: collection.IterableOnce[A]): C = this match { + def concat(that: collection.IterableOnce[A]^): C = this match { case optimizedSet @ (_ : scala.collection.immutable.Set.Set1[A] | _: scala.collection.immutable.Set.Set2[A] | _: scala.collection.immutable.Set.Set3[A] | _: scala.collection.immutable.Set.Set4[A]) => // StrictOptimizedSetOps optimization of concat (these Sets cannot extend StrictOptimizedSetOps because of binary-incompatible return type; cf. PR #10036) var result = optimizedSet.asInstanceOf[scala.collection.immutable.SetOps[A, scala.collection.immutable.Set, scala.collection.immutable.Set[A]]] @@ -242,7 +246,7 @@ transparent trait SetOps[A, +CC[_], +C <: SetOps[A, CC, C]] def + (elem1: A, elem2: A, elems: A*): C = fromSpecific(new View.Concat(new View.Appended(new View.Appended(this, elem1), elem2), elems)) /** Alias for `concat` */ - @`inline` final def ++ (that: collection.IterableOnce[A]): C = concat(that) + @`inline` final def ++ (that: collection.IterableOnce[A]^): C = concat(that) /** Computes the union between of set and another set. * diff --git a/library/src/scala/collection/SortedMap.scala b/library/src/scala/collection/SortedMap.scala index d4f1aae2b62e..827ec8a19ea1 100644 --- a/library/src/scala/collection/SortedMap.scala +++ b/library/src/scala/collection/SortedMap.scala @@ -14,6 +14,8 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.{implicitNotFound, nowarn} /** A Map whose keys are sorted according to a [[scala.math.Ordering]]*/ @@ -48,8 +50,8 @@ trait SortedMap[K, +V] } } -transparent trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMapOps[K, V, CC, C]] - extends MapOps[K, V, Map, C] +transparent trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _] with caps.Pure, +C <: SortedMapOps[K, V, CC, C]] + extends StrictMapOps[K, V, Map, C] with SortedOps[K, C] { /** The companion object of this sorted map, providing various factory methods. @@ -63,7 +65,7 @@ transparent trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X /** Similar to `mapFromIterable`, but returns a SortedMap collection type. * Note that the return type is now `CC[K2, V2]`. */ - @`inline` protected final def sortedMapFromIterable[K2, V2](it: Iterable[(K2, V2)])(implicit ordering: Ordering[K2]): CC[K2, V2] = sortedMapFactory.from(it) + @`inline` protected final def sortedMapFromIterable[K2, V2](it: Iterable[(K2, V2)]^)(implicit ordering: Ordering[K2]): CC[K2, V2] = sortedMapFactory.from(it) def unsorted: Map[K, V] @@ -163,7 +165,7 @@ transparent trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X * @return a new $coll resulting from applying the given collection-valued function * `f` to each element of this $coll and concatenating the results. */ - def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)])(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = + def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]^)(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = sortedMapFactory.from(new View.FlatMap(this, f)) /** Builds a new sorted map by applying a partial function to all elements of this $coll @@ -174,16 +176,16 @@ transparent trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X * `pf` to each element on which it is defined and collecting the results. * The order of the elements is preserved. */ - def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)])(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = + def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]^)(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = sortedMapFactory.from(new View.Collect(this, pf)) - override def concat[V2 >: V](suffix: IterableOnce[(K, V2)]): CC[K, V2] = sortedMapFactory.from(suffix match { + override def concat[V2 >: V](suffix: IterableOnce[(K, V2)]^): CC[K, V2] = sortedMapFactory.from(suffix match { case it: Iterable[(K, V2)] => new View.Concat(this, it) case _ => iterator.concat(suffix.iterator) })(using ordering) /** Alias for `concat` */ - @`inline` override final def ++ [V2 >: V](xs: IterableOnce[(K, V2)]): CC[K, V2] = concat(xs) + @`inline` override final def ++ [V2 >: V](xs: IterableOnce[(K, V2)]^): CC[K, V2] = concat(xs) @deprecated("Consider requiring an immutable Map or fall back to Map.concat", "2.13.0") override def + [V1 >: V](kv: (K, V1)): CC[K, V1] = sortedMapFactory.from(new View.Appended(this, kv))(using ordering) @@ -207,10 +209,10 @@ object SortedMapOps { def map[K2 : Ordering, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = self.sortedMapFactory.from(new View.Map(filtered, f)) - def flatMap[K2 : Ordering, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC[K2, V2] = + def flatMap[K2 : Ordering, V2](f: ((K, V)) => IterableOnce[(K2, V2)]^): CC[K2, V2] = self.sortedMapFactory.from(new View.FlatMap(filtered, f)) - override def withFilter(q: ((K, V)) => Boolean): WithFilter[K, V, IterableCC, MapCC, CC] = + override def withFilter(q: ((K, V)) => Boolean): WithFilter[K, V, IterableCC, MapCC, CC]^{this, q} = new WithFilter[K, V, IterableCC, MapCC, CC](self, (kv: (K, V)) => p(kv) && q(kv)) } diff --git a/library/src/scala/collection/SortedOps.scala b/library/src/scala/collection/SortedOps.scala index 4fefcc4038d0..b995ef9e1911 100644 --- a/library/src/scala/collection/SortedOps.scala +++ b/library/src/scala/collection/SortedOps.scala @@ -13,6 +13,7 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking /** Base trait for sorted collections */ transparent trait SortedOps[A, +C] { diff --git a/library/src/scala/collection/SortedSet.scala b/library/src/scala/collection/SortedSet.scala index 9290087cf06b..ed3cfaee73da 100644 --- a/library/src/scala/collection/SortedSet.scala +++ b/library/src/scala/collection/SortedSet.scala @@ -13,6 +13,8 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.{implicitNotFound, nowarn} import scala.annotation.unchecked.uncheckedVariance @@ -130,7 +132,7 @@ transparent trait SortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSetOps[A, * @return a new $coll resulting from applying the given collection-valued function * `f` to each element of this $coll and concatenating the results. */ - def flatMap[B](f: A => IterableOnce[B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = + def flatMap[B](f: A => IterableOnce[B]^)(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = sortedIterableFactory.from(new View.FlatMap(this, f)) /** Returns a $coll formed from this $coll and another iterable collection @@ -142,7 +144,7 @@ transparent trait SortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSetOps[A, * @return a new $coll containing pairs consisting of corresponding elements of this $coll and `that`. * The length of the returned collection is the minimum of the lengths of this $coll and `that`. */ - def zip[B](that: IterableOnce[B])(implicit @implicitNotFound(SortedSetOps.zipOrdMsg) ev: Ordering[(A @uncheckedVariance, B)]): CC[(A @uncheckedVariance, B)] = // sound bcs of VarianceNote + def zip[B](that: IterableOnce[B]^)(implicit @implicitNotFound(SortedSetOps.zipOrdMsg) ev: Ordering[(A @uncheckedVariance, B)]): CC[(A @uncheckedVariance, B)] = // sound bcs of VarianceNote sortedIterableFactory.from(that match { case that: Iterable[B] => new View.Zip(this, that) case _ => iterator.zip(that) @@ -157,7 +159,7 @@ transparent trait SortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSetOps[A, * `pf` to each element on which it is defined and collecting the results. * The order of the elements is preserved. */ - def collect[B](pf: scala.PartialFunction[A, B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = + def collect[B](pf: scala.PartialFunction[A, B]^)(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = sortedIterableFactory.from(new View.Collect(this, pf)) } @@ -177,10 +179,10 @@ object SortedSetOps { def map[B : Ordering](f: A => B): CC[B] = self.sortedIterableFactory.from(new View.Map(filtered, f)) - def flatMap[B : Ordering](f: A => IterableOnce[B]): CC[B] = + def flatMap[B : Ordering](f: A => IterableOnce[B]^): CC[B] = self.sortedIterableFactory.from(new View.FlatMap(filtered, f)) - override def withFilter(q: A => Boolean): WithFilter[A, IterableCC, CC] = + override def withFilter(q: A => Boolean): WithFilter[A, IterableCC, CC]^{this, q} = new WithFilter[A, IterableCC, CC](self, (a: A) => p(a) && q(a)) } diff --git a/library/src/scala/collection/Stepper.scala b/library/src/scala/collection/Stepper.scala index 8755142d7209..7c4b5821aef9 100644 --- a/library/src/scala/collection/Stepper.scala +++ b/library/src/scala/collection/Stepper.scala @@ -13,6 +13,8 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking + import java.util.function.{Consumer, DoubleConsumer, IntConsumer, LongConsumer} import java.util.{PrimitiveIterator, Spliterator, Iterator => JIterator} import java.{lang => jl} @@ -52,7 +54,7 @@ trait Stepper[@specialized(Double, Int, Long) +A] { * * See method `trySplit` in [[java.util.Spliterator]]. */ - def trySplit(): Stepper[A] + def trySplit(): Stepper[A]^{this} /** Returns an estimate of the number of elements of this Stepper, or [[Long.MaxValue]]. See * method `estimateSize` in [[java.util.Spliterator]]. @@ -70,7 +72,7 @@ trait Stepper[@specialized(Double, Int, Long) +A] { * a [[java.util.Spliterator.OfInt]] (which is a `Spliterator[Integer]`) in the subclass [[IntStepper]] * (which is a `Stepper[Int]`). */ - def spliterator[B >: A]: Spliterator[_] + def spliterator[B >: A]: Spliterator[_]^{this} /** Returns a Java [[java.util.Iterator]] corresponding to this Stepper. * @@ -78,12 +80,12 @@ trait Stepper[@specialized(Double, Int, Long) +A] { * a [[java.util.PrimitiveIterator.OfInt]] (which is a `Iterator[Integer]`) in the subclass * [[IntStepper]] (which is a `Stepper[Int]`). */ - def javaIterator[B >: A]: JIterator[_] + def javaIterator[B >: A]: JIterator[_]^{this} /** Returns an [[Iterator]] corresponding to this Stepper. Note that Iterators corresponding to * primitive Steppers box the elements. */ - def iterator: Iterator[A] = new AbstractIterator[A] { + def iterator: Iterator[A]^{this} = new AbstractIterator[A] { def hasNext: Boolean = hasStep def next(): A = nextStep() } @@ -104,78 +106,78 @@ object Stepper { * of the data is boxed. In other cases native implementations of the primitive stepper types should be provided * (see for example IntArrayStepper and WidenedByteArrayStepper). */ - private[collection] class UnboxingDoubleStepper(st: AnyStepper[Double]) extends DoubleStepper { + private[collection] class UnboxingDoubleStepper(st: AnyStepper[Double]^) extends DoubleStepper { def hasStep: Boolean = st.hasStep def nextStep(): Double = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): DoubleStepper = { + def trySplit(): DoubleStepper^{this} = { val s = st.trySplit() if (s == null) null else new UnboxingDoubleStepper(s) } } - private[collection] class UnboxingIntStepper(st: AnyStepper[Int]) extends IntStepper { + private[collection] class UnboxingIntStepper(st: AnyStepper[Int]^) extends IntStepper { def hasStep: Boolean = st.hasStep def nextStep(): Int = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): IntStepper = { + def trySplit(): IntStepper^{this} = { val s = st.trySplit() if (s == null) null else new UnboxingIntStepper(s) } } - private[collection] class UnboxingLongStepper(st: AnyStepper[Long]) extends LongStepper { + private[collection] class UnboxingLongStepper(st: AnyStepper[Long]^) extends LongStepper { def hasStep: Boolean = st.hasStep def nextStep(): Long = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): LongStepper = { + def trySplit(): LongStepper^{this} = { val s = st.trySplit() if (s == null) null else new UnboxingLongStepper(s) } } - private[collection] class UnboxingByteStepper(st: AnyStepper[Byte]) extends IntStepper { + private[collection] class UnboxingByteStepper(st: AnyStepper[Byte]^) extends IntStepper { def hasStep: Boolean = st.hasStep def nextStep(): Int = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): IntStepper = { + def trySplit(): IntStepper^{this} = { val s = st.trySplit() if (s == null) null else new UnboxingByteStepper(s) } } - private[collection] class UnboxingCharStepper(st: AnyStepper[Char]) extends IntStepper { + private[collection] class UnboxingCharStepper(st: AnyStepper[Char]^) extends IntStepper { def hasStep: Boolean = st.hasStep def nextStep(): Int = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): IntStepper = { + def trySplit(): IntStepper^{this} = { val s = st.trySplit() if (s == null) null else new UnboxingCharStepper(s) } } - private[collection] class UnboxingShortStepper(st: AnyStepper[Short]) extends IntStepper { + private[collection] class UnboxingShortStepper(st: AnyStepper[Short]^) extends IntStepper { def hasStep: Boolean = st.hasStep def nextStep(): Int = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): IntStepper = { + def trySplit(): IntStepper^{this} = { val s = st.trySplit() if (s == null) null else new UnboxingShortStepper(s) } } - private[collection] class UnboxingFloatStepper(st: AnyStepper[Float]) extends DoubleStepper { + private[collection] class UnboxingFloatStepper(st: AnyStepper[Float]^) extends DoubleStepper { def hasStep: Boolean = st.hasStep def nextStep(): Double = st.nextStep() def estimateSize: Long = st.estimateSize def characteristics: Int = st.characteristics - def trySplit(): DoubleStepper = { + def trySplit(): DoubleStepper^{this} = { val s = st.trySplit() if (s == null) null else new UnboxingFloatStepper(s) } @@ -184,21 +186,21 @@ object Stepper { /** A Stepper for arbitrary element types. See [[Stepper]]. */ trait AnyStepper[+A] extends Stepper[A] { - def trySplit(): AnyStepper[A] + def trySplit(): AnyStepper[A]^{this} - def spliterator[B >: A]: Spliterator[B] = new AnyStepper.AnyStepperSpliterator(this) + def spliterator[B >: A]: Spliterator[B]^{this} = new AnyStepper.AnyStepperSpliterator(this) - def javaIterator[B >: A]: JIterator[B] = new JIterator[B] { + def javaIterator[B >: A]: JIterator[B]^{this} = new JIterator[B] { def hasNext: Boolean = hasStep def next(): B = nextStep() } } object AnyStepper { - class AnyStepperSpliterator[A](s: AnyStepper[A]) extends Spliterator[A] { + class AnyStepperSpliterator[A](s: AnyStepper[A]^) extends Spliterator[A] { def tryAdvance(c: Consumer[_ >: A]): Boolean = if (s.hasStep) { c.accept(s.nextStep()); true } else false - def trySplit(): Spliterator[A] = { + def trySplit(): Spliterator[A]^{this} = { val sp = s.trySplit() if (sp == null) null else sp.spliterator } @@ -254,9 +256,9 @@ object AnyStepper { /** A Stepper for Ints. See [[Stepper]]. */ trait IntStepper extends Stepper[Int] { - def trySplit(): IntStepper + def trySplit(): IntStepper^{this} - def spliterator[B >: Int]: Spliterator.OfInt = new IntStepper.IntStepperSpliterator(this) + def spliterator[B >: Int]: Spliterator.OfInt^{this} = new IntStepper.IntStepperSpliterator(this) def javaIterator[B >: Int]: PrimitiveIterator.OfInt = new PrimitiveIterator.OfInt { def hasNext: Boolean = hasStep @@ -264,7 +266,7 @@ trait IntStepper extends Stepper[Int] { } } object IntStepper { - class IntStepperSpliterator(s: IntStepper) extends Spliterator.OfInt { + class IntStepperSpliterator(s: IntStepper^) extends Spliterator.OfInt { def tryAdvance(c: IntConsumer): Boolean = if (s.hasStep) { c.accept(s.nextStep()); true } else false // Override for efficiency: don't wrap the function and call the `tryAdvance` overload @@ -273,7 +275,7 @@ object IntStepper { case _ => if (s.hasStep) { c.accept(jl.Integer.valueOf(s.nextStep())); true } else false } // override required for dotty#6152 - override def trySplit(): Spliterator.OfInt = { + override def trySplit(): Spliterator.OfInt^{this} = { val sp = s.trySplit() if (sp == null) null else sp.spliterator } @@ -292,9 +294,9 @@ object IntStepper { /** A Stepper for Doubles. See [[Stepper]]. */ trait DoubleStepper extends Stepper[Double] { - def trySplit(): DoubleStepper + def trySplit(): DoubleStepper^{this} - def spliterator[B >: Double]: Spliterator.OfDouble = new DoubleStepper.DoubleStepperSpliterator(this) + def spliterator[B >: Double]: Spliterator.OfDouble^{this} = new DoubleStepper.DoubleStepperSpliterator(this) def javaIterator[B >: Double]: PrimitiveIterator.OfDouble = new PrimitiveIterator.OfDouble { def hasNext: Boolean = hasStep @@ -303,7 +305,7 @@ trait DoubleStepper extends Stepper[Double] { } object DoubleStepper { - class DoubleStepperSpliterator(s: DoubleStepper) extends Spliterator.OfDouble { + class DoubleStepperSpliterator(s: DoubleStepper^) extends Spliterator.OfDouble { def tryAdvance(c: DoubleConsumer): Boolean = if (s.hasStep) { c.accept(s.nextStep()); true } else false // Override for efficiency: don't wrap the function and call the `tryAdvance` overload @@ -312,7 +314,7 @@ object DoubleStepper { case _ => if (s.hasStep) { c.accept(java.lang.Double.valueOf(s.nextStep())); true } else false } // override required for dotty#6152 - override def trySplit(): Spliterator.OfDouble = { + override def trySplit(): Spliterator.OfDouble^{this} = { val sp = s.trySplit() if (sp == null) null else sp.spliterator } @@ -331,9 +333,9 @@ object DoubleStepper { /** A Stepper for Longs. See [[Stepper]]. */ trait LongStepper extends Stepper[Long] { - def trySplit(): LongStepper + def trySplit(): LongStepper^{this} - def spliterator[B >: Long]: Spliterator.OfLong = new LongStepper.LongStepperSpliterator(this) + def spliterator[B >: Long]: Spliterator.OfLong^{this} = new LongStepper.LongStepperSpliterator(this) def javaIterator[B >: Long]: PrimitiveIterator.OfLong = new PrimitiveIterator.OfLong { def hasNext: Boolean = hasStep @@ -342,7 +344,7 @@ trait LongStepper extends Stepper[Long] { } object LongStepper { - class LongStepperSpliterator(s: LongStepper) extends Spliterator.OfLong { + class LongStepperSpliterator(s: LongStepper^) extends Spliterator.OfLong { def tryAdvance(c: LongConsumer): Boolean = if (s.hasStep) { c.accept(s.nextStep()); true } else false // Override for efficiency: don't wrap the function and call the `tryAdvance` overload @@ -351,7 +353,7 @@ object LongStepper { case _ => if (s.hasStep) { c.accept(java.lang.Long.valueOf(s.nextStep())); true } else false } // override required for dotty#6152 - override def trySplit(): Spliterator.OfLong = { + override def trySplit(): Spliterator.OfLong^{this} = { val sp = s.trySplit() if (sp == null) null else sp.spliterator } diff --git a/library/src/scala/collection/StepperShape.scala b/library/src/scala/collection/StepperShape.scala index ec193cc27fc2..1b25d8758da0 100644 --- a/library/src/scala/collection/StepperShape.scala +++ b/library/src/scala/collection/StepperShape.scala @@ -15,22 +15,24 @@ package scala.collection import java.{lang => jl} import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.Stepper.EfficientSplit /** An implicit StepperShape instance is used in the [[IterableOnce.stepper]] to return a possibly * specialized Stepper `S` according to the element type `T`. */ -sealed trait StepperShape[-T, S <: Stepper[_]] { +sealed trait StepperShape[-T, S <: Stepper[_]] { self => /** Return the Int constant (as defined in the `StepperShape` companion object) for this `StepperShape`. */ def shape: StepperShape.Shape /** Create an unboxing primitive sequential Stepper from a boxed `AnyStepper`. * This is an identity operation for reference shapes. */ - def seqUnbox(st: AnyStepper[T]): S + def seqUnbox(st: AnyStepper[T]^): S^{st} /** Create an unboxing primitive parallel (i.e. `with EfficientSplit`) Stepper from a boxed `AnyStepper`. * This is an identity operation for reference shapes. */ - def parUnbox(st: AnyStepper[T] with EfficientSplit): S with EfficientSplit + def parUnbox(st: (AnyStepper[T] with EfficientSplit)^): (S with EfficientSplit)^{st} } object StepperShape extends StepperShapeLowPriority1 { @@ -52,50 +54,50 @@ object StepperShape extends StepperShapeLowPriority1 { implicit val intStepperShape: StepperShape[Int, IntStepper] = new StepperShape[Int, IntStepper] { def shape = IntShape - def seqUnbox(st: AnyStepper[Int]): IntStepper = new Stepper.UnboxingIntStepper(st) - def parUnbox(st: AnyStepper[Int] with EfficientSplit): IntStepper with EfficientSplit = new Stepper.UnboxingIntStepper(st) with EfficientSplit + def seqUnbox(st: AnyStepper[Int]^): IntStepper^{st} = new Stepper.UnboxingIntStepper(st) + def parUnbox(st: (AnyStepper[Int] with EfficientSplit)^): (IntStepper with EfficientSplit)^{st} = new Stepper.UnboxingIntStepper(st) with EfficientSplit } implicit val jIntegerStepperShape: StepperShape[jl.Integer, IntStepper] = intStepperShape.asInstanceOf[StepperShape[jl.Integer, IntStepper]] implicit val longStepperShape: StepperShape[Long, LongStepper] = new StepperShape[Long, LongStepper] { def shape = LongShape - def seqUnbox(st: AnyStepper[Long]): LongStepper = new Stepper.UnboxingLongStepper(st) - def parUnbox(st: AnyStepper[Long] with EfficientSplit): LongStepper with EfficientSplit = new Stepper.UnboxingLongStepper(st) with EfficientSplit + def seqUnbox(st: AnyStepper[Long]^): LongStepper^{st} = new Stepper.UnboxingLongStepper(st) + def parUnbox(st: (AnyStepper[Long] with EfficientSplit)^): (LongStepper with EfficientSplit)^{st} = new Stepper.UnboxingLongStepper(st) with EfficientSplit } implicit val jLongStepperShape: StepperShape[jl.Long, LongStepper] = longStepperShape.asInstanceOf[StepperShape[jl.Long, LongStepper]] implicit val doubleStepperShape: StepperShape[Double, DoubleStepper] = new StepperShape[Double, DoubleStepper] { def shape = DoubleShape - def seqUnbox(st: AnyStepper[Double]): DoubleStepper = new Stepper.UnboxingDoubleStepper(st) - def parUnbox(st: AnyStepper[Double] with EfficientSplit): DoubleStepper with EfficientSplit = new Stepper.UnboxingDoubleStepper(st) with EfficientSplit + def seqUnbox(st: AnyStepper[Double]^): DoubleStepper^{st} = new Stepper.UnboxingDoubleStepper(st) + def parUnbox(st: (AnyStepper[Double] with EfficientSplit)^): (DoubleStepper with EfficientSplit)^{st} = new Stepper.UnboxingDoubleStepper(st) with EfficientSplit } implicit val jDoubleStepperShape: StepperShape[jl.Double, DoubleStepper] = doubleStepperShape.asInstanceOf[StepperShape[jl.Double, DoubleStepper]] implicit val byteStepperShape: StepperShape[Byte, IntStepper] = new StepperShape[Byte, IntStepper] { def shape = ByteShape - def seqUnbox(st: AnyStepper[Byte]): IntStepper = new Stepper.UnboxingByteStepper(st) - def parUnbox(st: AnyStepper[Byte] with EfficientSplit): IntStepper with EfficientSplit = new Stepper.UnboxingByteStepper(st) with EfficientSplit + def seqUnbox(st: AnyStepper[Byte]^): IntStepper^{st} = new Stepper.UnboxingByteStepper(st) + def parUnbox(st: (AnyStepper[Byte] with EfficientSplit)^): (IntStepper with EfficientSplit)^{st} = new Stepper.UnboxingByteStepper(st) with EfficientSplit } implicit val jByteStepperShape: StepperShape[jl.Byte, IntStepper] = byteStepperShape.asInstanceOf[StepperShape[jl.Byte, IntStepper]] implicit val shortStepperShape: StepperShape[Short, IntStepper] = new StepperShape[Short, IntStepper] { def shape = ShortShape - def seqUnbox(st: AnyStepper[Short]): IntStepper = new Stepper.UnboxingShortStepper(st) - def parUnbox(st: AnyStepper[Short] with EfficientSplit): IntStepper with EfficientSplit = new Stepper.UnboxingShortStepper(st) with EfficientSplit + def seqUnbox(st: AnyStepper[Short]^): IntStepper^{st} = new Stepper.UnboxingShortStepper(st) + def parUnbox(st: (AnyStepper[Short] with EfficientSplit)^): (IntStepper with EfficientSplit)^{st} = new Stepper.UnboxingShortStepper(st) with EfficientSplit } implicit val jShortStepperShape: StepperShape[jl.Short, IntStepper] = shortStepperShape.asInstanceOf[StepperShape[jl.Short, IntStepper]] implicit val charStepperShape: StepperShape[Char, IntStepper] = new StepperShape[Char, IntStepper] { def shape = CharShape - def seqUnbox(st: AnyStepper[Char]): IntStepper = new Stepper.UnboxingCharStepper(st) - def parUnbox(st: AnyStepper[Char] with EfficientSplit): IntStepper with EfficientSplit = new Stepper.UnboxingCharStepper(st) with EfficientSplit + def seqUnbox(st: AnyStepper[Char]^): IntStepper^{st} = new Stepper.UnboxingCharStepper(st) + def parUnbox(st: (AnyStepper[Char] with EfficientSplit)^): (IntStepper with EfficientSplit)^{st} = new Stepper.UnboxingCharStepper(st) with EfficientSplit } implicit val jCharacterStepperShape: StepperShape[jl.Character, IntStepper] = charStepperShape.asInstanceOf[StepperShape[jl.Character, IntStepper]] implicit val floatStepperShape: StepperShape[Float, DoubleStepper] = new StepperShape[Float, DoubleStepper] { def shape = FloatShape - def seqUnbox(st: AnyStepper[Float]): DoubleStepper = new Stepper.UnboxingFloatStepper(st) - def parUnbox(st: AnyStepper[Float] with EfficientSplit): DoubleStepper with EfficientSplit = new Stepper.UnboxingFloatStepper(st) with EfficientSplit + def seqUnbox(st: AnyStepper[Float]^): DoubleStepper^{st} = new Stepper.UnboxingFloatStepper(st) + def parUnbox(st: (AnyStepper[Float] with EfficientSplit)^): (DoubleStepper with EfficientSplit)^{st} = new Stepper.UnboxingFloatStepper(st) with EfficientSplit } implicit val jFloatStepperShape: StepperShape[jl.Float, DoubleStepper] = floatStepperShape.asInstanceOf[StepperShape[jl.Float, DoubleStepper]] } @@ -109,7 +111,7 @@ trait StepperShapeLowPriority2 { protected val anyStepperShapePrototype: StepperShape[AnyRef, Stepper[AnyRef]] = new StepperShape[AnyRef, Stepper[AnyRef]] { def shape = StepperShape.ReferenceShape - def seqUnbox(st: AnyStepper[AnyRef]): Stepper[AnyRef] = st - def parUnbox(st: AnyStepper[AnyRef] with EfficientSplit): Stepper[AnyRef] with EfficientSplit = st + def seqUnbox(st: AnyStepper[AnyRef]^): Stepper[AnyRef]^{st} = st + def parUnbox(st: (AnyStepper[AnyRef] with EfficientSplit)^): (Stepper[AnyRef] with EfficientSplit)^{st} = st } -} \ No newline at end of file +} diff --git a/library/src/scala/collection/StrictOptimizedIterableOps.scala b/library/src/scala/collection/StrictOptimizedIterableOps.scala index 81c8a79aedf4..b89acb715cf7 100644 --- a/library/src/scala/collection/StrictOptimizedIterableOps.scala +++ b/library/src/scala/collection/StrictOptimizedIterableOps.scala @@ -14,6 +14,8 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.nowarn import scala.annotation.unchecked.uncheckedVariance import scala.runtime.Statics @@ -56,7 +58,7 @@ transparent trait StrictOptimizedIterableOps[+A, +CC[_], +C] (first.result(), second.result()) } - override def unzip[A1, A2](implicit asPair: A => (A1, A2)): (CC[A1], CC[A2]) = { + override def unzip[A1, A2](implicit asPair: A -> (A1, A2)): (CC[A1], CC[A2]) = { val first = iterableFactory.newBuilder[A1] val second = iterableFactory.newBuilder[A2] foreach { a => @@ -67,7 +69,7 @@ transparent trait StrictOptimizedIterableOps[+A, +CC[_], +C] (first.result(), second.result()) } - override def unzip3[A1, A2, A3](implicit asTriple: A => (A1, A2, A3)): (CC[A1], CC[A2], CC[A3]) = { + override def unzip3[A1, A2, A3](implicit asTriple: A -> (A1, A2, A3)): (CC[A1], CC[A2], CC[A3]) = { val b1 = iterableFactory.newBuilder[A1] val b2 = iterableFactory.newBuilder[A2] val b3 = iterableFactory.newBuilder[A3] @@ -103,7 +105,7 @@ transparent trait StrictOptimizedIterableOps[+A, +CC[_], +C] b.result() } - override def flatMap[B](f: A => IterableOnce[B]): CC[B] = + override def flatMap[B](f: A => IterableOnce[B]^): CC[B] = strictOptimizedFlatMap(iterableFactory.newBuilder, f) /** @@ -113,7 +115,7 @@ transparent trait StrictOptimizedIterableOps[+A, +CC[_], +C] * @tparam C2 Type of the resulting collection (e.g. `List[String]`) * @return The resulting collection */ - @inline protected[this] final def strictOptimizedFlatMap[B, C2](b: mutable.Builder[B, C2], f: A => IterableOnce[B]): C2 = { + @inline protected[this] final def strictOptimizedFlatMap[B, C2](b: mutable.Builder[B, C2]^, f: A => IterableOnce[B]^): C2 = { val it = iterator while (it.hasNext) { b ++= f(it.next()) @@ -128,13 +130,13 @@ transparent trait StrictOptimizedIterableOps[+A, +CC[_], +C] * @tparam C2 Type of the resulting collection (e.g. `List[Int]`) * @return The resulting collection */ - @inline protected[this] final def strictOptimizedConcat[B >: A, C2](that: IterableOnce[B], b: mutable.Builder[B, C2]): C2 = { + @inline protected[this] final def strictOptimizedConcat[B >: A, C2](that: IterableOnce[B]^, b: mutable.Builder[B, C2]^): C2 = { b ++= this b ++= that b.result() } - override def collect[B](pf: PartialFunction[A, B]): CC[B] = + override def collect[B](pf: PartialFunction[A, B]^): CC[B] = strictOptimizedCollect(iterableFactory.newBuilder, pf) /** @@ -144,7 +146,7 @@ transparent trait StrictOptimizedIterableOps[+A, +CC[_], +C] * @tparam C2 Type of the resulting collection (e.g. `List[String]`) * @return The resulting collection */ - @inline protected[this] final def strictOptimizedCollect[B, C2](b: mutable.Builder[B, C2], pf: PartialFunction[A, B]): C2 = { + @inline protected[this] final def strictOptimizedCollect[B, C2](b: mutable.Builder[B, C2]^, pf: PartialFunction[A, B]^): C2 = { val marker = Statics.pfMarker val it = iterator while (it.hasNext) { @@ -155,7 +157,7 @@ transparent trait StrictOptimizedIterableOps[+A, +CC[_], +C] b.result() } - override def flatten[B](implicit toIterableOnce: A => IterableOnce[B]): CC[B] = + override def flatten[B](implicit toIterableOnce: A -> IterableOnce[B]): CC[B] = strictOptimizedFlatten(iterableFactory.newBuilder) /** @@ -173,7 +175,7 @@ transparent trait StrictOptimizedIterableOps[+A, +CC[_], +C] b.result() } - override def zip[B](that: IterableOnce[B]): CC[(A @uncheckedVariance, B)] = + override def zip[B](that: IterableOnce[B]^): CC[(A @uncheckedVariance, B)] = strictOptimizedZip(that, iterableFactory.newBuilder[(A, B)]) /** @@ -183,7 +185,7 @@ transparent trait StrictOptimizedIterableOps[+A, +CC[_], +C] * @tparam C2 Type of the resulting collection (e.g. `List[(Int, String)]`) * @return The resulting collection */ - @inline protected[this] final def strictOptimizedZip[B, C2](that: IterableOnce[B], b: mutable.Builder[(A, B), C2]): C2 = { + @inline protected[this] final def strictOptimizedZip[B, C2](that: IterableOnce[B]^, b: mutable.Builder[(A, B), C2]^): C2 = { val it1 = iterator val it2 = that.iterator while (it1.hasNext && it2.hasNext) { @@ -246,7 +248,7 @@ transparent trait StrictOptimizedIterableOps[+A, +CC[_], +C] } // Optimization avoids creation of second collection - override def tapEach[U](f: A => U): C = { + override def tapEach[U](f: A => U): C^{this} = { foreach(f) coll } diff --git a/library/src/scala/collection/StrictOptimizedMapOps.scala b/library/src/scala/collection/StrictOptimizedMapOps.scala index cc594f268c07..b5eaa0d1638d 100644 --- a/library/src/scala/collection/StrictOptimizedMapOps.scala +++ b/library/src/scala/collection/StrictOptimizedMapOps.scala @@ -13,6 +13,7 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking /** * Trait that overrides map operations to take advantage of strict builders. @@ -29,13 +30,13 @@ transparent trait StrictOptimizedMapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyCo override def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = strictOptimizedMap(mapFactory.newBuilder, f) - override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC[K2, V2] = + override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]^): CC[K2, V2] = strictOptimizedFlatMap(mapFactory.newBuilder, f) - override def concat[V2 >: V](suffix: IterableOnce[(K, V2)]): CC[K, V2] = + override def concat[V2 >: V](suffix: IterableOnce[(K, V2)]^): CC[K, V2] = strictOptimizedConcat(suffix, mapFactory.newBuilder) - override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]): CC[K2, V2] = + override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]^): CC[K2, V2] = strictOptimizedCollect(mapFactory.newBuilder, pf) @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") diff --git a/library/src/scala/collection/StrictOptimizedSeqOps.scala b/library/src/scala/collection/StrictOptimizedSeqOps.scala index cf59f7b05b9e..15fa8be919ed 100644 --- a/library/src/scala/collection/StrictOptimizedSeqOps.scala +++ b/library/src/scala/collection/StrictOptimizedSeqOps.scala @@ -13,17 +13,16 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking /** * Trait that overrides operations on sequences in order * to take advantage of strict builders. */ -transparent trait StrictOptimizedSeqOps [+A, +CC[_], +C] - extends Any - with SeqOps[A, CC, C] - with StrictOptimizedIterableOps[A, CC, C] { +transparent trait StrictOptimizedSeqOps [+A, +CC[_] <: caps.Pure, +C] + extends Any with StrictSeqOps[A, CC, C] with StrictOptimizedIterableOps[A, CC, C] { - override def distinctBy[B](f: A => B): C = { + override def distinctBy[B](f: A -> B): C = { val builder = newSpecificBuilder val seen = mutable.HashSet.empty[B] val it = this.iterator @@ -50,10 +49,10 @@ transparent trait StrictOptimizedSeqOps [+A, +CC[_], +C] b.result() } - override def appendedAll[B >: A](suffix: IterableOnce[B]): CC[B] = + override def appendedAll[B >: A](suffix: IterableOnce[B]^): CC[B] = strictOptimizedConcat(suffix, iterableFactory.newBuilder) - override def prependedAll[B >: A](prefix: IterableOnce[B]): CC[B] = { + override def prependedAll[B >: A](prefix: IterableOnce[B]^): CC[B] = { val b = iterableFactory.newBuilder[B] b ++= prefix b ++= this diff --git a/library/src/scala/collection/StrictOptimizedSetOps.scala b/library/src/scala/collection/StrictOptimizedSetOps.scala index cef517c39fd6..5e5349b6cd78 100644 --- a/library/src/scala/collection/StrictOptimizedSetOps.scala +++ b/library/src/scala/collection/StrictOptimizedSetOps.scala @@ -13,6 +13,7 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking /** * Trait that overrides set operations to take advantage of strict builders. @@ -25,7 +26,7 @@ transparent trait StrictOptimizedSetOps[A, +CC[_], +C <: SetOps[A, CC, C]] extends SetOps[A, CC, C] with StrictOptimizedIterableOps[A, CC, C] { - override def concat(that: IterableOnce[A]): C = + override def concat(that: IterableOnce[A]^): C = strictOptimizedConcat(that, newSpecificBuilder) } diff --git a/library/src/scala/collection/StrictOptimizedSortedMapOps.scala b/library/src/scala/collection/StrictOptimizedSortedMapOps.scala index c71ad8d1e447..e9328dfb01e4 100644 --- a/library/src/scala/collection/StrictOptimizedSortedMapOps.scala +++ b/library/src/scala/collection/StrictOptimizedSortedMapOps.scala @@ -13,6 +13,8 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.implicitNotFound /** @@ -30,13 +32,13 @@ transparent trait StrictOptimizedSortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with override def map[K2, V2](f: ((K, V)) => (K2, V2))(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = strictOptimizedMap(sortedMapFactory.newBuilder, f) - override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)])(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = + override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]^)(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = strictOptimizedFlatMap(sortedMapFactory.newBuilder, f) - override def concat[V2 >: V](xs: IterableOnce[(K, V2)]): CC[K, V2] = + override def concat[V2 >: V](xs: IterableOnce[(K, V2)]^): CC[K, V2] = strictOptimizedConcat(xs, sortedMapFactory.newBuilder(using ordering)) - override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)])(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = + override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]^)(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] = strictOptimizedCollect(sortedMapFactory.newBuilder, pf) @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") diff --git a/library/src/scala/collection/StrictOptimizedSortedSetOps.scala b/library/src/scala/collection/StrictOptimizedSortedSetOps.scala index aef85be58bb3..d9c437573ca2 100644 --- a/library/src/scala/collection/StrictOptimizedSortedSetOps.scala +++ b/library/src/scala/collection/StrictOptimizedSortedSetOps.scala @@ -14,6 +14,8 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.implicitNotFound import scala.annotation.unchecked.uncheckedVariance @@ -31,13 +33,13 @@ transparent trait StrictOptimizedSortedSetOps[A, +CC[X] <: SortedSet[X], +C <: S override def map[B](f: A => B)(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = strictOptimizedMap(sortedIterableFactory.newBuilder, f) - override def flatMap[B](f: A => IterableOnce[B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = + override def flatMap[B](f: A => IterableOnce[B]^)(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = strictOptimizedFlatMap(sortedIterableFactory.newBuilder, f) - override def zip[B](that: IterableOnce[B])(implicit @implicitNotFound(SortedSetOps.zipOrdMsg) ev: Ordering[(A @uncheckedVariance, B)]): CC[(A @uncheckedVariance, B)] = + override def zip[B](that: IterableOnce[B]^)(implicit @implicitNotFound(SortedSetOps.zipOrdMsg) ev: Ordering[(A @uncheckedVariance, B)]): CC[(A @uncheckedVariance, B)] = strictOptimizedZip(that, sortedIterableFactory.newBuilder[(A, B)]) - override def collect[B](pf: PartialFunction[A, B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = + override def collect[B](pf: PartialFunction[A, B]^)(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = strictOptimizedCollect(sortedIterableFactory.newBuilder, pf) } diff --git a/library/src/scala/collection/StringOps.scala b/library/src/scala/collection/StringOps.scala index ab5ec5c1ff49..17800cb1d98b 100644 --- a/library/src/scala/collection/StringOps.scala +++ b/library/src/scala/collection/StringOps.scala @@ -14,6 +14,8 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import java.lang.{StringBuilder => JStringBuilder} import scala.collection.Stepper.EfficientSplit @@ -122,7 +124,7 @@ object StringOps { * @return a new collection resulting from applying the given collection-valued function * `f` to each char of this string and concatenating the results. */ - def flatMap[B](f: Char => IterableOnce[B]): immutable.IndexedSeq[B] = { + def flatMap[B](f: Char => IterableOnce[B]^): immutable.IndexedSeq[B] = { val len = s.length val b = immutable.IndexedSeq.newBuilder[B] var i = 0 @@ -154,7 +156,7 @@ object StringOps { } /** Creates a new non-strict filter which combines this filter with the given predicate. */ - def withFilter(q: Char => Boolean): WithFilter = new WithFilter(a => p(a) && q(a), s) + def withFilter(q: Char => Boolean): WithFilter^{this, q} = new WithFilter(a => p(a) && q(a), s) } /** Avoid an allocation in [[collect]]. */ @@ -176,7 +178,7 @@ object StringOps { * are handled correctly. Failing to do so may result in * an invalid Unicode string. */ -final class StringOps(private val s: String) extends AnyVal { +final class StringOps(private val s: String) extends AnyVal { self => import StringOps._ @inline def view: StringView = new StringView(s) @@ -237,7 +239,7 @@ final class StringOps(private val s: String) extends AnyVal { * @return a new collection resulting from applying the given collection-valued function * `f` to each char of this string and concatenating the results. */ - def flatMap[B](f: Char => IterableOnce[B]): immutable.IndexedSeq[B] = { + def flatMap[B](f: Char => IterableOnce[B]^): immutable.IndexedSeq[B] = { val len = s.length val b = immutable.IndexedSeq.newBuilder[B] var i = 0 @@ -273,7 +275,7 @@ final class StringOps(private val s: String) extends AnyVal { * @return a new String resulting from applying the given partial function * `pf` to each char on which it is defined and collecting the results. */ - def collect(pf: PartialFunction[Char, Char]): String = { + def collect(pf: PartialFunction[Char, Char]^): String = { val fallback: Any => Any = StringOps.fallback var i = 0 val b = new StringBuilder @@ -293,7 +295,7 @@ final class StringOps(private val s: String) extends AnyVal { * @return a new collection resulting from applying the given partial function * `pf` to each char on which it is defined and collecting the results. */ - def collect[B](pf: PartialFunction[Char, B]): immutable.IndexedSeq[B] = { + def collect[B](pf: PartialFunction[Char, B]^): immutable.IndexedSeq[B] = { val fallback: Any => Any = StringOps.fallback var i = 0 val b = immutable.IndexedSeq.newBuilder[B] @@ -312,7 +314,7 @@ final class StringOps(private val s: String) extends AnyVal { * @return a new collection which contains all chars * of this string followed by all elements of `suffix`. */ - def concat[B >: Char](suffix: IterableOnce[B]): immutable.IndexedSeq[B] = { + def concat[B >: Char](suffix: IterableOnce[B]^): immutable.IndexedSeq[B] = { val b = immutable.IndexedSeq.newBuilder[B] val k = suffix.knownSize b.sizeHint(s.length + (if(k >= 0) k else 16)) @@ -328,7 +330,7 @@ final class StringOps(private val s: String) extends AnyVal { * @return a new string which contains all chars * of this string followed by all chars of `suffix`. */ - def concat(suffix: IterableOnce[Char]): String = { + def concat(suffix: IterableOnce[Char]^): String = { val k = suffix.knownSize val sb = new JStringBuilder(s.length + (if(k >= 0) k else 16)) sb.append(s) @@ -346,10 +348,10 @@ final class StringOps(private val s: String) extends AnyVal { @inline def concat(suffix: String): String = s + suffix /** Alias for `concat` */ - @inline def ++[B >: Char](suffix: Iterable[B]): immutable.IndexedSeq[B] = concat(suffix) + @inline def ++[B >: Char](suffix: Iterable[B]^): immutable.IndexedSeq[B] = concat(suffix) /** Alias for `concat` */ - @inline def ++(suffix: IterableOnce[Char]): String = concat(suffix) + @inline def ++(suffix: IterableOnce[Char]^): String = concat(suffix) /** Alias for `concat` */ def ++(xs: String): String = concat(xs) @@ -421,7 +423,7 @@ final class StringOps(private val s: String) extends AnyVal { @inline def +: (c: Char): String = prepended(c) /** A copy of the string with all elements from a collection prepended */ - def prependedAll[B >: Char](prefix: IterableOnce[B]): immutable.IndexedSeq[B] = { + def prependedAll[B >: Char](prefix: IterableOnce[B]^): immutable.IndexedSeq[B] = { val b = immutable.IndexedSeq.newBuilder[B] val k = prefix.knownSize b.sizeHint(s.length + (if(k >= 0) k else 16)) @@ -431,7 +433,7 @@ final class StringOps(private val s: String) extends AnyVal { } /** Alias for `prependedAll` */ - @inline def ++: [B >: Char] (prefix: IterableOnce[B]): immutable.IndexedSeq[B] = prependedAll(prefix) + @inline def ++: [B >: Char] (prefix: IterableOnce[B]^): immutable.IndexedSeq[B] = prependedAll(prefix) /** A copy of the string with another string prepended */ def prependedAll(prefix: String): String = prefix + s @@ -459,11 +461,11 @@ final class StringOps(private val s: String) extends AnyVal { @inline def :+ (c: Char): String = appended(c) /** A copy of the string with all elements from a collection appended */ - @inline def appendedAll[B >: Char](suffix: IterableOnce[B]): immutable.IndexedSeq[B] = + @inline def appendedAll[B >: Char](suffix: IterableOnce[B]^): immutable.IndexedSeq[B] = concat(suffix) /** Alias for `appendedAll` */ - @inline def :++ [B >: Char](suffix: IterableOnce[B]): immutable.IndexedSeq[B] = + @inline def :++ [B >: Char](suffix: IterableOnce[B]^): immutable.IndexedSeq[B] = concat(suffix) /** A copy of the string with another string appended */ @@ -485,7 +487,7 @@ final class StringOps(private val s: String) extends AnyVal { * except that `replaced` chars starting from `from` are replaced * by `other`. */ - def patch[B >: Char](from: Int, other: IterableOnce[B], replaced: Int): immutable.IndexedSeq[B] = { + def patch[B >: Char](from: Int, other: IterableOnce[B]^, replaced: Int): immutable.IndexedSeq[B] = { val len = s.length @inline def slc(off: Int, length: Int): WrappedString = new WrappedString(s.substring(off, off+length)) @@ -514,7 +516,7 @@ final class StringOps(private val s: String) extends AnyVal { * by `other`. * @note $unicodeunaware */ - def patch(from: Int, other: IterableOnce[Char], replaced: Int): String = + def patch(from: Int, other: IterableOnce[Char]^, replaced: Int): String = patch(from, other.iterator.mkString, replaced) /** Produces a new string where a slice of characters in this string is replaced by another string. @@ -1196,7 +1198,7 @@ final class StringOps(private val s: String) extends AnyVal { * All these operations apply to those chars of this string * which satisfy the predicate `p`. */ - def withFilter(p: Char => Boolean): StringOps.WithFilter = new StringOps.WithFilter(p, s) + def withFilter(p: Char => Boolean): StringOps.WithFilter^{p} = new StringOps.WithFilter(p, s) /** The rest of the string without its first char. * @throws UnsupportedOperationException if the string is empty. @@ -1249,7 +1251,7 @@ final class StringOps(private val s: String) extends AnyVal { def inits: Iterator[String] = iterateUntilEmpty(_.init) // A helper for tails and inits. - private[this] def iterateUntilEmpty(f: String => String): Iterator[String] = + private[this] def iterateUntilEmpty(f: String => String): Iterator[String]^{f} = Iterator.iterate(s)(f).takeWhile(x => !x.isEmpty) ++ Iterator.single("") /** Selects all chars of this string which satisfy a predicate. */ @@ -1467,7 +1469,7 @@ final class StringOps(private val s: String) extends AnyVal { * @return a decorator `LazyZip2` that allows strict operations to be performed on the lazily evaluated pairs * or chained calls to `lazyZip`. Implicit conversion to `Iterable[(A, B)]` is also supported. */ - def lazyZip[B](that: Iterable[B]): LazyZip2[Char, B, String] = new LazyZip2(s, new WrappedString(s), that) + def lazyZip[B](that: Iterable[B]^): LazyZip2[Char, B, String]^{that} = new LazyZip2(s, new WrappedString(s), that) /* ************************************************************************************************************ @@ -1515,7 +1517,7 @@ final class StringOps(private val s: String) extends AnyVal { * @return a new string consisting of all the chars of this string without duplicates. * @note $unicodeunaware */ - def distinctBy[B](f: Char => B): String = new WrappedString(s).distinctBy(f).unwrap + def distinctBy[B](f: Char -> B): String = new WrappedString(s).distinctBy(f).unwrap /** Sorts the characters of this string according to an Ordering. * diff --git a/library/src/scala/collection/StringParsers.scala b/library/src/scala/collection/StringParsers.scala index cfaa4f86985f..6c4807bf1929 100644 --- a/library/src/scala/collection/StringParsers.scala +++ b/library/src/scala/collection/StringParsers.scala @@ -14,6 +14,8 @@ package scala package collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.tailrec /** A module containing the implementations of parsers from strings to numeric types, and boolean diff --git a/library/src/scala/collection/View.scala b/library/src/scala/collection/View.scala index 690ba49aafe8..6b862aece4ea 100644 --- a/library/src/scala/collection/View.scala +++ b/library/src/scala/collection/View.scala @@ -13,9 +13,12 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.{nowarn, tailrec} import scala.collection.mutable.{ArrayBuffer, Builder} import scala.collection.immutable.LazyList +import caps.unsafe.unsafeAssumePure /** Views are collections whose transformation operations are non strict: the resulting elements * are evaluated only when the view is effectively traversed (e.g. using `foreach` or `foldLeft`), @@ -25,7 +28,7 @@ import scala.collection.immutable.LazyList */ trait View[+A] extends Iterable[A] with IterableOps[A, View, View[A]] with IterableFactoryDefaults[A, View] with Serializable { - override def view: View[A] = this + override def view: View[A]^{this} = this override def iterableFactory: IterableFactory[View] = View @@ -56,7 +59,7 @@ object View extends IterableFactory[View] { * * @tparam A View element type */ - def fromIteratorProvider[A](it: () => Iterator[A]): View[A] = new AbstractView[A] { + def fromIteratorProvider[A](it: () => Iterator[A]^): View[A]^{it} = new AbstractView[A] { def iterator = it() } @@ -68,10 +71,10 @@ object View extends IterableFactory[View] { * * @tparam E View element type */ - def from[E](it: IterableOnce[E]): View[E] = it match { - case it: View[E] => it - case it: Iterable[E] => View.fromIteratorProvider(() => it.iterator) - case _ => LazyList.from(it).view + def from[E](it: IterableOnce[E]^): View[E]^{it} = it match { + case it: (View[E]^{it}) => it + case it: (Iterable[E]^{it}) => View.fromIteratorProvider(() => it.iterator) + case _ => LazyList.from(it).view } def empty[A]: View[A] = Empty @@ -115,7 +118,7 @@ object View extends IterableFactory[View] { /** A view containing values of a given function over a range of integer values starting from 0. */ @SerialVersionUID(3L) class Tabulate[A](n: Int)(f: Int => A) extends AbstractView[A] { - def iterator: Iterator[A] = Iterator.tabulate(n)(f) + def iterator: Iterator[A]^{f} = Iterator.tabulate(n)(f) override def knownSize: Int = 0 max n override def isEmpty: Boolean = n <= 0 } @@ -123,7 +126,7 @@ object View extends IterableFactory[View] { /** A view containing repeated applications of a function to a start value */ @SerialVersionUID(3L) class Iterate[A](start: A, len: Int)(f: A => A) extends AbstractView[A] { - def iterator: Iterator[A] = Iterator.iterate(start)(f).take(len) + def iterator: Iterator[A]^{f} = Iterator.iterate(start)(f).take(len) override def knownSize: Int = 0 max len override def isEmpty: Boolean = len <= 0 } @@ -133,7 +136,7 @@ object View extends IterableFactory[View] { */ @SerialVersionUID(3L) class Unfold[A, S](initial: S)(f: S => Option[(A, S)]) extends AbstractView[A] { - def iterator: Iterator[A] = Iterator.unfold(initial)(f) + def iterator: Iterator[A]^{f} = Iterator.unfold(initial)(f) } /** An `IterableOps` whose collection type and collection type constructor are unknown */ @@ -141,30 +144,34 @@ object View extends IterableFactory[View] { /** A view that filters an underlying collection. */ @SerialVersionUID(3L) - class Filter[A](val underlying: SomeIterableOps[A], val p: A => Boolean, val isFlipped: Boolean) extends AbstractView[A] { + class Filter[A](val underlying: SomeIterableOps[A]^, val p: A => Boolean, val isFlipped: Boolean) extends AbstractView[A] { def iterator = underlying.iterator.filterImpl(p, isFlipped) override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize override def isEmpty: Boolean = iterator.isEmpty } object Filter { - def apply[A](underlying: Iterable[A], p: A => Boolean, isFlipped: Boolean): Filter[A] = + def apply[A](underlying: Iterable[A]^, p: A => Boolean, isFlipped: Boolean): Filter[A]^{underlying, p} = underlying match { - case filter: Filter[A] if filter.isFlipped == isFlipped => new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped) + case filter : Filter[A] if filter.isFlipped == isFlipped => { + val f: Filter[A]^{underlying} = filter.unsafeAssumePure // TODO remove when pattern matching works + val f2: Filter[A]^{f, p} = new Filter(f.underlying, a => f.p(a) && p(a), isFlipped) + f2 + } case _ => new Filter(underlying, p, isFlipped) } } /** A view that removes the duplicated elements as determined by the transformation function `f` */ @SerialVersionUID(3L) - class DistinctBy[A, B](underlying: SomeIterableOps[A], f: A => B) extends AbstractView[A] { - def iterator: Iterator[A] = underlying.iterator.distinctBy(f) + class DistinctBy[A, B](underlying: SomeIterableOps[A]^, f: A -> B) extends AbstractView[A] { + def iterator: Iterator[A]^{underlying} = underlying.iterator.distinctBy(f) override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize override def isEmpty: Boolean = underlying.isEmpty } @SerialVersionUID(3L) - class LeftPartitionMapped[A, A1, A2](underlying: SomeIterableOps[A], f: A => Either[A1, A2]) extends AbstractView[A1] { + class LeftPartitionMapped[A, A1, A2](underlying: SomeIterableOps[A]^, f: A => Either[A1, A2]) extends AbstractView[A1] { def iterator: AbstractIterator[A1] = new AbstractIterator[A1] { private[this] val self = underlying.iterator private[this] var hd: A1 = _ @@ -189,7 +196,7 @@ object View extends IterableFactory[View] { } @SerialVersionUID(3L) - class RightPartitionMapped[A, A1, A2](underlying: SomeIterableOps[A], f: A => Either[A1, A2]) extends AbstractView[A2] { + class RightPartitionMapped[A, A1, A2](underlying: SomeIterableOps[A]^, f: A => Either[A1, A2]) extends AbstractView[A2] { def iterator: AbstractIterator[A2] = new AbstractIterator[A2] { private[this] val self = underlying.iterator private[this] var hd: A2 = _ @@ -215,7 +222,7 @@ object View extends IterableFactory[View] { /** A view that drops leading elements of the underlying collection. */ @SerialVersionUID(3L) - class Drop[A](underlying: SomeIterableOps[A], n: Int) extends AbstractView[A] { + class Drop[A](underlying: SomeIterableOps[A]^, n: Int) extends AbstractView[A] { def iterator = underlying.iterator.drop(n) protected val normN = n max 0 override def knownSize = { @@ -227,7 +234,7 @@ object View extends IterableFactory[View] { /** A view that drops trailing elements of the underlying collection. */ @SerialVersionUID(3L) - class DropRight[A](underlying: SomeIterableOps[A], n: Int) extends AbstractView[A] { + class DropRight[A](underlying: SomeIterableOps[A]^, n: Int) extends AbstractView[A] { def iterator = dropRightIterator(underlying.iterator, n) protected val normN = n max 0 override def knownSize = { @@ -240,7 +247,7 @@ object View extends IterableFactory[View] { } @SerialVersionUID(3L) - class DropWhile[A](underlying: SomeIterableOps[A], p: A => Boolean) extends AbstractView[A] { + class DropWhile[A](underlying: SomeIterableOps[A]^, p: A => Boolean) extends AbstractView[A] { def iterator = underlying.iterator.dropWhile(p) override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize override def isEmpty: Boolean = iterator.isEmpty @@ -248,7 +255,7 @@ object View extends IterableFactory[View] { /** A view that takes leading elements of the underlying collection. */ @SerialVersionUID(3L) - class Take[+A](underlying: SomeIterableOps[A], n: Int) extends AbstractView[A] { + class Take[+A](underlying: SomeIterableOps[A]^, n: Int) extends AbstractView[A] { def iterator = underlying.iterator.take(n) protected val normN = n max 0 override def knownSize = { @@ -260,7 +267,7 @@ object View extends IterableFactory[View] { /** A view that takes trailing elements of the underlying collection. */ @SerialVersionUID(3L) - class TakeRight[+A](underlying: SomeIterableOps[A], n: Int) extends AbstractView[A] { + class TakeRight[+A](underlying: SomeIterableOps[A]^, n: Int) extends AbstractView[A] { def iterator = takeRightIterator(underlying.iterator, n) protected val normN = n max 0 override def knownSize = { @@ -273,15 +280,15 @@ object View extends IterableFactory[View] { } @SerialVersionUID(3L) - class TakeWhile[A](underlying: SomeIterableOps[A], p: A => Boolean) extends AbstractView[A] { - def iterator: Iterator[A] = underlying.iterator.takeWhile(p) + class TakeWhile[A](underlying: SomeIterableOps[A]^, p: A => Boolean) extends AbstractView[A] { + def iterator = underlying.iterator.takeWhile(p) override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize override def isEmpty: Boolean = iterator.isEmpty } @SerialVersionUID(3L) - class ScanLeft[+A, +B](underlying: SomeIterableOps[A], z: B, op: (B, A) => B) extends AbstractView[B] { - def iterator: Iterator[B] = underlying.iterator.scanLeft(z)(op) + class ScanLeft[+A, +B](underlying: SomeIterableOps[A]^, z: B, op: (B, A) => B) extends AbstractView[B] { + def iterator = underlying.iterator.scanLeft(z)(op) override def knownSize: Int = { val size = underlying.knownSize if (size >= 0) size + 1 else -1 @@ -291,7 +298,7 @@ object View extends IterableFactory[View] { /** A view that maps elements of the underlying collection. */ @SerialVersionUID(3L) - class Map[+A, +B](underlying: SomeIterableOps[A], f: A => B) extends AbstractView[B] { + class Map[+A, +B](underlying: SomeIterableOps[A]^, f: A => B) extends AbstractView[B] { def iterator = underlying.iterator.map(f) override def knownSize = underlying.knownSize override def isEmpty: Boolean = underlying.isEmpty @@ -299,7 +306,7 @@ object View extends IterableFactory[View] { /** A view that flatmaps elements of the underlying collection. */ @SerialVersionUID(3L) - class FlatMap[A, B](underlying: SomeIterableOps[A], f: A => IterableOnce[B]) extends AbstractView[B] { + class FlatMap[A, B](underlying: SomeIterableOps[A]^, f: A => IterableOnce[B]^) extends AbstractView[B] { def iterator = underlying.iterator.flatMap(f) override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize override def isEmpty: Boolean = iterator.isEmpty @@ -307,7 +314,7 @@ object View extends IterableFactory[View] { /** A view that collects elements of the underlying collection. */ @SerialVersionUID(3L) - class Collect[+A, B](underlying: SomeIterableOps[A], pf: PartialFunction[A, B]) extends AbstractView[B] { + class Collect[+A, B](underlying: SomeIterableOps[A]^, pf: PartialFunction[A, B]^) extends AbstractView[B] { def iterator = underlying.iterator.collect(pf) } @@ -315,7 +322,7 @@ object View extends IterableFactory[View] { * of the suffix collection or iterator. */ @SerialVersionUID(3L) - class Concat[A](prefix: SomeIterableOps[A], suffix: SomeIterableOps[A]) extends AbstractView[A] { + class Concat[A](prefix: SomeIterableOps[A]^, suffix: SomeIterableOps[A]^) extends AbstractView[A] { def iterator = prefix.iterator ++ suffix.iterator override def knownSize = { val prefixSize = prefix.knownSize @@ -333,7 +340,7 @@ object View extends IterableFactory[View] { * of another collection. */ @SerialVersionUID(3L) - class Zip[A, B](underlying: SomeIterableOps[A], other: Iterable[B]) extends AbstractView[(A, B)] { + class Zip[A, B](underlying: SomeIterableOps[A]^, other: Iterable[B]^) extends AbstractView[(A, B)] { def iterator = underlying.iterator.zip(other) override def knownSize = { val s1 = underlying.knownSize @@ -350,7 +357,7 @@ object View extends IterableFactory[View] { * placeholder elements are used to extend the shorter collection to the length of the longer. */ @SerialVersionUID(3L) - class ZipAll[A, B](underlying: SomeIterableOps[A], other: Iterable[B], thisElem: A, thatElem: B) extends AbstractView[(A, B)] { + class ZipAll[A, B](underlying: SomeIterableOps[A]^, other: Iterable[B]^, thisElem: A, thatElem: B) extends AbstractView[(A, B)] { def iterator = underlying.iterator.zipAll(other, thisElem, thatElem) override def knownSize = { val s1 = underlying.knownSize @@ -364,8 +371,8 @@ object View extends IterableFactory[View] { /** A view that appends an element to its elements */ @SerialVersionUID(3L) - class Appended[+A](underlying: SomeIterableOps[A], elem: A) extends AbstractView[A] { - def iterator: Iterator[A] = new Concat(underlying, new View.Single(elem)).iterator + class Appended[+A](underlying: SomeIterableOps[A]^, elem: A) extends AbstractView[A] { + def iterator = new Concat(underlying, new View.Single(elem)).iterator override def knownSize: Int = { val size = underlying.knownSize if (size >= 0) size + 1 else -1 @@ -375,8 +382,8 @@ object View extends IterableFactory[View] { /** A view that prepends an element to its elements */ @SerialVersionUID(3L) - class Prepended[+A](elem: A, underlying: SomeIterableOps[A]) extends AbstractView[A] { - def iterator: Iterator[A] = new Concat(new View.Single(elem), underlying).iterator + class Prepended[+A](elem: A, underlying: SomeIterableOps[A]^) extends AbstractView[A] { + def iterator = new Concat(new View.Single(elem), underlying).iterator override def knownSize: Int = { val size = underlying.knownSize if (size >= 0) size + 1 else -1 @@ -385,8 +392,8 @@ object View extends IterableFactory[View] { } @SerialVersionUID(3L) - class Updated[A](underlying: SomeIterableOps[A], index: Int, elem: A) extends AbstractView[A] { - def iterator: Iterator[A] = new AbstractIterator[A] { + class Updated[A](underlying: SomeIterableOps[A]^, index: Int, elem: A) extends AbstractView[A] { + def iterator: Iterator[A]^{underlying} = new AbstractIterator[A] { private[this] val it = underlying.iterator private[this] var i = 0 def next(): A = { @@ -404,28 +411,28 @@ object View extends IterableFactory[View] { } @SerialVersionUID(3L) - private[collection] class Patched[A](underlying: SomeIterableOps[A], from: Int, other: IterableOnce[A], replaced: Int) extends AbstractView[A] { + private[collection] class Patched[A](underlying: SomeIterableOps[A]^, from: Int, other: IterableOnce[A]^, replaced: Int) extends AbstractView[A] { // we may be unable to traverse `other` more than once, so we need to cache it if that's the case - private val _other: Iterable[A] = other match { + private val _other: Iterable[A]^{other} = other match { case other: Iterable[A] => other case other => LazyList.from(other) } - def iterator: Iterator[A] = underlying.iterator.patch(from, _other.iterator, replaced) + def iterator: Iterator[A]^{this} = underlying.iterator.patch(from, _other.iterator, replaced) override def knownSize: Int = if (underlying.knownSize == 0 && _other.knownSize == 0) 0 else super.knownSize override def isEmpty: Boolean = if (knownSize == 0) true else iterator.isEmpty } @SerialVersionUID(3L) - class ZipWithIndex[A](underlying: SomeIterableOps[A]) extends AbstractView[(A, Int)] { - def iterator: Iterator[(A, Int)] = underlying.iterator.zipWithIndex + class ZipWithIndex[A](underlying: SomeIterableOps[A]^) extends AbstractView[(A, Int)] { + def iterator: Iterator[(A, Int)]^{this} = underlying.iterator.zipWithIndex override def knownSize: Int = underlying.knownSize override def isEmpty: Boolean = underlying.isEmpty } @SerialVersionUID(3L) - class PadTo[A](underlying: SomeIterableOps[A], len: Int, elem: A) extends AbstractView[A] { - def iterator: Iterator[A] = underlying.iterator.padTo(len, elem) + class PadTo[A](underlying: SomeIterableOps[A]^, len: Int, elem: A) extends AbstractView[A] { + def iterator: Iterator[A]^{this} = underlying.iterator.padTo(len, elem) override def knownSize: Int = { val size = underlying.knownSize @@ -434,7 +441,7 @@ object View extends IterableFactory[View] { override def isEmpty: Boolean = underlying.isEmpty && len <= 0 } - private[collection] def takeRightIterator[A](it: Iterator[A], n: Int): Iterator[A] = { + private[collection] def takeRightIterator[A](it: Iterator[A]^, n: Int): Iterator[A]^{it} = { val k = it.knownSize if(k == 0 || n <= 0) Iterator.empty else if(n == Int.MaxValue) it @@ -442,7 +449,7 @@ object View extends IterableFactory[View] { else new TakeRightIterator[A](it, n) } - private final class TakeRightIterator[A](private[this] var underlying: Iterator[A], maxlen: Int) extends AbstractIterator[A] { + private final class TakeRightIterator[A](private[this] var underlying: Iterator[A]^, maxlen: Int) extends AbstractIterator[A] { private[this] var len: Int = -1 private[this] var pos: Int = 0 private[this] var buf: ArrayBuffer[AnyRef] = _ @@ -478,7 +485,7 @@ object View extends IterableFactory[View] { x } } - override def drop(n: Int): Iterator[A] = { + override def drop(n: Int): Iterator[A]^{this} = { init() if (n > 0) { len = (len - n) max 0 @@ -488,7 +495,7 @@ object View extends IterableFactory[View] { } } - private[collection] def dropRightIterator[A](it: Iterator[A], n: Int): Iterator[A] = { + private[collection] def dropRightIterator[A](it: Iterator[A]^, n: Int): Iterator[A]^{it} = { if(n <= 0) it else { val k = it.knownSize @@ -497,7 +504,7 @@ object View extends IterableFactory[View] { } } - private final class DropRightIterator[A](private[this] var underlying: Iterator[A], maxlen: Int) extends AbstractIterator[A] { + private final class DropRightIterator[A](private[this] var underlying: Iterator[A]^, maxlen: Int) extends AbstractIterator[A] { private[this] var len: Int = -1 // known size or -1 if the end of `underlying` has not been seen yet private[this] var pos: Int = 0 private[this] var buf: ArrayBuffer[AnyRef] = _ diff --git a/library/src/scala/collection/WithFilter.scala b/library/src/scala/collection/WithFilter.scala index da65ba6fab27..fa4cf28e43a4 100644 --- a/library/src/scala/collection/WithFilter.scala +++ b/library/src/scala/collection/WithFilter.scala @@ -13,6 +13,7 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking /** A template trait that contains just the `map`, `flatMap`, `foreach` and `withFilter` methods * of trait `Iterable`. @@ -34,7 +35,7 @@ abstract class WithFilter[+A, +CC[_]] extends Serializable { * the given function `f` to each element of the filtered outer $coll * and collecting the results. */ - def map[B](f: A => B): CC[B] + def map[B](f: A => B): CC[B]^{this, f} /** Builds a new collection by applying a function to all elements of the * `filtered` outer $coll containing this `WithFilter` instance that satisfy @@ -46,7 +47,7 @@ abstract class WithFilter[+A, +CC[_]] extends Serializable { * of the filtered outer $coll and * concatenating the results. */ - def flatMap[B](f: A => IterableOnce[B]): CC[B] + def flatMap[B](f: A => IterableOnce[B]^): CC[B]^{this, f} /** Applies a function `f` to all elements of the `filtered` outer $coll. * @@ -67,6 +68,6 @@ abstract class WithFilter[+A, +CC[_]] extends Serializable { * All these operations apply to those elements of this $coll which * also satisfy both `p` and `q` predicates. */ - def withFilter(q: A => Boolean): WithFilter[A, CC] + def withFilter(q: A => Boolean): WithFilter[A, CC]^{this, q} } diff --git a/library/src/scala/collection/concurrent/Map.scala b/library/src/scala/collection/concurrent/Map.scala index 818fcda2a510..2269816f49b6 100644 --- a/library/src/scala/collection/concurrent/Map.scala +++ b/library/src/scala/collection/concurrent/Map.scala @@ -14,6 +14,8 @@ package scala package collection.concurrent import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.tailrec /** A template trait for mutable maps that allow concurrent access. diff --git a/library/src/scala/collection/concurrent/TrieMap.scala b/library/src/scala/collection/concurrent/TrieMap.scala index 4e3916c6981c..679c8258a0b1 100644 --- a/library/src/scala/collection/concurrent/TrieMap.scala +++ b/library/src/scala/collection/concurrent/TrieMap.scala @@ -15,6 +15,8 @@ package collection package concurrent import scala.language.`2.13` +import language.experimental.captureChecking + import java.util.concurrent.atomic._ import scala.{unchecked => uc} import scala.annotation.tailrec @@ -711,7 +713,7 @@ final class TrieMap[K, V] private (r: AnyRef, rtupd: AtomicReferenceFieldUpdater def this() = this(Hashing.default, Equiv.universal) - override def mapFactory: MapFactory[TrieMap] = TrieMap + override def mapFactory: StrictMapFactory[TrieMap] = TrieMap /* internal methods */ @@ -1016,10 +1018,10 @@ final class TrieMap[K, V] private (r: AnyRef, rtupd: AtomicReferenceFieldUpdater override def view: MapView[K, V] = if (nonReadOnly) readOnlySnapshot().view else super.view @deprecated("Use .view.filterKeys(f). A future version will include a strict version of this method (for now, .view.filterKeys(p).toMap).", "2.13.0") - override def filterKeys(p: K => Boolean): collection.MapView[K, V] = view.filterKeys(p) + override def filterKeys(p: K => Boolean): collection.MapView[K, V]^{p} = view.filterKeys(p) @deprecated("Use .view.mapValues(f). A future version will include a strict version of this method (for now, .view.mapValues(f).toMap).", "2.13.0") - override def mapValues[W](f: V => W): collection.MapView[K, W] = view.mapValues(f) + override def mapValues[W](f: V => W): collection.MapView[K, W]^{f} = view.mapValues(f) // END extra overrides /////////////////////////////////////////////////////////////////// @@ -1038,11 +1040,11 @@ final class TrieMap[K, V] private (r: AnyRef, rtupd: AtomicReferenceFieldUpdater @SerialVersionUID(3L) -object TrieMap extends MapFactory[TrieMap] { +object TrieMap extends StrictMapFactory[TrieMap] { def empty[K, V]: TrieMap[K, V] = new TrieMap[K, V] - def from[K, V](it: IterableOnce[(K, V)]): TrieMap[K, V] = new TrieMap[K, V]() ++= it + def from[K, V](it: IterableOnce[(K, V)]^): TrieMap[K, V] = new TrieMap[K, V]() ++= it def newBuilder[K, V]: mutable.GrowableBuilder[(K, V), TrieMap[K, V]] = new GrowableBuilder(empty[K, V]) @@ -1068,7 +1070,7 @@ object TrieMap extends MapFactory[TrieMap] { } // non-final as an extension point for parallel collections -private[collection] class TrieMapIterator[K, V](var level: Int, private var ct: TrieMap[K, V], mustInit: Boolean = true) extends AbstractIterator[(K, V)] { +private[collection] class TrieMapIterator[K, V](var level: Int, private var ct: TrieMap[K, V], mustInit: Boolean = true) extends AbstractIterator[(K, V)] { self => private val stack = new Array[Array[BasicNode]](7) private val stackpos = new Array[Int](7) private var depth = -1 diff --git a/library/src/scala/collection/convert/AsJavaConverters.scala b/library/src/scala/collection/convert/AsJavaConverters.scala index f82546e251a7..079847776368 100644 --- a/library/src/scala/collection/convert/AsJavaConverters.scala +++ b/library/src/scala/collection/convert/AsJavaConverters.scala @@ -15,6 +15,8 @@ package collection package convert import scala.language.`2.13` +import language.experimental.captureChecking + import java.util.{concurrent => juc} import java.{lang => jl, util => ju} diff --git a/library/src/scala/collection/convert/AsJavaExtensions.scala b/library/src/scala/collection/convert/AsJavaExtensions.scala index 406ad7e8b82f..67f127d9e042 100644 --- a/library/src/scala/collection/convert/AsJavaExtensions.scala +++ b/library/src/scala/collection/convert/AsJavaExtensions.scala @@ -15,6 +15,8 @@ package collection package convert import scala.language.`2.13` +import language.experimental.captureChecking + import java.util.{concurrent => juc} import java.{lang => jl, util => ju} diff --git a/library/src/scala/collection/convert/AsScalaConverters.scala b/library/src/scala/collection/convert/AsScalaConverters.scala index dac9b9484369..e2fa0f39bfef 100644 --- a/library/src/scala/collection/convert/AsScalaConverters.scala +++ b/library/src/scala/collection/convert/AsScalaConverters.scala @@ -15,6 +15,9 @@ package collection package convert import scala.language.`2.13` +import language.experimental.captureChecking +import caps.unsafe.unsafeAssumePure + import java.util.{concurrent => juc} import java.{lang => jl, util => ju} @@ -40,7 +43,7 @@ trait AsScalaConverters { */ def asScala[A](i: ju.Iterator[A]): Iterator[A] = i match { case null => null - case wrapper: IteratorWrapper[A @uc] => wrapper.underlying + case wrapper: IteratorWrapper[A @uc] => wrapper.underlying.unsafeAssumePure // TODO: Remove when pattern matching recognizes this is safe case _ => new JIteratorWrapper(i) } @@ -58,7 +61,7 @@ trait AsScalaConverters { */ def asScala[A](e: ju.Enumeration[A]): Iterator[A] = e match { case null => null - case wrapper: IteratorWrapper[A @uc] => wrapper.underlying + case wrapper: IteratorWrapper[A @uc] => wrapper.underlying.unsafeAssumePure // TODO: Remove when pattern matching recognizes this is safe case _ => new JEnumerationWrapper(e) } @@ -76,7 +79,7 @@ trait AsScalaConverters { */ def asScala[A](i: jl.Iterable[A]): Iterable[A] = i match { case null => null - case wrapper: IterableWrapper[A @uc] => wrapper.underlying + case wrapper: IterableWrapper[A @uc] => wrapper.underlying.unsafeAssumePure // TODO: Remove when pattern matching recognizes this is safe case _ => new JIterableWrapper(i) } @@ -91,7 +94,7 @@ trait AsScalaConverters { */ def asScala[A](c: ju.Collection[A]): Iterable[A] = c match { case null => null - case wrapper: IterableWrapper[A @uc] => wrapper.underlying + case wrapper: IterableWrapper[A @uc] => wrapper.underlying.unsafeAssumePure // TODO: Remove when pattern matching recognizes this is safe case _ => new JCollectionWrapper(c) } diff --git a/library/src/scala/collection/convert/AsScalaExtensions.scala b/library/src/scala/collection/convert/AsScalaExtensions.scala index 65f017495280..3ef08f0690f3 100644 --- a/library/src/scala/collection/convert/AsScalaExtensions.scala +++ b/library/src/scala/collection/convert/AsScalaExtensions.scala @@ -15,6 +15,8 @@ package collection package convert import scala.language.`2.13` +import language.experimental.captureChecking + import java.util.{concurrent => juc} import java.{lang => jl, util => ju} diff --git a/library/src/scala/collection/convert/ImplicitConversions.scala b/library/src/scala/collection/convert/ImplicitConversions.scala index ef72a79d5795..b2ebd48f16e9 100644 --- a/library/src/scala/collection/convert/ImplicitConversions.scala +++ b/library/src/scala/collection/convert/ImplicitConversions.scala @@ -15,6 +15,8 @@ package collection package convert import scala.language.`2.13` +import language.experimental.captureChecking + import java.util.{concurrent => juc} import java.{lang => jl, util => ju} diff --git a/library/src/scala/collection/convert/JavaCollectionWrappers.scala b/library/src/scala/collection/convert/JavaCollectionWrappers.scala index 1679491efcb0..5058f93fc48f 100644 --- a/library/src/scala/collection/convert/JavaCollectionWrappers.scala +++ b/library/src/scala/collection/convert/JavaCollectionWrappers.scala @@ -15,6 +15,8 @@ package collection package convert import scala.language.`2.13` +import language.experimental.captureChecking + import java.util.{concurrent => juc} import java.util.{NavigableMap} import java.{lang => jl, util => ju} @@ -29,7 +31,7 @@ import scala.util.control.ControlThrowable // not private[convert] because `WeakHashMap` uses JMapWrapper private[collection] object JavaCollectionWrappers extends Serializable { @SerialVersionUID(3L) - class IteratorWrapper[A](val underlying: Iterator[A]) extends ju.Iterator[A] with ju.Enumeration[A] with Serializable { + class IteratorWrapper[A](val underlying: Iterator[A]^) extends ju.Iterator[A] with ju.Enumeration[A] with Serializable { def hasNext = underlying.hasNext def next() = underlying.next() def hasMoreElements = underlying.hasNext @@ -65,14 +67,14 @@ private[collection] object JavaCollectionWrappers extends Serializable { } trait IterableWrapperTrait[A] extends ju.AbstractCollection[A] { - val underlying: Iterable[A] + val underlying: Iterable[A]^ def size = underlying.size - override def iterator: IteratorWrapper[A] = new IteratorWrapper(underlying.iterator) + override def iterator: IteratorWrapper[A]^{this} = new IteratorWrapper(underlying.iterator) override def isEmpty = underlying.isEmpty } @SerialVersionUID(3L) - class IterableWrapper[A](val underlying: Iterable[A]) extends ju.AbstractCollection[A] with IterableWrapperTrait[A] with Serializable { + class IterableWrapper[A](val underlying: Iterable[A]^) extends ju.AbstractCollection[A] with IterableWrapperTrait[A] with Serializable { override def equals(other: Any): Boolean = other match { case that: IterableWrapper[_] => this.underlying == that.underlying case _ => false @@ -151,7 +153,7 @@ private[collection] object JavaCollectionWrappers extends Serializable { def prepend(elem: A) = { underlying.subList(0, 0) add elem; this } def addOne(elem: A): this.type = { underlying add elem; this } def insert(idx: Int,elem: A): Unit = underlying.subList(0, idx).add(elem) - def insertAll(i: Int, elems: IterableOnce[A]) = { + def insertAll(i: Int, elems: IterableOnce[A]^) = { val ins = underlying.subList(0, i) elems.iterator.foreach(ins.add(_)) } @@ -160,7 +162,7 @@ private[collection] object JavaCollectionWrappers extends Serializable { // Note: Clone cannot just call underlying.clone because in Java, only specific collections // expose clone methods. Generically, they're protected. override def clone(): JListWrapper[A] = new JListWrapper(new ju.ArrayList[A](underlying)) - def patchInPlace(from: Int, patch: scala.collection.IterableOnce[A], replaced: Int): this.type = { + def patchInPlace(from: Int, patch: scala.collection.IterableOnce[A]^, replaced: Int): this.type = { remove(from, replaced) insertAll(from, patch) this diff --git a/library/src/scala/collection/convert/StreamExtensions.scala b/library/src/scala/collection/convert/StreamExtensions.scala index 4457989ead40..5e70ed1b4fd0 100644 --- a/library/src/scala/collection/convert/StreamExtensions.scala +++ b/library/src/scala/collection/convert/StreamExtensions.scala @@ -13,6 +13,8 @@ package scala.collection.convert import scala.language.`2.13` +import language.experimental.captureChecking + import java.util.Spliterator import java.util.stream._ import java.{lang => jl} diff --git a/library/src/scala/collection/generic/BitOperations.scala b/library/src/scala/collection/generic/BitOperations.scala index 39aa29d1daf9..a0a2806bd0dc 100644 --- a/library/src/scala/collection/generic/BitOperations.scala +++ b/library/src/scala/collection/generic/BitOperations.scala @@ -14,6 +14,7 @@ package scala.collection package generic import scala.language.`2.13` +import language.experimental.captureChecking /** Some bit operations. * diff --git a/library/src/scala/collection/generic/CommonErrors.scala b/library/src/scala/collection/generic/CommonErrors.scala index 3ced8e3debbf..c7cb23a3d112 100644 --- a/library/src/scala/collection/generic/CommonErrors.scala +++ b/library/src/scala/collection/generic/CommonErrors.scala @@ -14,6 +14,7 @@ package scala.collection package generic import scala.language.`2.13` +import language.experimental.captureChecking /** Some precomputed common errors to reduce the generated code size. */ diff --git a/library/src/scala/collection/generic/DefaultSerializationProxy.scala b/library/src/scala/collection/generic/DefaultSerializationProxy.scala index 056e53b6f882..d6b5e4e47693 100644 --- a/library/src/scala/collection/generic/DefaultSerializationProxy.scala +++ b/library/src/scala/collection/generic/DefaultSerializationProxy.scala @@ -15,6 +15,8 @@ package scala.collection.generic import java.io.{ObjectInputStream, ObjectOutputStream} import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.{Factory, Iterable} import scala.collection.mutable.Builder @@ -26,7 +28,7 @@ import scala.collection.mutable.Builder * additional state required to create the proper `Builder` needs to be captured by the `factory`. */ @SerialVersionUID(3L) -final class DefaultSerializationProxy[A](factory: Factory[A, Any], @transient private[this] val coll: Iterable[A]) extends Serializable { +final class DefaultSerializationProxy[A](factory: Factory[A, Any], @transient private[this] val coll: Iterable[A]^) extends Serializable { @transient protected var builder: Builder[A, Any] = _ @@ -75,8 +77,8 @@ private[collection] case object SerializeEnd * it directly without using this trait if you need a non-standard factory or if you want to use a different * serialization scheme. */ -transparent trait DefaultSerializable extends Serializable { this: scala.collection.Iterable[_] => - protected[this] def writeReplace(): AnyRef = { +transparent trait DefaultSerializable extends Serializable { this: scala.collection.Iterable[_]^ => + protected[this] def writeReplace(): AnyRef^{this} = { val f: Factory[Any, Any] = this match { case it: scala.collection.SortedMap[_, _] => it.sortedMapFactory.sortedMapFactory[Any, Any](using it.ordering.asInstanceOf[Ordering[Any]]).asInstanceOf[Factory[Any, Any]] case it: scala.collection.Map[_, _] => it.mapFactory.mapFactory[Any, Any].asInstanceOf[Factory[Any, Any]] diff --git a/library/src/scala/collection/generic/IsIterable.scala b/library/src/scala/collection/generic/IsIterable.scala index fd5db475536f..0b80203037fc 100644 --- a/library/src/scala/collection/generic/IsIterable.scala +++ b/library/src/scala/collection/generic/IsIterable.scala @@ -14,6 +14,7 @@ package scala.collection package generic import scala.language.`2.13` +import language.experimental.captureChecking /** A trait which can be used to avoid code duplication when defining extension * methods that should be applicable both to existing Scala collections (i.e., diff --git a/library/src/scala/collection/generic/IsIterableOnce.scala b/library/src/scala/collection/generic/IsIterableOnce.scala index a5c2c4889722..bced6e190206 100644 --- a/library/src/scala/collection/generic/IsIterableOnce.scala +++ b/library/src/scala/collection/generic/IsIterableOnce.scala @@ -15,6 +15,7 @@ package collection package generic import scala.language.`2.13` +import language.experimental.captureChecking /** Type class witnessing that a collection representation type `Repr` has * elements of type `A` and has a conversion to `IterableOnce[A]`. diff --git a/library/src/scala/collection/generic/IsMap.scala b/library/src/scala/collection/generic/IsMap.scala index 47eac5587f11..5b5a988792c0 100644 --- a/library/src/scala/collection/generic/IsMap.scala +++ b/library/src/scala/collection/generic/IsMap.scala @@ -14,6 +14,8 @@ package scala.collection package generic import scala.language.`2.13` +import language.experimental.captureChecking + import IsMap.Tupled import scala.collection.immutable.{IntMap, LongMap} diff --git a/library/src/scala/collection/generic/IsSeq.scala b/library/src/scala/collection/generic/IsSeq.scala index 2707ce988e34..04a94c5e5da0 100644 --- a/library/src/scala/collection/generic/IsSeq.scala +++ b/library/src/scala/collection/generic/IsSeq.scala @@ -14,6 +14,8 @@ package scala.collection package generic import scala.language.`2.13` +import language.experimental.captureChecking + import scala.reflect.ClassTag /** Type class witnessing that a collection representation type `Repr` has @@ -69,7 +71,7 @@ object IsSeq { def apply(i: Int): Char = s.charAt(i) def toIterable: Iterable[Char] = new immutable.WrappedString(s) protected[this] def coll: String = s - protected[this] def fromSpecific(coll: IterableOnce[Char]): String = coll.iterator.mkString + protected[this] def fromSpecific(coll: IterableOnce[Char]^): String = coll.iterator.mkString def iterableFactory: IterableFactory[immutable.ArraySeq] = immutable.ArraySeq.untagged override def empty: String = "" protected[this] def newSpecificBuilder: mutable.Builder[Char, String] = new StringBuilder @@ -94,7 +96,7 @@ object IsSeq { def length: Int = a.length def toIterable: Iterable[A] = mutable.ArraySeq.make(a) protected def coll: Array[A] = a - protected def fromSpecific(coll: IterableOnce[A]): Array[A] = Array.from(coll) + protected def fromSpecific(coll: IterableOnce[A]^): Array[A] = Array.from(coll) def iterableFactory: IterableFactory[mutable.ArraySeq] = mutable.ArraySeq.untagged override def empty: Array[A] = Array.empty[A] protected def newSpecificBuilder: mutable.Builder[A, Array[A]] = Array.newBuilder diff --git a/library/src/scala/collection/generic/Subtractable.scala b/library/src/scala/collection/generic/Subtractable.scala index 2e06819b6832..87c8f57c79d2 100644 --- a/library/src/scala/collection/generic/Subtractable.scala +++ b/library/src/scala/collection/generic/Subtractable.scala @@ -15,6 +15,7 @@ package collection package generic import scala.language.`2.13` +import language.experimental.captureChecking /** This trait represents collection-like objects that can be reduced * using a '+' operator. It defines variants of `-` and `--` @@ -60,5 +61,5 @@ trait Subtractable[A, +Repr <: Subtractable[A, Repr]] { self => * @return a new $coll that contains all elements of the current $coll * except one less occurrence of each of the elements of `elems`. */ - def --(xs: IterableOnce[A]): Repr = (repr /: xs.iterator) (_ - _) + def --(xs: IterableOnce[A]^): Repr = (repr /: xs.iterator) (_ - _) } diff --git a/library/src/scala/collection/generic/package.scala b/library/src/scala/collection/generic/package.scala index 0dc855d81d54..91181e0f7751 100644 --- a/library/src/scala/collection/generic/package.scala +++ b/library/src/scala/collection/generic/package.scala @@ -13,6 +13,7 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking package object generic { @deprecated("Clearable was moved from collection.generic to collection.mutable", "2.13.0") diff --git a/library/src/scala/collection/immutable/ArraySeq.scala b/library/src/scala/collection/immutable/ArraySeq.scala index 9cd2700779fb..a6c13b6fdd95 100644 --- a/library/src/scala/collection/immutable/ArraySeq.scala +++ b/library/src/scala/collection/immutable/ArraySeq.scala @@ -14,6 +14,8 @@ package scala.collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import java.util.Arrays import scala.annotation.unchecked.uncheckedVariance @@ -125,7 +127,7 @@ sealed abstract class ArraySeq[+A] } } - override def appendedAll[B >: A](suffix: collection.IterableOnce[B]): ArraySeq[B] = { + override def appendedAll[B >: A](suffix: collection.IterableOnce[B]^): ArraySeq[B] = { def genericResult = { val k = suffix.knownSize if (k == 0) this @@ -148,7 +150,7 @@ sealed abstract class ArraySeq[+A] } } - override def prependedAll[B >: A](prefix: collection.IterableOnce[B]): ArraySeq[B] = { + override def prependedAll[B >: A](prefix: collection.IterableOnce[B]^): ArraySeq[B] = { def genericResult = { val k = prefix.knownSize if (k == 0) this @@ -172,7 +174,7 @@ sealed abstract class ArraySeq[+A] } } - override def zip[B](that: collection.IterableOnce[B]): ArraySeq[(A, B)] = + override def zip[B](that: collection.IterableOnce[B]^): ArraySeq[(A, B)] = that match { case bs: ArraySeq[B] => ArraySeq.tabulate(length min bs.length) { i => @@ -278,7 +280,7 @@ object ArraySeq extends StrictOptimizedClassTagSeqFactory[ArraySeq] { self => def empty[A : ClassTag]: ArraySeq[A] = emptyImpl - def from[A](it: scala.collection.IterableOnce[A])(implicit tag: ClassTag[A]): ArraySeq[A] = it match { + def from[A](it: scala.collection.IterableOnce[A]^)(implicit tag: ClassTag[A]): ArraySeq[A] = it match { case as: ArraySeq[A] => as case _ => unsafeWrapArray(Array.from[A](it)) } diff --git a/library/src/scala/collection/immutable/BitSet.scala b/library/src/scala/collection/immutable/BitSet.scala index c454727fe4b9..176c31794621 100644 --- a/library/src/scala/collection/immutable/BitSet.scala +++ b/library/src/scala/collection/immutable/BitSet.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import BitSetOps.{LogWL, updateArray} import mutable.Builder import scala.annotation.{implicitNotFound, nowarn} @@ -38,7 +40,7 @@ sealed abstract class BitSet override def unsorted: Set[Int] = this - override protected def fromSpecific(coll: IterableOnce[Int]): BitSet = bitSetFactory.fromSpecific(coll) + override protected def fromSpecific(coll: IterableOnce[Int]^): BitSet = bitSetFactory.fromSpecific(coll) override protected def newSpecificBuilder: Builder[Int, BitSet] = bitSetFactory.newBuilder override def empty: BitSet = bitSetFactory.empty @@ -71,16 +73,16 @@ sealed abstract class BitSet override def map[B](f: Int => B)(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = super[StrictOptimizedSortedSetOps].map(f) - override def flatMap(f: Int => IterableOnce[Int]): BitSet = strictOptimizedFlatMap(newSpecificBuilder, f) - override def flatMap[B](f: Int => IterableOnce[B])(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = + override def flatMap(f: Int => IterableOnce[Int]^): BitSet = strictOptimizedFlatMap(newSpecificBuilder, f) + override def flatMap[B](f: Int => IterableOnce[B]^)(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = super[StrictOptimizedSortedSetOps].flatMap(f) - override def collect(pf: PartialFunction[Int, Int]): BitSet = strictOptimizedCollect(newSpecificBuilder, pf) - override def collect[B](pf: scala.PartialFunction[Int, B])(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = + override def collect(pf: PartialFunction[Int, Int]^): BitSet = strictOptimizedCollect(newSpecificBuilder, pf) + override def collect[B](pf: scala.PartialFunction[Int, B]^)(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = super[StrictOptimizedSortedSetOps].collect(pf) // necessary for disambiguation - override def zip[B](that: scala.IterableOnce[B])(implicit @implicitNotFound(collection.BitSet.zipOrdMsg) ev: Ordering[(Int, B)]): SortedSet[(Int, B)] = + override def zip[B](that: scala.IterableOnce[B]^)(implicit @implicitNotFound(collection.BitSet.zipOrdMsg) ev: Ordering[(Int, B)]): SortedSet[(Int, B)] = super.zip(that) protected[this] def writeReplace(): AnyRef = new BitSet.SerializationProxy(this) @@ -95,7 +97,7 @@ sealed abstract class BitSet @SerialVersionUID(3L) object BitSet extends SpecificIterableFactory[Int, BitSet] { - def fromSpecific(it: scala.collection.IterableOnce[Int]): BitSet = + def fromSpecific(it: scala.collection.IterableOnce[Int]^): BitSet = it match { case bs: BitSet => bs case _ => (newBuilder ++= it).result() diff --git a/library/src/scala/collection/immutable/ChampCommon.scala b/library/src/scala/collection/immutable/ChampCommon.scala index 899525e822a6..86b212de72f9 100644 --- a/library/src/scala/collection/immutable/ChampCommon.scala +++ b/library/src/scala/collection/immutable/ChampCommon.scala @@ -13,6 +13,8 @@ package scala.collection.immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.AbstractIterator import java.lang.Integer.bitCount import java.lang.Math.ceil diff --git a/library/src/scala/collection/immutable/HashMap.scala b/library/src/scala/collection/immutable/HashMap.scala index eefdf6b6adbb..b808b2e93e8a 100644 --- a/library/src/scala/collection/immutable/HashMap.scala +++ b/library/src/scala/collection/immutable/HashMap.scala @@ -14,6 +14,8 @@ package scala package collection.immutable import scala.language.`2.13` +import language.experimental.captureChecking + import java.lang.Integer.bitCount import java.lang.System.arraycopy @@ -22,7 +24,7 @@ import scala.collection.Hashing.improve import scala.collection.Stepper.EfficientSplit import scala.collection.generic.DefaultSerializable import scala.collection.mutable, mutable.ReusableBuilder -import scala.collection.{Iterator, MapFactory, MapFactoryDefaults, Stepper, StepperShape, mutable} +import scala.collection.{Iterator, StrictMapFactory, MapFactoryDefaults, Stepper, StepperShape, mutable} import scala.runtime.AbstractFunction2 import scala.runtime.Statics.releaseFence import scala.util.hashing.MurmurHash3 @@ -48,7 +50,7 @@ final class HashMap[K, +V] private[immutable] (private[immutable] val rootNode: // This release fence is present because rootNode may have previously been mutated during construction. releaseFence() - override def mapFactory: MapFactory[HashMap] = HashMap + override def mapFactory: StrictMapFactory[HashMap] = HashMap override def knownSize: Int = rootNode.size @@ -162,7 +164,7 @@ final class HashMap[K, +V] private[immutable] (private[immutable] val rootNode: newHashMapOrThis(rootNode.removed(key, keyUnimprovedHash, improve(keyUnimprovedHash), 0)) } - override def concat[V1 >: V](that: scala.IterableOnce[(K, V1)]): HashMap[K, V1] = that match { + override def concat[V1 >: V](that: scala.IterableOnce[(K, V1)]^): HashMap[K, V1] = that match { case hm: HashMap[K, V1] => if (isEmpty) hm else { @@ -385,7 +387,7 @@ final class HashMap[K, +V] private[immutable] (private[immutable] val rootNode: else new HashMap(newRootNode) } - override def removedAll(keys: IterableOnce[K]): HashMap[K, V] = { + override def removedAll(keys: IterableOnce[K]^): HashMap[K, V] = { if (isEmpty) { this } else { @@ -2192,7 +2194,7 @@ private final class MapNodeRemoveAllSetNodeIterator[K](rootSetNode: SetNode[K]) * @define coll immutable champ hash map */ @SerialVersionUID(3L) -object HashMap extends MapFactory[HashMap] { +object HashMap extends StrictMapFactory[HashMap] { @transient private final val EmptyMap = new HashMap(MapNode.empty) @@ -2200,7 +2202,7 @@ object HashMap extends MapFactory[HashMap] { def empty[K, V]: HashMap[K, V] = EmptyMap.asInstanceOf[HashMap[K, V]] - def from[K, V](source: collection.IterableOnce[(K, V)]): HashMap[K, V] = + def from[K, V](source: collection.IterableOnce[(K, V)]^): HashMap[K, V] = source match { case hs: HashMap[K, V] => hs case _ => (newBuilder[K, V] ++= source).result() @@ -2364,7 +2366,7 @@ private[immutable] final class HashMapBuilder[K, V] extends ReusableBuilder[(K, this } - override def addAll(xs: IterableOnce[(K, V)]): this.type = { + override def addAll(xs: IterableOnce[(K, V)]^): this.type = { ensureUnaliased() xs match { case hm: HashMap[K, V] => diff --git a/library/src/scala/collection/immutable/HashSet.scala b/library/src/scala/collection/immutable/HashSet.scala index 8ce8035fd015..65dca3dbba79 100644 --- a/library/src/scala/collection/immutable/HashSet.scala +++ b/library/src/scala/collection/immutable/HashSet.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import java.lang.Integer.{bitCount, numberOfTrailingZeros} import java.lang.System.arraycopy @@ -92,7 +94,7 @@ final class HashSet[A] private[immutable](private[immutable] val rootNode: Bitma newHashSetOrThis(newRootNode) } - override def concat(that: IterableOnce[A]): HashSet[A] = + override def concat(that: IterableOnce[A]^): HashSet[A] = that match { case hs: HashSet[A] => if (isEmpty) hs @@ -277,7 +279,7 @@ final class HashSet[A] private[immutable](private[immutable] val rootNode: Bitma * * That is, this method is safe to call on published sets because it does not mutate `this` */ - private[this] def removedAllWithShallowMutations(that: IterableOnce[A]): HashSet[A] = { + private[this] def removedAllWithShallowMutations(that: IterableOnce[A]^): HashSet[A] = { val iter = that.iterator var curr = rootNode while (iter.hasNext) { @@ -306,7 +308,7 @@ final class HashSet[A] private[immutable](private[immutable] val rootNode: Bitma this } - override def removedAll(that: IterableOnce[A]): HashSet[A] = that match { + override def removedAll(that: IterableOnce[A]^): HashSet[A] = that match { case set: scala.collection.Set[A] => diff(set) case range: Range if range.length > size => filter { @@ -1937,7 +1939,7 @@ object HashSet extends IterableFactory[HashSet] { def empty[A]: HashSet[A] = EmptySet.asInstanceOf[HashSet[A]] - def from[A](source: collection.IterableOnce[A]): HashSet[A] = + def from[A](source: collection.IterableOnce[A]^): HashSet[A] = source match { case hs: HashSet[A] => hs case _ if source.knownSize == 0 => empty[A] @@ -2077,7 +2079,7 @@ private[collection] final class HashSetBuilder[A] extends ReusableBuilder[A, Has this } - override def addAll(xs: IterableOnce[A]) = { + override def addAll(xs: IterableOnce[A]^) = { ensureUnaliased() xs match { case hm: HashSet[A] => diff --git a/library/src/scala/collection/immutable/IntMap.scala b/library/src/scala/collection/immutable/IntMap.scala index 1728da6c5710..6705091640b8 100644 --- a/library/src/scala/collection/immutable/IntMap.scala +++ b/library/src/scala/collection/immutable/IntMap.scala @@ -14,6 +14,8 @@ package scala.collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.generic.{BitOperations, DefaultSerializationProxy} import scala.collection.mutable.{Builder, ImmutableBuilder} import scala.annotation.tailrec @@ -53,7 +55,7 @@ object IntMap { def apply[T](elems: (Int, T)*): IntMap[T] = elems.foldLeft(empty[T])((x, y) => x.updated(y._1, y._2)) - def from[V](coll: IterableOnce[(Int, V)]): IntMap[V] = + def from[V](coll: IterableOnce[(Int, V)]^): IntMap[V] = newBuilder[V].addAll(coll).result() private[immutable] case object Nil extends IntMap[Nothing] { @@ -90,13 +92,13 @@ object IntMap { @SerialVersionUID(3L) private[this] object ToFactory extends Factory[(Int, AnyRef), IntMap[AnyRef]] with Serializable { - def fromSpecific(it: IterableOnce[(Int, AnyRef)]): IntMap[AnyRef] = IntMap.from[AnyRef](it) + def fromSpecific(it: IterableOnce[(Int, AnyRef)]^): IntMap[AnyRef] = IntMap.from[AnyRef](it) def newBuilder: Builder[(Int, AnyRef), IntMap[AnyRef]] = IntMap.newBuilder[AnyRef] } implicit def toBuildFrom[V](factory: IntMap.type): BuildFrom[Any, (Int, V), IntMap[V]] = ToBuildFrom.asInstanceOf[BuildFrom[Any, (Int, V), IntMap[V]]] private[this] object ToBuildFrom extends BuildFrom[Any, (Int, AnyRef), IntMap[AnyRef]] { - def fromSpecific(from: Any)(it: IterableOnce[(Int, AnyRef)]) = IntMap.from(it) + def fromSpecific(from: Any)(it: IterableOnce[(Int, AnyRef)]^) = IntMap.from(it) def newBuilder(from: Any) = IntMap.newBuilder[AnyRef] } @@ -181,9 +183,9 @@ sealed abstract class IntMap[+T] extends AbstractMap[Int, T] with StrictOptimizedMapOps[Int, T, Map, IntMap[T]] with Serializable { - override protected def fromSpecific(coll: scala.collection.IterableOnce[(Int, T) @uncheckedVariance]): IntMap[T] = + override protected def fromSpecific(coll: scala.collection.IterableOnce[(Int, T) @uncheckedVariance]^): IntMap[T] = intMapFrom[T](coll) - protected def intMapFrom[V2](coll: scala.collection.IterableOnce[(Int, V2)]): IntMap[V2] = { + protected def intMapFrom[V2](coll: scala.collection.IterableOnce[(Int, V2)]^): IntMap[V2] = { val b = IntMap.newBuilder[V2] b.sizeHint(coll) b.addAll(coll) @@ -326,12 +328,12 @@ sealed abstract class IntMap[+T] extends AbstractMap[Int, T] def map[V2](f: ((Int, T)) => (Int, V2)): IntMap[V2] = intMapFrom(new View.Map(this, f)) - def flatMap[V2](f: ((Int, T)) => IterableOnce[(Int, V2)]): IntMap[V2] = intMapFrom(new View.FlatMap(this, f)) + def flatMap[V2](f: ((Int, T)) => IterableOnce[(Int, V2)]^): IntMap[V2] = intMapFrom(new View.FlatMap(this, f)) - override def concat[V1 >: T](that: collection.IterableOnce[(Int, V1)]): IntMap[V1] = + override def concat[V1 >: T](that: collection.IterableOnce[(Int, V1)]^): IntMap[V1] = super.concat(that).asInstanceOf[IntMap[V1]] // Already has correct type but not declared as such - override def ++ [V1 >: T](that: collection.IterableOnce[(Int, V1)]): IntMap[V1] = concat(that) + override def ++ [V1 >: T](that: collection.IterableOnce[(Int, V1)]^): IntMap[V1] = concat(that) def collect[V2](pf: PartialFunction[(Int, T), (Int, V2)]): IntMap[V2] = strictOptimizedCollect(IntMap.newBuilder[V2], pf) diff --git a/library/src/scala/collection/immutable/Iterable.scala b/library/src/scala/collection/immutable/Iterable.scala index 0691663e7e4d..65f95ad87a27 100644 --- a/library/src/scala/collection/immutable/Iterable.scala +++ b/library/src/scala/collection/immutable/Iterable.scala @@ -13,6 +13,8 @@ package scala.collection.immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.{IterableFactory, IterableFactoryDefaults} /** A trait for collections that are guaranteed immutable. @@ -31,7 +33,7 @@ trait Iterable[+A] extends collection.Iterable[A] @SerialVersionUID(3L) object Iterable extends IterableFactory.Delegate[Iterable](List) { - override def from[E](it: IterableOnce[E]): Iterable[E] = it match { + override def from[E](it: IterableOnce[E]^): Iterable[E]^{it} = it match { case iterable: Iterable[E] => iterable case _ => super.from(it) } diff --git a/library/src/scala/collection/immutable/LazyList.scala b/library/src/scala/collection/immutable/LazyList.scala index bcf5f5df17a8..823144a19338 100644 --- a/library/src/scala/collection/immutable/LazyList.scala +++ b/library/src/scala/collection/immutable/LazyList.scala @@ -262,6 +262,9 @@ import scala.runtime.Statics * @define initiallyLazy This method does not evaluate anything until an operation is performed * on the result (e.g. calling `head` or `tail`, or checking if it is empty). * @define evaluatesAllElements This method evaluates all elements of the collection. + * @note Under Capture Checking, LazyList is unsound with regards to lazy/strict separation of collections. + * LazyList as-is will not be capture checked and might be deprecated in the future. + * Use LazyListIterable instead. */ @SerialVersionUID(4L) final class LazyList[+A] private (lazyState: AnyRef /* EmptyMarker.type | () => LazyList[A] */) diff --git a/library/src/scala/collection/immutable/LazyListIterable.scala b/library/src/scala/collection/immutable/LazyListIterable.scala new file mode 100644 index 000000000000..f1d14bbb1fad --- /dev/null +++ b/library/src/scala/collection/immutable/LazyListIterable.scala @@ -0,0 +1,1478 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. dba Akka + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection +package immutable + +import scala.language.`2.13` +import language.experimental.captureChecking + +import java.io.{ObjectInputStream, ObjectOutputStream} +import java.lang.{StringBuilder => JStringBuilder} + +import scala.annotation.tailrec +import scala.collection.generic.SerializeEnd +import scala.collection.mutable.{Builder, ReusableBuilder, StringBuilder} +import scala.language.implicitConversions +import scala.runtime.Statics + +/** This class implements an immutable linked list. We call it "lazy" + * because it computes its elements only when they are needed. + * + * Elements are memoized; that is, the value of each element is computed at most once. + * + * Elements are computed in order and are never skipped. + * As a consequence, accessing the tail causes the head to be computed first. + * + * How lazy is a `LazyListIterable`? When you have a value of type `LazyListIterable`, you + * don't know yet whether the list is empty. + * We say that it is lazy in its head. + * If you have tested that it is non-empty, + * then you also know that the head has been computed. + * + * It is also lazy in its tail, which is also a `LazyListIterable`. + * You don't know whether the tail is empty until it is "forced", which is to say, + * until an element of the tail is computed. + * + * These important properties of `LazyListIterable` depend on its construction using `#::` (or `#:::`). + * That operator is analogous to the "cons" of a strict `List`, `::`. + * It is "right-associative", so that the collection goes on the "right", + * and the element on the left of the operator is prepended to the collection. + * However, unlike the cons of a strict `List`, `#::` is lazy in its parameter, + * which is the element prepended to the left, and also lazy in its right-hand side, + * which is the `LazyListIterable` being prepended to. + * (That is accomplished by implicitly wrapping the `LazyListIterable`, as shown in the Scaladoc.) + * + * Other combinators from the collections API do not preserve this laziness. + * In particular, `++`, or `concat`, is "eager" or "strict" in its parameter + * and should not be used to compose `LazyListIterable`s. + * + * A `LazyListIterable` may be infinite. For example, `LazyListIterable.from(0)` contains + * all of the natural numbers `0`, `1`, `2`, ... For infinite sequences, + * some methods (such as `count`, `sum`, `max` or `min`) will not terminate. + * + * Here is an example showing the Fibonacci sequence, + * which may be evaluated to an arbitrary number of elements: + * + * {{{ + * import scala.math.BigInt + * object Main extends App { + * val fibs: LazyListIterable[BigInt] = + * BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map(n => n._1 + n._2) + * println { + * fibs.take(5).mkString(", ") + * } + * } + * // prints: 0, 1, 1, 2, 3 + * }}} + * + * To illustrate, let's add some output to the definition `fibs`, so we + * see what's going on. + * + * {{{ + * import scala.math.BigInt + * import scala.util.chaining._ + * object Main extends App { + * val fibs: LazyListIterable[BigInt] = + * BigInt(0) #:: BigInt(1) #:: + * fibs.zip(fibs.tail).map(n => (n._1 + n._2) + * .tap(sum => println(s"Adding ${n._1} and ${n._2} => $sum"))) + * fibs.take(5).foreach(println) + * fibs.take(6).foreach(println) + * } + * + * // prints + * // + * // 0 + * // 1 + * // Adding 0 and 1 => 1 + * // 1 + * // Adding 1 and 1 => 2 + * // 2 + * // Adding 1 and 2 => 3 + * // 3 + * + * // And then prints + * // + * // 0 + * // 1 + * // 1 + * // 2 + * // 3 + * // Adding 2 and 3 => 5 + * // 5 + * }}} + * + * Note that the definition of `fibs` uses `val` not `def`. + * Memoization of the `LazyListIterable` requires us to retain a reference to the computed values. + * + * `LazyListIterable` is considered an immutable data structure, even though its elements are computed on demand. + * Once the values are memoized they do not change. + * Moreover, the `LazyListIterable` itself is defined once and references to it are interchangeable. + * Values that have yet to be memoized still "exist"; they simply haven't been computed yet. + * + * Memoization can be a source of memory leaks and must be used with caution. + * It avoids recomputing elements of the list, but if a reference to the head + * is retained unintentionally, then all elements will be retained. + * + * The caveat that all elements are computed in order means + * that some operations, such as [[drop]], [[dropWhile]], [[flatMap]] or [[collect]], + * may process a large number of intermediate elements before returning. + * + * Here's an example that illustrates these behaviors. + * Let's begin with an iteration of the natural numbers. + * + * {{{ + * // We'll start with a silly iteration + * def loop(s: String, i: Int, iter: Iterator[Int]): Unit = { + * // Stop after 200,000 + * if (i < 200001) { + * if (i % 50000 == 0) println(s + i) + * loop(s, iter.next(), iter) + * } + * } + * + * // Our first LazyListIterable definition will be a val definition + * val lazylist1: LazyListIterable[Int] = { + * def loop(v: Int): LazyListIterable[Int] = v #:: loop(v + 1) + * loop(0) + * } + * + * // Because lazylist1 is a val, everything that the iterator produces is held + * // by virtue of the fact that the head of the LazyListIterable is held in lazylist1 + * val it1 = lazylist1.iterator + * loop("Iterator1: ", it1.next(), it1) + * + * // We can redefine this LazyListIterable such that we retain only a reference to its Iterator. + * // That allows the LazyListIterable to be garbage collected. + * // Using `def` to produce the LazyListIterable in a method ensures + * // that no val is holding onto the head, as with lazylist1. + * def lazylist2: LazyListIterable[Int] = { + * def loop(v: Int): LazyListIterable[Int] = v #:: loop(v + 1) + * loop(0) + * } + * val it2 = lazylist2.iterator + * loop("Iterator2: ", it2.next(), it2) + * + * // And, of course, we don't actually need a LazyListIterable at all for such a simple + * // problem. There's no reason to use a LazyListIterable if you don't actually need + * // one. + * val it3 = new Iterator[Int] { + * var i = -1 + * def hasNext = true + * def next(): Int = { i += 1; i } + * } + * loop("Iterator3: ", it3.next(), it3) + * }}} + * + * In the `fibs` example earlier, the fact that `tail` works at all is of interest. + * `fibs` has an initial `(0, 1, LazyListIterable(...))`, so `tail` is deterministic. + * If we defined `fibs` such that only `0` were concretely known, then the act + * of determining `tail` would require the evaluation of `tail`, so the + * computation would be unable to progress, as in this code: + * {{{ + * // The first time we try to access the tail we're going to need more + * // information which will require us to recurse, which will require us to + * // recurse, which... + * lazy val sov: LazyListIterable[Vector[Int]] = Vector(0) #:: sov.zip(sov.tail).map { n => n._1 ++ n._2 } + * }}} + * + * The definition of `fibs` above creates a larger number of objects than + * necessary depending on how you might want to implement it. The following + * implementation provides a more "cost effective" implementation due to the + * fact that it has a more direct route to the numbers themselves: + * + * {{{ + * lazy val fib: LazyListIterable[Int] = { + * def loop(h: Int, n: Int): LazyListIterable[Int] = h #:: loop(n, h + n) + * loop(1, 1) + * } + * }}} + * + * The head, the tail and whether the list is empty is initially unknown. + * Once any of those are evaluated, they are all known, though if the tail is + * built with `#::` or `#:::`, its content still isn't evaluated. Instead, evaluating + * the tail's content is deferred until the tail's empty status, head or tail is + * evaluated. + * + * Delaying the evaluation of whether a LazyListIterable is empty until it's needed + * allows LazyListIterable to not eagerly evaluate any elements on a call to `filter`. + * + * Only when it's further evaluated (which may be never!) do any of the elements get forced. + * + * For example: + * + * {{{ + * def tailWithSideEffect: LazyListIterable[Nothing] = { + * println("getting empty LazyListIterable") + * LazyListIterable.empty + * } + * + * val emptyTail = tailWithSideEffect // prints "getting empty LazyListIterable" + * + * val suspended = 1 #:: tailWithSideEffect // doesn't print anything + * val tail = suspended.tail // although the tail is evaluated, *still* nothing is yet printed + * val filtered = tail.filter(_ => false) // still nothing is printed + * filtered.isEmpty // prints "getting empty LazyListIterable" + * }}} + * + * ---- + * + * You may sometimes encounter an exception like the following: + * + * {{{ + * java.lang.RuntimeException: "LazyListIterable evaluation depends on its own result (self-reference); see docs for more info + * }}} + * + * This exception occurs when a `LazyListIterable` is attempting to derive its next element + * from itself, and is attempting to read the element currently being evaluated. + * As a trivial example: + * + * {{{ + * lazy val a: LazyListIterable[Int] = 1 #:: 2 #:: a.filter(_ > 2) + * }}} + * + * When attempting to evaluate the third element of `a`, it will skip the first two + * elements and read the third, but that element is already being evaluated. This is + * often caused by a subtle logic error; in this case, using `>=` in the `filter` + * would fix the error. + * + * @tparam A the type of the elements contained in this lazy list. + * + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#lazylistsIterable "Scala's Collection Library overview"]] + * section on `LazyListIterables` for a summary. + * @define Coll `LazyListIterable` + * @define coll lazy list + * @define orderDependent + * @define orderDependentFold + * @define appendStackSafety Note: Repeated chaining of calls to append methods (`appended`, + * `appendedAll`, `lazyAppendedAll`) without forcing any of the + * intermediate resulting lazy lists may overflow the stack when + * the final result is forced. + * @define preservesLaziness This method preserves laziness; elements are only evaluated + * individually as needed. + * @define initiallyLazy This method does not evaluate anything until an operation is performed + * on the result (e.g. calling `head` or `tail`, or checking if it is empty). + * @define evaluatesAllElements This method evaluates all elements of the collection. + */ +@SerialVersionUID(4L) +final class LazyListIterable[+A] private (lazyState: LazyListIterable.EmptyMarker.type | (() => LazyListIterable[A]^) /* EmptyMarker.type | () => LazyListIterable[A] */) + extends Iterable[A] + with collection.SeqOps[A, LazyListIterable, LazyListIterable[A]] + with IterableFactoryDefaults[A, LazyListIterable] + with Serializable { self: LazyListIterable[A]^ => + import LazyListIterable._ + + // kount() // LazyListTest.countAlloc + + private def this(head: A, tail: LazyListIterable[A]^) = { + this(LazyListIterable.EmptyMarker) + _head = head + _tail = caps.unsafe.unsafeAssumePure(tail) // SAFETY: we initialize LazyListIterable capturing tail + } + + // used to synchronize lazy state evaluation + // after initialization (`_head ne Uninitialized`) + // - `null` if this is an empty lazy list + // - `head: A` otherwise (can be `null`, `_tail == null` is used to test emptiness) + @volatile private[this] var _head: Any /* Uninitialized | A */ = + if (lazyState eq EmptyMarker) null else Uninitialized + + // when `_head eq Uninitialized` + // - `lazySate: () => LazyListIterable[A]` + // - MidEvaluation while evaluating lazyState + // when `_head ne Uninitialized` + // - `null` if this is an empty lazy list + // - `tail: LazyListIterable[A]` otherwise + private[this] var _tail: AnyRef^{this} /* () => LazyListIterable[A] | MidEvaluation.type | LazyListIterable[A] */ = + if (lazyState eq EmptyMarker) null else lazyState + + private def rawHead: Any = _head + private def rawTail: AnyRef^{this} = _tail + + @inline private def isEvaluated: Boolean = _head.asInstanceOf[AnyRef] ne Uninitialized + + private def initState(): Unit = synchronized { + if (!isEvaluated) { + // if it's already mid-evaluation, we're stuck in an infinite + // self-referential loop (also it's empty) + if (_tail eq MidEvaluation) + throw new RuntimeException( + "LazyListIterable evaluation depends on its own result (self-reference); see docs for more info") + + val fun = _tail.asInstanceOf[() ->{this} LazyListIterable[A]^] + _tail = MidEvaluation + val l = + // `fun` returns a LazyListIterable that represents the state (head/tail) of `this`. We call `l.evaluated` to ensure + // `l` is initialized, to prevent races when reading `rawTail` / `rawHead` below. + // Often, lazy lists are created with `newLL(eagerCons(...))` so `l` is already initialized, but `newLL` also + // accepts non-evaluated lazy lists. + try fun().evaluated + // restore `fun` in finally so we can try again later if an exception was thrown (similar to lazy val) + finally _tail = fun + _tail = l.rawTail + _head = l.rawHead + } + } + + @tailrec private def evaluated: LazyListIterable[A]^{this} = + if (isEvaluated) { + if (_tail == null) Empty + else this + } else { + initState() + evaluated + } + + override def iterableFactory: IterableFactory[LazyListIterable] = LazyListIterable + + // NOTE: `evaluated; this eq Empty` would be wrong. Deserialization of `Empty` creates a new + // instance with `null` fields, but the `evaluated` method always returns the canonical `Empty`. + @inline override def isEmpty: Boolean = evaluated eq Empty + + /** @inheritdoc + * + * $preservesLaziness + */ + override def knownSize: Int = if (knownIsEmpty) 0 else -1 + + override def head: A = + // inlined `isEmpty` to make it clear that `rawHead` below is initialized + if (evaluated eq Empty) throw new NoSuchElementException("head of empty lazy list") + else rawHead.asInstanceOf[A] + + override def tail: LazyListIterable[A]^{this} = + // inlined `isEmpty` to make it clear that `rawTail` below is initialized + if (evaluated eq Empty) throw new UnsupportedOperationException("tail of empty lazy list") + else rawTail.asInstanceOf[LazyListIterable[A]] + + /* Same implementation as of LinearSeq */ + override def length: Int = { + var these: LazyListIterable[A]^{this} = coll + var len = 0 + while (these.nonEmpty) { + len += 1 + these = these.tail + } + len + } + + /* Same implementation as of LinearSeq */ + // `apply` is defined in terms of `drop`, which is in turn defined in + // terms of `tail`. + @throws[IndexOutOfBoundsException] + override def apply(n: Int): A = { + if (n < 0) throw new IndexOutOfBoundsException(n.toString) + val skipped = drop(n) + if (skipped.isEmpty) throw new IndexOutOfBoundsException(n.toString) + skipped.head + } + + @inline private[this] def knownIsEmpty: Boolean = isEvaluated && isEmpty + @inline private def knownNonEmpty: Boolean = isEvaluated && !isEmpty + + /** Evaluates all undefined elements of the lazy list. + * + * This method detects cycles in lazy lists, and terminates after all + * elements of the cycle are evaluated. For example: + * + * {{{ + * val ring: LazyListIterable[Int] = 1 #:: 2 #:: 3 #:: ring + * ring.force + * ring.toString + * + * // prints + * // + * // LazyListIterable(1, 2, 3, ...) + * }}} + * + * This method will *not* terminate for non-cyclic infinite-sized collections. + * + * @return this + */ + def force: this.type = { + // Use standard 2x 1x iterator trick for cycle detection ("those" is slow one) + var these, those: LazyListIterable[A]^{this} = this + if (!these.isEmpty) { + these = these.tail + } + while (those ne these) { + if (these.isEmpty) return this + these = these.tail + if (these.isEmpty) return this + these = these.tail + if (these eq those) return this + those = those.tail + } + this + } + + /** @inheritdoc + * + * The iterator returned by this method preserves laziness; elements are + * only evaluated individually as needed. + */ + override def iterator: Iterator[A]^{this} = + if (knownIsEmpty) Iterator.empty + else new LazyIterator(this) + + /** Apply the given function `f` to each element of this linear sequence + * (while respecting the order of the elements). + * + * @param f The treatment to apply to each element. + * @note Overridden here as final to trigger tail-call optimization, which + * replaces 'this' with 'tail' at each iteration. This is absolutely + * necessary for allowing the GC to collect the underlying LazyListIterable as elements + * are consumed. + * @note This function will force the realization of the entire LazyListIterable + * unless the `f` throws an exception. + */ + @tailrec + override def foreach[U](f: A => U): Unit = { + if (!isEmpty) { + f(head) + tail.foreach(f) + } + } + + /** LazyListIterable specialization of foldLeft which allows GC to collect along the + * way. + * + * @tparam B The type of value being accumulated. + * @param z The initial value seeded into the function `op`. + * @param op The operation to perform on successive elements of the `LazyListIterable`. + * @return The accumulated value from successive applications of `op`. + */ + @tailrec + override def foldLeft[B](z: B)(op: (B, A) => B): B = + if (isEmpty) z + else tail.foldLeft(op(z, head))(op) + + // LazyListIterable.Empty doesn't use the SerializationProxy + protected[this] def writeReplace(): AnyRef^{this} = + if (knownNonEmpty) new SerializationProxy[A](this) else this + + override protected[this] def className = "LazyListIterable" + + /** The lazy list resulting from the concatenation of this lazy list with the argument lazy list. + * + * $preservesLaziness + * + * $appendStackSafety + * + * @param suffix The collection that gets appended to this lazy list + * @return The lazy list containing elements of this lazy list and the iterable object. + */ + def lazyAppendedAll[B >: A](suffix: => collection.IterableOnce[B]^): LazyListIterable[B]^{this, suffix} = + newLL { + if (isEmpty) suffix match { + case lazyList: LazyListIterable[B] => lazyList // don't recompute the LazyListIterable + case coll if coll.knownSize == 0 => Empty + case coll => eagerHeadFromIterator(coll.iterator) + } + else eagerCons(head, tail lazyAppendedAll suffix) + } + + /** @inheritdoc + * + * $preservesLaziness + * + * $appendStackSafety + */ + override def appendedAll[B >: A](suffix: IterableOnce[B]^): LazyListIterable[B]^{this, suffix} = + if (knownIsEmpty) LazyListIterable.from(suffix) + else lazyAppendedAll(suffix) + + /** @inheritdoc + * + * $preservesLaziness + * + * $appendStackSafety + */ + override def appended[B >: A](elem: B): LazyListIterable[B]^{this} = + if (knownIsEmpty) eagerCons(elem, Empty) + else lazyAppendedAll(Iterator.single(elem)) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def scanLeft[B](z: B)(op: (B, A) => B): LazyListIterable[B]^{this, op} = + if (knownIsEmpty) eagerCons(z, Empty) + else scanLeftImpl(z)(op) + + private def scanLeftImpl[B](z: B)(op: (B, A) => B): LazyListIterable[B]^{this, op} = + eagerCons( + z, + newLL { + if (isEmpty) Empty + else tail.scanLeftImpl(op(z, head))(op) + } + ) + + /** LazyListIterable specialization of reduceLeft which allows GC to collect + * along the way. + * + * @tparam B The type of value being accumulated. + * @param f The operation to perform on successive elements of the `LazyListIterable`. + * @return The accumulated value from successive applications of `f`. + */ + override def reduceLeft[B >: A](f: (B, A) => B): B = { + if (isEmpty) throw new UnsupportedOperationException("empty.reduceLeft") + else { + var reducedRes: B = head + var left: LazyListIterable[A]^{this} = tail + while (!left.isEmpty) { + reducedRes = f(reducedRes, left.head) + left = left.tail + } + reducedRes + } + } + + /** @inheritdoc + * + * $preservesLaziness + */ + override def partition(p: A => Boolean): (LazyListIterable[A]^{this, p}, LazyListIterable[A]^{this, p}) = (filter(p), filterNot(p)) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def partitionMap[A1, A2](f: A => Either[A1, A2]): (LazyListIterable[A1]^{this, f}, LazyListIterable[A2]^{this, f}) = { + val p: (LazyListIterable[Either[A1, A2]]^{this, f}, LazyListIterable[Either[A1, A2]]^{this, f}) = map(f).partition(_.isLeft) + (p._1.map(_.asInstanceOf[Left[A1, _]].value), p._2.map(_.asInstanceOf[Right[_, A2]].value)) + } + + /** @inheritdoc + * + * $preservesLaziness + */ + override def filter(pred: A => Boolean): LazyListIterable[A]^{this, pred} = + if (knownIsEmpty) Empty + else filterImpl(this, pred, isFlipped = false) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def filterNot(pred: A => Boolean): LazyListIterable[A]^{this, pred} = + if (knownIsEmpty) Empty + else filterImpl(this, pred, isFlipped = true) + + /** A `collection.WithFilter` which allows GC of the head of lazy list during processing. + * + * This method is not particularly useful for a lazy list, as [[filter]] already preserves + * laziness. + * + * The `collection.WithFilter` returned by this method preserves laziness; elements are + * only evaluated individually as needed. + */ + override def withFilter(p: A => Boolean): collection.WithFilter[A, LazyListIterable]^{this, p} = + new LazyListIterable.WithFilter(coll, p) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def prepended[B >: A](elem: B): LazyListIterable[B]^{this} = eagerCons(elem, this) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def prependedAll[B >: A](prefix: collection.IterableOnce[B]^): LazyListIterable[B]^{this, prefix} = + if (knownIsEmpty) LazyListIterable.from(prefix) + else if (prefix.knownSize == 0) this + else newLL(eagerHeadPrependIterator(prefix.iterator)(this)) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def map[B](f: A => B): LazyListIterable[B]^{this, f} = + if (knownIsEmpty) Empty + else mapImpl(f) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def tapEach[U](f: A => U): LazyListIterable[A]^{this, f} = map { a => f(a); a } + + private def mapImpl[B](f: A => B): LazyListIterable[B]^{this, f} = + newLL { + if (isEmpty) Empty + else eagerCons(f(head), tail.mapImpl(f)) + } + + /** @inheritdoc + * + * $preservesLaziness + */ + override def collect[B](pf: PartialFunction[A, B]^): LazyListIterable[B]^{this, pf} = + if (knownIsEmpty) Empty + else collectImpl(this, pf) + + /** @inheritdoc + * + * This method does not evaluate any elements further than + * the first element for which the partial function is defined. + */ + @tailrec + override def collectFirst[B](pf: PartialFunction[A, B]^): Option[B] = + if (isEmpty) None + else { + val res = pf.applyOrElse(head, anyToMarker.asInstanceOf[A => B]) + if (res.asInstanceOf[AnyRef] eq Statics.pfMarker) tail.collectFirst(pf) + else Some(res) + } + + /** @inheritdoc + * + * This method does not evaluate any elements further than + * the first element matching the predicate. + */ + @tailrec + override def find(p: A => Boolean): Option[A] = + if (isEmpty) None + else { + val elem = head + if (p(elem)) Some(elem) + else tail.find(p) + } + + /** @inheritdoc + * + * $preservesLaziness + */ + // optimisations are not for speed, but for functionality + // see tickets #153, #498, #2147, and corresponding tests in run/ (as well as run/stream_flatmap_odds.scala) + override def flatMap[B](f: A => IterableOnce[B]^): LazyListIterable[B]^{this, f} = + if (knownIsEmpty) Empty + else flatMapImpl(this, f) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def flatten[B](implicit asIterable: A -> IterableOnce[B]): LazyListIterable[B]^{this} = flatMap(asIterable) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def zip[B](that: collection.IterableOnce[B]^): LazyListIterable[(A, B)]^{this, that} = + if (knownIsEmpty || that.knownSize == 0) Empty + else newLL(eagerHeadZipImpl(that.iterator)) + + private def eagerHeadZipImpl[B](it: Iterator[B]^): LazyListIterable[(A, B)]^{this, it} = + if (isEmpty || !it.hasNext) Empty + else eagerCons((head, it.next()), newLL { tail eagerHeadZipImpl it }) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def zipWithIndex: LazyListIterable[(A, Int)]^{this} = this zip LazyListIterable.from(0) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def zipAll[A1 >: A, B](that: collection.Iterable[B]^, thisElem: A1, thatElem: B): LazyListIterable[(A1, B)]^{this, that} = { + if (knownIsEmpty) { + if (that.knownSize == 0) Empty + else LazyListIterable.continually(thisElem) zip that + } else { + if (that.knownSize == 0) zip(LazyListIterable.continually(thatElem)) + else newLL(eagerHeadZipAllImpl(that.iterator, thisElem, thatElem)) + } + } + + private def eagerHeadZipAllImpl[A1 >: A, B](it: Iterator[B]^, thisElem: A1, thatElem: B): LazyListIterable[(A1, B)]^{this, it} = { + if (it.hasNext) { + if (isEmpty) eagerCons((thisElem, it.next()), newLL { LazyListIterable.continually(thisElem) eagerHeadZipImpl it }) + else eagerCons((head, it.next()), newLL { tail.eagerHeadZipAllImpl(it, thisElem, thatElem) }) + } else { + if (isEmpty) Empty + else eagerCons((head, thatElem), tail zip LazyListIterable.continually(thatElem)) + } + } + + /** @inheritdoc + * + * This method is not particularly useful for a lazy list, as [[zip]] already preserves + * laziness. + * + * The `collection.LazyZip2` returned by this method preserves laziness; elements are + * only evaluated individually as needed. + */ + // just in case it can be meaningfully overridden at some point + override def lazyZip[B](that: collection.Iterable[B]^): LazyZip2[A, B, this.type]^{this, that} = + super.lazyZip(that) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def unzip[A1, A2](implicit asPair: A -> (A1, A2)): (LazyListIterable[A1]^{this}, LazyListIterable[A2]^{this}) = + (map(asPair(_)._1), map(asPair(_)._2)) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def unzip3[A1, A2, A3](implicit asTriple: A -> (A1, A2, A3)): (LazyListIterable[A1]^{this}, LazyListIterable[A2]^{this}, LazyListIterable[A3]^{this}) = + (map(asTriple(_)._1), map(asTriple(_)._2), map(asTriple(_)._3)) + + /** @inheritdoc + * + * $initiallyLazy + * Additionally, it preserves laziness for all except the first `n` elements. + */ + override def drop(n: Int): LazyListIterable[A]^{this} = + if (n <= 0) this + else if (knownIsEmpty) Empty + else dropImpl(this, n) + + /** @inheritdoc + * + * $initiallyLazy + * Additionally, it preserves laziness for all elements after the predicate returns `false`. + */ + override def dropWhile(p: A => Boolean): LazyListIterable[A]^{this, p} = + if (knownIsEmpty) Empty + else dropWhileImpl(this, p) + + /** @inheritdoc + * + * $initiallyLazy + */ + override def dropRight(n: Int): LazyListIterable[A]^{this} = { + if (n <= 0) this + else if (knownIsEmpty) Empty + else newLL { + var scout: LazyListIterable[A]^{this} = this + var remaining = n + // advance scout n elements ahead (or until empty) + while (remaining > 0 && !scout.isEmpty) { + remaining -= 1 + scout = scout.tail + } + eagerHeadDropRightImpl(scout) + } + } + + private def eagerHeadDropRightImpl(scout: LazyListIterable[_]^): LazyListIterable[A]^{this, scout} = + if (scout.isEmpty) Empty + else eagerCons(head, newLL(tail.eagerHeadDropRightImpl(scout.tail))) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def take(n: Int): LazyListIterable[A]^{this} = + if (knownIsEmpty) Empty + else takeImpl(n) + + private def takeImpl(n: Int): LazyListIterable[A]^{this} = { + if (n <= 0) Empty + else newLL { + if (isEmpty) Empty + else eagerCons(head, tail.takeImpl(n - 1)) + } + } + + /** @inheritdoc + * + * $preservesLaziness + */ + override def takeWhile(p: A => Boolean): LazyListIterable[A]^{this, p} = + if (knownIsEmpty) Empty + else takeWhileImpl(p) + + private def takeWhileImpl(p: A => Boolean): LazyListIterable[A]^{this, p} = + newLL { + if (isEmpty || !p(head)) Empty + else eagerCons(head, tail.takeWhileImpl(p)) + } + + /** @inheritdoc + * + * $initiallyLazy + */ + override def takeRight(n: Int): LazyListIterable[A]^{this} = + if (n <= 0 || knownIsEmpty) Empty + else takeRightImpl(this, n) + + /** @inheritdoc + * + * $initiallyLazy + * Additionally, it preserves laziness for all but the first `from` elements. + */ + override def slice(from: Int, until: Int): LazyListIterable[A]^{this} = take(until).drop(from) + + /** @inheritdoc + * + * $evaluatesAllElements + */ + override def reverse: LazyListIterable[A]^{this} = reverseOnto(Empty) + + // need contravariant type B to make the compiler happy - still returns LazyListIterable[A] + @tailrec + private def reverseOnto[B >: A](tl: LazyListIterable[B]^): LazyListIterable[B]^{this, tl} = + if (isEmpty) tl + else tail.reverseOnto(newLL(eagerCons(head, tl))) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def diff[B >: A](that: collection.Seq[B]): LazyListIterable[A]^{this} = + if (knownIsEmpty) Empty + else super.diff(that) + + /** @inheritdoc + * + * $preservesLaziness + */ + override def intersect[B >: A](that: collection.Seq[B]): LazyListIterable[A]^{this} = + if (knownIsEmpty) Empty + else super.intersect(that) + + @tailrec + private def lengthGt(len: Int): Boolean = + if (len < 0) true + else if (isEmpty) false + else tail.lengthGt(len - 1) + + /** @inheritdoc + * + * The iterator returned by this method mostly preserves laziness; + * a single element ahead of the iterator is evaluated. + */ + override def grouped(size: Int): Iterator[LazyListIterable[A]^{this}]^{this} = { + require(size > 0, "size must be positive, but was " + size) + slidingImpl(size = size, step = size) + } + + /** @inheritdoc + * + * The iterator returned by this method mostly preserves laziness; + * `size - step max 1` elements ahead of the iterator are evaluated. + */ + override def sliding(size: Int, step: Int): Iterator[LazyListIterable[A]^{this}]^{this} = { + require(size > 0 && step > 0, s"size=$size and step=$step, but both must be positive") + slidingImpl(size = size, step = step) + } + + @inline private def slidingImpl(size: Int, step: Int): Iterator[LazyListIterable[A]^{this}]^{this} = + if (knownIsEmpty) Iterator.empty + else + val it = new SlidingIterator[A](this, size = size, step = step) + it.asInstanceOf[Iterator[Nothing]] // CC cannot figure this out yet + + /** @inheritdoc + * + * $preservesLaziness + */ + override def padTo[B >: A](len: Int, elem: B): LazyListIterable[B]^{this} = + if (len <= 0) this + else newLL { + if (isEmpty) LazyListIterable.fill(len)(elem) + else eagerCons(head, tail.padTo(len - 1, elem)) + } + + /** @inheritdoc + * + * $preservesLaziness + */ + override def patch[B >: A](from: Int, other: IterableOnce[B]^, replaced: Int): LazyListIterable[B]^{this, other} = + if (knownIsEmpty) LazyListIterable from other + else patchImpl(from, other, replaced) + + private def patchImpl[B >: A](from: Int, other: IterableOnce[B]^, replaced: Int): LazyListIterable[B]^{this, other} = + newLL { + if (from <= 0) eagerHeadPrependIterator(other.iterator)(dropImpl(this, replaced)) + else if (isEmpty) eagerHeadFromIterator(other.iterator) + else eagerCons(head, tail.patchImpl(from - 1, other, replaced)) + } + + /** @inheritdoc + * + * $evaluatesAllElements + */ + // overridden just in case a lazy implementation is developed at some point + override def transpose[B](implicit asIterable: A -> collection.Iterable[B]): LazyListIterable[LazyListIterable[B]^{this}]^{this} = super.transpose + + /** @inheritdoc + * + * $preservesLaziness + */ + override def updated[B >: A](index: Int, elem: B): LazyListIterable[B]^{this} = + if (index < 0) throw new IndexOutOfBoundsException(s"$index") + else updatedImpl(index, elem, index) + + private def updatedImpl[B >: A](index: Int, elem: B, startIndex: Int): LazyListIterable[B]^{this} = + newLL { + if (index <= 0) eagerCons(elem, tail) + else if (tail.isEmpty) throw new IndexOutOfBoundsException(startIndex.toString) + else eagerCons(head, tail.updatedImpl(index - 1, elem, startIndex)) + } + + /** Appends all elements of this $coll to a string builder using start, end, and separator strings. + * The written text begins with the string `start` and ends with the string `end`. + * Inside, the string representations (w.r.t. the method `toString`) + * of all elements of this $coll are separated by the string `sep`. + * + * An undefined state is represented with `"<not computed>"` and cycles are represented with `"<cycle>"`. + * + * $evaluatesAllElements + * + * @param sb the string builder to which elements are appended. + * @param start the starting string. + * @param sep the separator string. + * @param end the ending string. + * @return the string builder `b` to which elements were appended. + */ + override def addString(sb: StringBuilder, start: String, sep: String, end: String): sb.type = { + force + addStringNoForce(sb.underlying, start, sep, end) + sb + } + + private[this] def addStringNoForce(b: JStringBuilder, start: String, sep: String, end: String): b.type = { + b.append(start) + if (!isEvaluated) b.append("") + else if (!isEmpty) { + b.append(head) + var cursor: LazyListIterable[A]^{this} = this + // explicit param to prevent an ObjectRef for cursor + @inline def appendHead(c: LazyListIterable[A]^): Unit = b.append(sep).append(c.head) + var scout: LazyListIterable[A]^{this} = tail + if (cursor ne scout) { + cursor = scout + if (scout.knownNonEmpty) { + scout = scout.tail + // Use 2x 1x iterator trick for cycle detection; slow iterator can add strings + while ((cursor ne scout) && scout.knownNonEmpty) { + appendHead(cursor) + cursor = cursor.tail + scout = scout.tail + if (scout.knownNonEmpty) scout = scout.tail + } + } + } + if (!scout.knownNonEmpty) { // Not a cycle, scout hit an end (empty or non-evaluated) + while (cursor ne scout) { + appendHead(cursor) + cursor = cursor.tail + } + // if cursor (eq scout) has state defined, it is empty; else unknown state + if (!cursor.isEvaluated) b.append(sep).append("") + } else { + // Cycle: the scout is `knownNonEmpty` and `eq cursor`. + // if the cycle starts at `this`, its elements were already added + if (cursor ne this) { + // If we have a prefix of length P followed by a cycle of length C, + // the scout will be at position (P%C) in the cycle when the cursor + // enters it at P. They'll then collide when the scout advances another + // C - (P%C) ahead of the cursor. + // If we run the scout P farther, then it will be at the start of + // the cycle: (C - (P%C) + (P%C)) == C == 0. So if another runner + // starts at the beginning of the prefix, they'll collide exactly at + // the start of the loop. + var runner: LazyListIterable[A]^{this} = this + while (runner ne scout) { + runner = runner.tail + scout = scout.tail + } + while({ + val ct = cursor.tail + if (ct ne scout) { + // In `lazy val xs: LazyListIterable[Int] = 1 #:: 2 #:: xs`, method `#::` creates a LazyListIterable instance which ends up as the 3rd element. + // That 3rd element initially has unknown head/tail. Once it completes, the tail is assigned to be `xs.tail`. + // So in memory the structure is `LLx(1, LLy(2, LLz(1, )))`. + // In `toString` we skip the last element to maintain the illusion. + appendHead(cursor) + } + cursor = ct + cursor ne scout + }) () + } + b.append(sep).append("") + } + } + b.append(end) + b + } + + /** $preservesLaziness + * + * @return a string representation of this collection. An undefined state is + * represented with `"<not computed>"` and cycles are represented with `"<cycle>"` + * + * Examples: + * + * - `"LazyListIterable(4, <not computed>)"`, a non-empty lazy list ; + * - `"LazyListIterable(1, 2, 3, <not computed>)"`, a lazy list with at least three elements ; + * - `"LazyListIterable(1, 2, 3, <cycle>)"`, an infinite lazy list that contains + * a cycle at the fourth element. + */ + override def toString(): String = addStringNoForce(new JStringBuilder(className), "(", ", ", ")").toString + + /** @inheritdoc + * + * $preservesLaziness + */ + @deprecated("Check .knownSize instead of .hasDefiniteSize for more actionable information (see scaladoc for details)", "2.13.0") + override def hasDefiniteSize: Boolean = { + if (!isEvaluated) false + else if (isEmpty) true + else { + // Two-iterator trick (2x & 1x speed) for cycle detection. + var those: LazyListIterable[A]^{this} = this + var these: LazyListIterable[A]^{this} = tail + while (those ne these) { + if (!these.isEvaluated) return false + else if (these.isEmpty) return true + these = these.tail + if (!these.isEvaluated) return false + else if (these.isEmpty) return true + these = these.tail + if (those eq these) return false + those = those.tail + } + false // Cycle detected + } + } +} + +/** + * $factoryInfo + * @define coll lazy list + * @define Coll `LazyListIterable` + */ +@SerialVersionUID(4L) +object LazyListIterable extends IterableFactory[LazyListIterable] { + + // LazyListTest.countAlloc + // var k = 0 + // def kount(): Unit = k += 1 + + private object Uninitialized extends Serializable + private object MidEvaluation + private object EmptyMarker + + private val Empty: LazyListIterable[Nothing] = new LazyListIterable(EmptyMarker) + + /** Creates a new LazyListIterable. */ + @inline private def newLL[A](state: => LazyListIterable[A]^): LazyListIterable[A]^{state} = new LazyListIterable[A](() => state) + + /** Creates a new LazyListIterable with evaluated `head` and `tail`. */ + @inline private def eagerCons[A](hd: A, tl: LazyListIterable[A]^): LazyListIterable[A]^{tl} = new LazyListIterable[A](hd, tl) + + private val anyToMarker: Any -> Any = _ => Statics.pfMarker + + /* All of the following `Impl` methods are carefully written so as not to + * leak the beginning of the `LazyListIterable`. They copy the initial `LazyListIterable` (`ll`) into + * `var rest`, which gets closed over as a `scala.runtime.ObjectRef`, thus not permanently + * leaking the head of the `LazyListIterable`. Additionally, the methods are written so that, should + * an exception be thrown by the evaluation of the `LazyListIterable` or any supplied function, they + * can continue their execution where they left off. + */ + + private def filterImpl[A](ll: LazyListIterable[A]^, p: A => Boolean, isFlipped: Boolean): LazyListIterable[A]^{ll, p} = { + // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD + var restRef: LazyListIterable[A]^{ll} = ll // val restRef = new ObjectRef(ll) + newLL { + var elem: A = null.asInstanceOf[A] + var found = false + var rest: LazyListIterable[A]^{ll} = restRef // var rest = restRef.elem + while (!found && !rest.isEmpty) { + elem = rest.head + found = p(elem) != isFlipped + rest = rest.tail + restRef = rest // restRef.elem = rest + } + if (found) eagerCons(elem, filterImpl(rest, p, isFlipped)) else Empty + } + } + + private def collectImpl[A, B](ll: LazyListIterable[A]^, pf: PartialFunction[A, B]^): LazyListIterable[B]^{ll, pf} = { + // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD + var restRef: LazyListIterable[A]^{ll} = ll // val restRef = new ObjectRef(ll) + newLL { + val marker = Statics.pfMarker + val toMarker = anyToMarker.asInstanceOf[A => B] // safe because Function1 is erased + + var res: B = marker.asInstanceOf[B] // safe because B is unbounded + var rest: LazyListIterable[A]^{ll} = restRef // var rest = restRef.elem + while((res.asInstanceOf[AnyRef] eq marker) && !rest.isEmpty) { + res = pf.applyOrElse(rest.head, toMarker) + rest = rest.tail + restRef = rest // restRef.elem = rest + } + if (res.asInstanceOf[AnyRef] eq marker) Empty + else eagerCons(res, collectImpl(rest, pf)) + } + } + + private def flatMapImpl[A, B](ll: LazyListIterable[A]^, f: A => IterableOnce[B]^): LazyListIterable[B]^{ll, f} = { + // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD + var restRef: LazyListIterable[A]^{ll} = ll // val restRef = new ObjectRef(ll) + newLL { + var it: Iterator[B]^{f} = null + var itHasNext = false + var rest: LazyListIterable[A]^{ll} = restRef // var rest = restRef.elem + while (!itHasNext && !rest.isEmpty) { + it = f(rest.head).iterator + itHasNext = it.hasNext + if (!itHasNext) { // wait to advance `rest` because `it.next()` can throw + rest = rest.tail + restRef = rest // restRef.elem = rest + } + } + if (itHasNext) { + val head = it.next() + rest = rest.tail + restRef = rest // restRef.elem = rest + eagerCons(head, newLL(eagerHeadPrependIterator(it)(flatMapImpl(rest, f)))) + } else Empty + } + } + + private def dropImpl[A](ll: LazyListIterable[A]^, n: Int): LazyListIterable[A]^{ll} = { + // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD + var restRef: LazyListIterable[A]^{ll} = ll // val restRef = new ObjectRef(ll) + var iRef = n // val iRef = new IntRef(n) + newLL { + var rest: LazyListIterable[A]^{ll} = restRef // var rest = restRef.elem + var i = iRef // var i = iRef.elem + while (i > 0 && !rest.isEmpty) { + rest = rest.tail + restRef = rest // restRef.elem = rest + i -= 1 + iRef = i // iRef.elem = i + } + rest + } + } + + private def dropWhileImpl[A](ll: LazyListIterable[A]^, p: A => Boolean): LazyListIterable[A]^{ll, p} = { + // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD + var restRef: LazyListIterable[A]^{ll} = ll // val restRef = new ObjectRef(ll) + newLL { + var rest: LazyListIterable[A]^{ll} = restRef // var rest = restRef.elem + while (!rest.isEmpty && p(rest.head)) { + rest = rest.tail + restRef = rest // restRef.elem = rest + } + rest + } + } + + private def takeRightImpl[A](ll: LazyListIterable[A]^, n: Int): LazyListIterable[A]^{ll} = { + // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD + var restRef: LazyListIterable[A]^{ll} = ll // val restRef = new ObjectRef(ll) + var scoutRef: LazyListIterable[A]^{ll} = ll // val scoutRef = new ObjectRef(ll) + var remainingRef = n // val remainingRef = new IntRef(n) + newLL { + var scout: LazyListIterable[A]^{ll} = scoutRef // var scout = scoutRef.elem + var remaining = remainingRef // var remaining = remainingRef.elem + // advance `scout` `n` elements ahead (or until empty) + while (remaining > 0 && !scout.isEmpty) { + scout = scout.tail + scoutRef = scout // scoutRef.elem = scout + remaining -= 1 + remainingRef = remaining // remainingRef.elem = remaining + } + var rest: LazyListIterable[A]^{ll} = restRef // var rest = restRef.elem + // advance `rest` and `scout` in tandem until `scout` reaches the end + while(!scout.isEmpty) { + scout = scout.tail + scoutRef = scout // scoutRef.elem = scout + rest = rest.tail // can't throw an exception as `scout` has already evaluated its tail + restRef = rest // restRef.elem = rest + } + // `rest` is the last `n` elements (or all of them) + rest + } + } + + /** An alternative way of building and matching lazy lists using LazyListIterable.cons(hd, tl). + */ + object cons { + /** A lazy list consisting of a given first element and remaining elements + * @param hd The first element of the result lazy list + * @param tl The remaining elements of the result lazy list + */ + def apply[A](hd: => A, tl: => LazyListIterable[A]): LazyListIterable[A]^{hd, tl} = newLL(eagerCons(hd, newLL(tl))) + + /** Maps a lazy list to its head and tail */ + def unapply[A](xs: LazyListIterable[A]^): Option[(A, LazyListIterable[A]^{xs})] = #::.unapply(xs) + } + + implicit def toDeferrer[A](l: => LazyListIterable[A]^): Deferrer[A]^{l} = new Deferrer[A](() => l) + + final class Deferrer[A] private[LazyListIterable] (private val l: () => LazyListIterable[A]^) extends AnyVal { self: Deferrer[A]^ => + /** Construct a LazyListIterable consisting of a given first element followed by elements + * from another LazyListIterable. + */ + def #:: [B >: A](elem: => B): LazyListIterable[B]^{this, elem} = newLL(eagerCons(elem, newLL(l()))) + /** Construct a LazyListIterable consisting of the concatenation of the given LazyListIterable and + * another LazyListIterable. + */ + def #:::[B >: A](prefix: LazyListIterable[B]^): LazyListIterable[B]^{this, prefix} = prefix lazyAppendedAll l() + } + + object #:: { + def unapply[A](s: LazyListIterable[A]^): Option[(A, LazyListIterable[A]^{s})] = + if (!s.isEmpty) Some((s.head, s.tail)) else None + } + + def from[A](coll: collection.IterableOnce[A]^): LazyListIterable[A]^{coll} = coll match { + case lazyList: LazyListIterable[A] => lazyList + case _ if coll.knownSize == 0 => empty[A] + case _ => newLL(eagerHeadFromIterator(coll.iterator)) + } + + def empty[A]: LazyListIterable[A] = Empty + + /** Creates a LazyListIterable with the elements of an iterator followed by a LazyListIterable suffix. + * Eagerly evaluates the first element. + */ + private def eagerHeadPrependIterator[A](it: Iterator[A]^)(suffix: => LazyListIterable[A]^): LazyListIterable[A]^{it, suffix} = + if (it.hasNext) eagerCons(it.next(), newLL(eagerHeadPrependIterator(it)(suffix))) + else suffix + + /** Creates a LazyListIterable from an Iterator. Eagerly evaluates the first element. */ + private def eagerHeadFromIterator[A](it: Iterator[A]^): LazyListIterable[A]^{it} = + if (it.hasNext) eagerCons(it.next(), newLL(eagerHeadFromIterator(it))) + else Empty + + override def concat[A](xss: collection.Iterable[A]*): LazyListIterable[A] = + if (xss.knownSize == 0) empty + else newLL(eagerHeadConcatIterators(xss.iterator)) + + private def eagerHeadConcatIterators[A](it: Iterator[collection.Iterable[A]^]^): LazyListIterable[A]^{it} = + if (!it.hasNext) Empty + else eagerHeadPrependIterator(it.next().iterator)(eagerHeadConcatIterators(it)) + + /** An infinite LazyListIterable that repeatedly applies a given function to a start value. + * + * @param start the start value of the LazyListIterable + * @param f the function that's repeatedly applied + * @return the LazyListIterable returning the infinite sequence of values `start, f(start), f(f(start)), ...` + */ + def iterate[A](start: => A)(f: A => A): LazyListIterable[A]^{start, f} = + newLL { + val head = start + eagerCons(head, iterate(f(head))(f)) + } + + /** + * Create an infinite LazyListIterable starting at `start` and incrementing by + * step `step`. + * + * @param start the start value of the LazyListIterable + * @param step the increment value of the LazyListIterable + * @return the LazyListIterable starting at value `start`. + */ + def from(start: Int, step: Int): LazyListIterable[Int] = + newLL(eagerCons(start, from(start + step, step))) + + /** + * Create an infinite LazyListIterable starting at `start` and incrementing by `1`. + * + * @param start the start value of the LazyListIterable + * @return the LazyListIterable starting at value `start`. + */ + def from(start: Int): LazyListIterable[Int] = from(start, 1) + + /** + * Create an infinite LazyListIterable containing the given element expression (which + * is computed for each occurrence). + * + * @param elem the element composing the resulting LazyListIterable + * @return the LazyListIterable containing an infinite number of elem + */ + def continually[A](elem: => A): LazyListIterable[A]^{elem} = newLL(eagerCons(elem, continually(elem))) + + override def fill[A](n: Int)(elem: => A): LazyListIterable[A]^{elem} = + if (n > 0) newLL(eagerCons(elem, LazyListIterable.fill(n - 1)(elem))) else empty + + override def tabulate[A](n: Int)(f: Int => A): LazyListIterable[A]^{f} = { + def at(index: Int): LazyListIterable[A]^{f} = + if (index < n) newLL(eagerCons(f(index), at(index + 1))) else empty + + at(0) + } + + // significantly simpler than the iterator returned by Iterator.unfold + override def unfold[A, S](init: S)(f: S => Option[(A, S)]): LazyListIterable[A]^{f} = + newLL { + f(init) match { + case Some((elem, state)) => eagerCons(elem, unfold(state)(f)) + case None => Empty + } + } + + /** Unlike LazyList, the builder returned by this method will eagerly evaluate all elements + * passed to it in `addAll`. + * To create a LazyListIterable from an IterableOnce, use `fromSpecific`. + * + * @tparam A the type of the ${coll}’s elements + * @return A builder for $Coll objects. + */ + def newBuilder[A]: Builder[A, LazyListIterable[A]] = (new collection.mutable.ListBuffer[A]).mapResult(from) + + private class LazyIterator[+A](private[this] var lazyList: LazyListIterable[A]^) extends AbstractIterator[A] { + override def hasNext: Boolean = !lazyList.isEmpty + + override def next(): A = + if (lazyList.isEmpty) Iterator.empty.next() + else { + val res = lazyList.head + lazyList = lazyList.tail + res + } + } + + private class SlidingIterator[A](l: LazyListIterable[A]^, size: Int, step: Int) + extends AbstractIterator[LazyListIterable[A]^{l}] { + private[this] var lazyList: LazyListIterable[A]^{l} = l + private val minLen = size - step max 0 + private var first = true + + def hasNext: Boolean = + if (first) !lazyList.isEmpty + else lazyList.lengthGt(minLen) + + def next(): LazyListIterable[A]^{l} = { + if (!hasNext) Iterator.empty.next() + else { + first = false + val list = lazyList + lazyList = list.drop(step) + list.take(size) + } + } + } + + private final class WithFilter[A] private[LazyListIterable](lazyList: LazyListIterable[A]^, p: A => Boolean) + extends collection.WithFilter[A, LazyListIterable] { + private[this] val filtered = lazyList.filter(p) + def map[B](f: A => B): LazyListIterable[B]^{this, f} = filtered.map(f) + def flatMap[B](f: A => IterableOnce[B]^): LazyListIterable[B]^{this, f} = filtered.flatMap(f) + def foreach[U](f: A => U): Unit = filtered.foreach(f) + def withFilter(q: A => Boolean): collection.WithFilter[A, LazyListIterable]^{this, q} = new WithFilter(filtered, q) + } + + // CC Note: Lazy Builder is not unsafe, but requires an explicit capture set. + + // private final class LazyBuilder[A, Cap^] extends ReusableBuilder[A, LazyListIterable[A]] { + // import LazyBuilder._ + + // private[this] var next: DeferredState[A, Cap]^{Cap} = _ + // private[this] var list: LazyListIterable[A]^{Cap} = _ + + // clear() + + // override def clear(): Unit = { + // val deferred = new DeferredState[A, Cap] + // list = newLL(deferred.eval()) + // next = deferred + // } + + // override def result(): LazyListIterable[A]^{Cap} = { + // next init Empty + // list + // } + + // override def addOne(elem: A): this.type = { + // val deferred = new DeferredState[A, Cap] + // next init eagerCons(elem, newLL(deferred.eval())) + // next = deferred + // this + // } + + // // lazy implementation which doesn't evaluate the collection being added + // override def addAll(xs: IterableOnce[A]^{Cap}): this.type = { + // if (xs.knownSize != 0) { + // val deferred = new DeferredState[A, Cap] + // next init eagerHeadPrependIterator(xs.iterator)(deferred.eval()) + // next = deferred + // } + // this + // } + // } + + // private object LazyBuilder { + // final class DeferredState[A, Cap^] { + // private[this] var _tail: () ->{Cap} LazyListIterable[A]^{Cap} = _ + + // def eval(): LazyListIterable[A]^{Cap} = { + // val state = _tail + // if (state == null) throw new IllegalStateException("uninitialized") + // state() + // } + + // // racy + // def init(state: ->{Cap} LazyListIterable[A]^{Cap}): Unit = { + // if (_tail != null) throw new IllegalStateException("already initialized") + // _tail = () => state + // } + // } + // } + + /** This serialization proxy is used for LazyListIterables which start with a sequence of evaluated cons cells. + * The forced sequence is serialized in a compact, sequential format, followed by the unevaluated tail, which uses + * standard Java serialization to store the complete structure of unevaluated thunks. This allows the serialization + * of long evaluated lazy lists without exhausting the stack through recursive serialization of cons cells. + */ + @SerialVersionUID(4L) + final class SerializationProxy[A](of: LazyListIterable[A]^) extends Serializable { + @transient protected var coll: LazyListIterable[A]^{this} = of + + private[this] def writeObject(out: ObjectOutputStream): Unit = { + out.defaultWriteObject() + var these: LazyListIterable[A]^{this} = coll + while (these.knownNonEmpty) { + out.writeObject(these.head) + these = these.tail + } + out.writeObject(SerializeEnd) + out.writeObject(these) + } + + private[this] def readObject(in: ObjectInputStream): Unit = { + in.defaultReadObject() + val init = new mutable.ListBuffer[A] + var initRead = false + while (!initRead) in.readObject match { + case SerializeEnd => initRead = true + case a => init += a.asInstanceOf[A] + } + val tail = in.readObject().asInstanceOf[LazyListIterable[A]] + // scala/scala#10118: caution that no code path can evaluate `tail.evaluated` + // before the resulting LazyListIterable is returned + val it = init.toList.iterator + coll = newLL(eagerHeadPrependIterator(it)(tail)) + } + + private[this] def readResolve(): Any = coll + } +} diff --git a/library/src/scala/collection/immutable/List.scala b/library/src/scala/collection/immutable/List.scala index f7b828bb97b5..dde3e5e8a05c 100644 --- a/library/src/scala/collection/immutable/List.scala +++ b/library/src/scala/collection/immutable/List.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.unchecked.uncheckedVariance import scala.annotation.tailrec import mutable.{Builder, ListBuffer} @@ -144,7 +146,7 @@ sealed abstract class List[+A] override def prepended[B >: A](elem: B): List[B] = elem :: this - override def prependedAll[B >: A](prefix: collection.IterableOnce[B]): List[B] = prefix match { + override def prependedAll[B >: A](prefix: collection.IterableOnce[B]^): List[B] = prefix match { case xs: List[B] => xs ::: this case _ if prefix.knownSize == 0 => this case b: ListBuffer[B] if this.isEmpty => b.toList @@ -166,7 +168,7 @@ sealed abstract class List[+A] } // When calling appendAll with another list `suffix`, avoid copying `suffix` - override def appendedAll[B >: A](suffix: collection.IterableOnce[B]): List[B] = suffix match { + override def appendedAll[B >: A](suffix: collection.IterableOnce[B]^): List[B] = suffix match { case xs: List[B] => this ::: xs case _ => super.appendedAll(suffix) } @@ -259,7 +261,7 @@ sealed abstract class List[+A] } } - final override def collect[B](pf: PartialFunction[A, B]): List[B] = { + final override def collect[B](pf: PartialFunction[A, B]^): List[B] = { if (this eq Nil) Nil else { var rest = this var h: ::[B] = null @@ -287,7 +289,7 @@ sealed abstract class List[+A] } } - final override def flatMap[B](f: A => IterableOnce[B]): List[B] = { + final override def flatMap[B](f: A => IterableOnce[B]^): List[B] = { var rest = this var h: ::[B] = null var t: ::[B] = null @@ -671,7 +673,7 @@ case object Nil extends List[Nothing] { override def init: Nothing = throw new UnsupportedOperationException("init of empty list") override def knownSize: Int = 0 override def iterator: Iterator[Nothing] = Iterator.empty - override def unzip[A1, A2](implicit asPair: Nothing => (A1, A2)): (List[A1], List[A2]) = EmptyUnzip + override def unzip[A1, A2](implicit asPair: Nothing -> (A1, A2)): (List[A1], List[A2]) = EmptyUnzip @transient private[this] val EmptyUnzip = (Nil, Nil) @@ -686,7 +688,7 @@ case object Nil extends List[Nothing] { object List extends StrictOptimizedSeqFactory[List] { private val TupleOfNil = (Nil, Nil) - def from[B](coll: collection.IterableOnce[B]): List[B] = Nil.prependedAll(coll) + def from[B](coll: collection.IterableOnce[B]^): List[B] = Nil.prependedAll(coll) def newBuilder[A]: Builder[A, List[A]] = new ListBuffer() diff --git a/library/src/scala/collection/immutable/ListMap.scala b/library/src/scala/collection/immutable/ListMap.scala index 8b2cd61175fe..1a7a465fc3c8 100644 --- a/library/src/scala/collection/immutable/ListMap.scala +++ b/library/src/scala/collection/immutable/ListMap.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.{nowarn, tailrec} import scala.collection.mutable.ReusableBuilder import scala.collection.generic.DefaultSerializable @@ -48,7 +50,7 @@ sealed class ListMap[K, +V] with MapFactoryDefaults[K, V, ListMap, Iterable] with DefaultSerializable { - override def mapFactory: MapFactory[ListMap] = ListMap + override def mapFactory: StrictMapFactory[ListMap] = ListMap override def size: Int = 0 @@ -127,7 +129,7 @@ sealed class ListMap[K, +V] * @define coll list map */ @SerialVersionUID(3L) -object ListMap extends MapFactory[ListMap] { +object ListMap extends StrictMapFactory[ListMap] { /** * Represents an entry in the `ListMap`. */ @@ -241,7 +243,7 @@ object ListMap extends MapFactory[ListMap] { private object EmptyListMap extends ListMap[Any, Nothing] - def from[K, V](it: collection.IterableOnce[(K, V)]): ListMap[K, V] = + def from[K, V](it: collection.IterableOnce[(K, V)]^): ListMap[K, V] = it match { case lm: ListMap[K, V] => lm case lhm: collection.mutable.LinkedHashMap[K, V] => @@ -324,7 +326,7 @@ private[immutable] final class ListMapBuilder[K, V] extends mutable.ReusableBuil } this } - override def addAll(xs: IterableOnce[(K, V)]): this.type = { + override def addAll(xs: IterableOnce[(K, V)]^): this.type = { if (isAliased) { super.addAll(xs) } else if (underlying.nonEmpty) { diff --git a/library/src/scala/collection/immutable/ListSet.scala b/library/src/scala/collection/immutable/ListSet.scala index 4325bd500b14..ed08c69d1b37 100644 --- a/library/src/scala/collection/immutable/ListSet.scala +++ b/library/src/scala/collection/immutable/ListSet.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import mutable.{Builder, ImmutableBuilder} import scala.annotation.tailrec import scala.collection.generic.DefaultSerializable @@ -118,7 +120,7 @@ sealed class ListSet[A] @SerialVersionUID(3L) object ListSet extends IterableFactory[ListSet] { - def from[E](it: scala.collection.IterableOnce[E]): ListSet[E] = + def from[E](it: scala.collection.IterableOnce[E]^): ListSet[E] = it match { case ls: ListSet[E] => ls case _ if it.knownSize == 0 => empty[E] diff --git a/library/src/scala/collection/immutable/LongMap.scala b/library/src/scala/collection/immutable/LongMap.scala index 9832b4a7d55c..fab52e1d1db2 100644 --- a/library/src/scala/collection/immutable/LongMap.scala +++ b/library/src/scala/collection/immutable/LongMap.scala @@ -14,6 +14,8 @@ package scala.collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import java.lang.IllegalStateException import scala.collection.generic.{BitOperations, DefaultSerializationProxy} @@ -53,7 +55,7 @@ object LongMap { def apply[T](elems: (Long, T)*): LongMap[T] = elems.foldLeft(empty[T])((x, y) => x.updated(y._1, y._2)) - def from[V](coll: IterableOnce[(Long, V)]): LongMap[V] = + def from[V](coll: IterableOnce[(Long, V)]^): LongMap[V] = newBuilder[V].addAll(coll).result() def newBuilder[V]: Builder[(Long, V), LongMap[V]] = @@ -87,13 +89,13 @@ object LongMap { @SerialVersionUID(3L) private[this] object ToFactory extends Factory[(Long, AnyRef), LongMap[AnyRef]] with Serializable { - def fromSpecific(it: IterableOnce[(Long, AnyRef)]): LongMap[AnyRef] = LongMap.from[AnyRef](it) + def fromSpecific(it: IterableOnce[(Long, AnyRef)]^): LongMap[AnyRef] = LongMap.from[AnyRef](it) def newBuilder: Builder[(Long, AnyRef), LongMap[AnyRef]] = LongMap.newBuilder[AnyRef] } implicit def toBuildFrom[V](factory: LongMap.type): BuildFrom[Any, (Long, V), LongMap[V]] = ToBuildFrom.asInstanceOf[BuildFrom[Any, (Long, V), LongMap[V]]] private[this] object ToBuildFrom extends BuildFrom[Any, (Long, AnyRef), LongMap[AnyRef]] { - def fromSpecific(from: Any)(it: IterableOnce[(Long, AnyRef)]) = LongMap.from(it) + def fromSpecific(from: Any)(it: IterableOnce[(Long, AnyRef)]^) = LongMap.from(it) def newBuilder(from: Any) = LongMap.newBuilder[AnyRef] } @@ -177,7 +179,7 @@ sealed abstract class LongMap[+T] extends AbstractMap[Long, T] with StrictOptimizedMapOps[Long, T, Map, LongMap[T]] with Serializable { - override protected def fromSpecific(coll: scala.collection.IterableOnce[(Long, T)] @uncheckedVariance): LongMap[T] = { + override protected def fromSpecific(coll: (scala.collection.IterableOnce[(Long, T)]^) @uncheckedVariance): LongMap[T] = { //TODO should this be the default implementation of this method in StrictOptimizedIterableOps? val b = newSpecificBuilder b.sizeHint(coll) @@ -477,12 +479,12 @@ sealed abstract class LongMap[+T] extends AbstractMap[Long, T] def map[V2](f: ((Long, T)) => (Long, V2)): LongMap[V2] = LongMap.from(new View.Map(coll, f)) - def flatMap[V2](f: ((Long, T)) => IterableOnce[(Long, V2)]): LongMap[V2] = LongMap.from(new View.FlatMap(coll, f)) + def flatMap[V2](f: ((Long, T)) => IterableOnce[(Long, V2)]^): LongMap[V2] = LongMap.from(new View.FlatMap(coll, f)) - override def concat[V1 >: T](that: scala.collection.IterableOnce[(Long, V1)]): LongMap[V1] = + override def concat[V1 >: T](that: scala.collection.IterableOnce[(Long, V1)]^): LongMap[V1] = super.concat(that).asInstanceOf[LongMap[V1]] // Already has correct type but not declared as such - override def ++ [V1 >: T](that: scala.collection.IterableOnce[(Long, V1)]): LongMap[V1] = concat(that) + override def ++ [V1 >: T](that: scala.collection.IterableOnce[(Long, V1)]^): LongMap[V1] = concat(that) def collect[V2](pf: PartialFunction[(Long, T), (Long, V2)]): LongMap[V2] = strictOptimizedCollect(LongMap.newBuilder[V2], pf) diff --git a/library/src/scala/collection/immutable/Map.scala b/library/src/scala/collection/immutable/Map.scala index b262243901c9..fe45a21e98d3 100644 --- a/library/src/scala/collection/immutable/Map.scala +++ b/library/src/scala/collection/immutable/Map.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.unchecked.uncheckedVariance import scala.collection.generic.DefaultSerializable import scala.collection.immutable.Map.Map4 @@ -28,7 +30,7 @@ trait Map[K, +V] with MapOps[K, V, Map, Map[K, V]] with MapFactoryDefaults[K, V, Map, Iterable] { - override def mapFactory: scala.collection.MapFactory[Map] = Map + override def mapFactory: scala.collection.StrictMapFactory[Map] = Map override final def toMap[K2, V2](implicit ev: (K, V) <:< (K2, V2)): Map[K2, V2] = Map.from(this.asInstanceOf[Map[K2, V2]]) @@ -41,7 +43,7 @@ trait Map[K, +V] * @param d the function mapping keys to values, used for non-present keys * @return a wrapper of the map with a default value */ - def withDefault[V1 >: V](d: K => V1): Map[K, V1] = new Map.WithDefault[K, V1](this, d) + def withDefault[V1 >: V](d: K -> V1): Map[K, V1] = new Map.WithDefault[K, V1](this, d) /** The same map with a given default value. * Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc. @@ -62,7 +64,7 @@ trait Map[K, +V] */ transparent trait MapOps[K, +V, +CC[X, +Y] <: MapOps[X, Y, CC, _], +C <: MapOps[K, V, CC, C]] extends IterableOps[(K, V), Iterable, C] - with collection.MapOps[K, V, CC, C] { + with collection.StrictMapOps[K, V, CC, C] { protected def coll: C with CC[K, V] @@ -88,10 +90,10 @@ transparent trait MapOps[K, +V, +CC[X, +Y] <: MapOps[X, Y, CC, _], +C <: MapOps[ * @return a new $coll that contains all elements of the current $coll * except one less occurrence of each of the elements of `elems`. */ - def removedAll(keys: IterableOnce[K]): C = keys.iterator.foldLeft[C](coll)(_ - _) + def removedAll(keys: IterableOnce[K]^): C = keys.iterator.foldLeft[C](coll)(_ - _) /** Alias for `removedAll` */ - @`inline` final override def -- (keys: IterableOnce[K]): C = removedAll(keys) + @`inline` final override def -- (keys: IterableOnce[K]^): C = removedAll(keys) /** Creates a new map obtained by updating this map with a given key/value pair. * @param key the key @@ -155,7 +157,7 @@ transparent trait StrictOptimizedMapOps[K, +V, +CC[X, +Y] <: MapOps[X, Y, CC, _] with collection.StrictOptimizedMapOps[K, V, CC, C] with StrictOptimizedIterableOps[(K, V), Iterable, C] { - override def concat [V1 >: V](that: collection.IterableOnce[(K, V1)]): CC[K, V1] = { + override def concat [V1 >: V](that: collection.IterableOnce[(K, V1)]^): CC[K, V1] = { var result: CC[K, V1] = coll val it = that.iterator while (it.hasNext) result = result + it.next() @@ -170,10 +172,10 @@ transparent trait StrictOptimizedMapOps[K, +V, +CC[X, +Y] <: MapOps[X, Y, CC, _] * @define Coll `immutable.Map` */ @SerialVersionUID(3L) -object Map extends MapFactory[Map] { +object Map extends StrictMapFactory[Map] { @SerialVersionUID(3L) - class WithDefault[K, +V](val underlying: Map[K, V], val defaultValue: K => V) + class WithDefault[K, +V](val underlying: Map[K, V], val defaultValue: K -> V) extends AbstractMap[K, V] with MapOps[K, V, Map, WithDefault[K, V]] with Serializable { @@ -187,9 +189,9 @@ object Map extends MapFactory[Map] { override def isEmpty: Boolean = underlying.isEmpty - override def mapFactory: MapFactory[Map] = underlying.mapFactory + override def mapFactory: StrictMapFactory[Map] = underlying.mapFactory - override def concat [V2 >: V](xs: collection.IterableOnce[(K, V2)]): WithDefault[K, V2] = + override def concat [V2 >: V](xs: collection.IterableOnce[(K, V2)]^): WithDefault[K, V2] = new WithDefault(underlying.concat(xs), defaultValue) def removed(key: K): WithDefault[K, V] = new WithDefault[K, V](underlying.removed(key), defaultValue) @@ -199,7 +201,7 @@ object Map extends MapFactory[Map] { override def empty: WithDefault[K, V] = new WithDefault[K, V](underlying.empty, defaultValue) - override protected def fromSpecific(coll: collection.IterableOnce[(K, V)] @uncheckedVariance): WithDefault[K, V] = + override protected def fromSpecific(coll: (collection.IterableOnce[(K, V)]^) @uncheckedVariance): WithDefault[K, V] = new WithDefault[K, V](mapFactory.from(coll), defaultValue) override protected def newSpecificBuilder: Builder[(K, V), WithDefault[K, V]] @uncheckedVariance = @@ -208,7 +210,7 @@ object Map extends MapFactory[Map] { def empty[K, V]: Map[K, V] = EmptyMap.asInstanceOf[Map[K, V]] - def from[K, V](it: IterableOnce[(K, V)]): Map[K, V] = + def from[K, V](it: IterableOnce[(K, V)]^): Map[K, V] = it match { case it: Iterable[_] if it.isEmpty => empty[K, V] // Since IterableOnce[(K, V)] launders the variance of K, @@ -250,7 +252,7 @@ object Map extends MapFactory[Map] { override def valuesIterator: Iterator[Nothing] = Iterator.empty def updated [V1] (key: Any, value: V1): Map[Any, V1] = new Map1(key, value) def removed(key: Any): Map[Any, Nothing] = this - override def concat[V2 >: Nothing](suffix: IterableOnce[(Any, V2)]): Map[Any, V2] = suffix match { + override def concat[V2 >: Nothing](suffix: IterableOnce[(Any, V2)]^): Map[Any, V2] = suffix match { case m: immutable.Map[Any, V2] => m case _ => super.concat(suffix) } @@ -703,7 +705,7 @@ private[immutable] final class MapBuilderImpl[K, V] extends ReusableBuilder[(K, def addOne(elem: (K, V)) = addOne(elem._1, elem._2) - override def addAll(xs: IterableOnce[(K, V)]): this.type = + override def addAll(xs: IterableOnce[(K, V)]^): this.type = if (switchedToHashMapBuilder) { hashMapBuilder.addAll(xs) this diff --git a/library/src/scala/collection/immutable/NumericRange.scala b/library/src/scala/collection/immutable/NumericRange.scala index 6a10bef7171f..c09b49810b24 100644 --- a/library/src/scala/collection/immutable/NumericRange.scala +++ b/library/src/scala/collection/immutable/NumericRange.scala @@ -13,6 +13,8 @@ package scala.collection.immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.Stepper.EfficientSplit import scala.collection.{AbstractIterator, AnyStepper, IterableFactoryDefaults, Iterator, Stepper, StepperShape} import scala.collection.generic.CommonErrors diff --git a/library/src/scala/collection/immutable/Queue.scala b/library/src/scala/collection/immutable/Queue.scala index 6deaabc5c7f7..326d3c7c56a6 100644 --- a/library/src/scala/collection/immutable/Queue.scala +++ b/library/src/scala/collection/immutable/Queue.scala @@ -14,6 +14,8 @@ package scala.collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.generic.DefaultSerializable import scala.collection.mutable.{Builder, ListBuffer} @@ -120,7 +122,7 @@ sealed class Queue[+A] protected(protected val in: List[A], protected val out: L override def appended[B >: A](elem: B): Queue[B] = enqueue(elem) - override def appendedAll[B >: A](that: scala.collection.IterableOnce[B]): Queue[B] = { + override def appendedAll[B >: A](that: scala.collection.IterableOnce[B]^): Queue[B] = { val newIn = that match { case that: Queue[B] => that.in ++ (that.out reverse_::: this.in) case that: List[B] => that reverse_::: this.in @@ -151,7 +153,7 @@ sealed class Queue[+A] protected(protected val in: List[A], protected val out: L * @param iter an iterable object */ @deprecated("Use `enqueueAll` instead of `enqueue` to enqueue a collection of elements", "2.13.0") - @`inline` final def enqueue[B >: A](iter: scala.collection.Iterable[B]) = enqueueAll(iter) + @`inline` final def enqueue[B >: A](iter: scala.collection.Iterable[B]^) = enqueueAll(iter) /** Creates a new queue with all elements provided by an `Iterable` object * added at the end of the old queue. @@ -161,7 +163,7 @@ sealed class Queue[+A] protected(protected val in: List[A], protected val out: L * * @param iter an iterable object */ - def enqueueAll[B >: A](iter: scala.collection.Iterable[B]): Queue[B] = appendedAll(iter) + def enqueueAll[B >: A](iter: scala.collection.Iterable[B]^): Queue[B] = appendedAll(iter) /** Returns a tuple with the first element in the queue, * and a new queue with this element removed. @@ -203,7 +205,7 @@ sealed class Queue[+A] protected(protected val in: List[A], protected val out: L object Queue extends StrictOptimizedSeqFactory[Queue] { def newBuilder[A]: Builder[A, Queue[A]] = new ListBuffer[A] mapResult (x => new Queue[A](Nil, x)) - def from[A](source: IterableOnce[A]): Queue[A] = source match { + def from[A](source: IterableOnce[A]^): Queue[A] = source match { case q: Queue[A] => q case _ => val list = List.from(source) diff --git a/library/src/scala/collection/immutable/Range.scala b/library/src/scala/collection/immutable/Range.scala index 5fd0490596d7..1f361c70b7bb 100644 --- a/library/src/scala/collection/immutable/Range.scala +++ b/library/src/scala/collection/immutable/Range.scala @@ -14,6 +14,8 @@ package scala package collection.immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.Stepper.EfficientSplit import scala.collection.convert.impl.RangeStepper import scala.collection.generic.CommonErrors @@ -217,7 +219,7 @@ sealed abstract class Range( private[this] def posOf(i: Int): Int = if (contains(i)) (i - start) / step else -1 - override def sameElements[B >: Int](that: IterableOnce[B]): Boolean = that match { + override def sameElements[B >: Int](that: IterableOnce[B]^): Boolean = that match { case other: Range => (this.length : @annotation.switch) match { case 0 => other.isEmpty @@ -613,7 +615,7 @@ object Range { // As there is no appealing default step size for not-really-integral ranges, // we offer a partially constructed object. - class Partial[T, U](private val f: T => U) extends AnyVal { + class Partial[T, U](private val f: T => U) extends AnyVal { self: Partial[T, U]^ => def by(x: T): U = f(x) override def toString = "Range requires step" } @@ -641,7 +643,7 @@ private class RangeIterator( step: Int, lastElement: Int, initiallyEmpty: Boolean -) extends AbstractIterator[Int] with Serializable { +) extends AbstractIterator[Int] with Serializable { self => private[this] var _hasNext: Boolean = !initiallyEmpty private[this] var _next: Int = start override def knownSize: Int = if (_hasNext) (lastElement - _next) / step + 1 else 0 diff --git a/library/src/scala/collection/immutable/RedBlackTree.scala b/library/src/scala/collection/immutable/RedBlackTree.scala index a57785bbf741..94d6ed434d75 100644 --- a/library/src/scala/collection/immutable/RedBlackTree.scala +++ b/library/src/scala/collection/immutable/RedBlackTree.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.meta.{getter, setter} import scala.annotation.tailrec import scala.runtime.Statics.releaseFence diff --git a/library/src/scala/collection/immutable/Seq.scala b/library/src/scala/collection/immutable/Seq.scala index fc11b697dc0c..44eea79f54f3 100644 --- a/library/src/scala/collection/immutable/Seq.scala +++ b/library/src/scala/collection/immutable/Seq.scala @@ -15,6 +15,7 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking trait Seq[+A] extends Iterable[A] with collection.Seq[A] @@ -30,7 +31,7 @@ trait Seq[+A] extends Iterable[A] * @define coll immutable sequence * @define Coll `immutable.Seq` */ -transparent trait SeqOps[+A, +CC[_], +C] extends Any with collection.SeqOps[A, CC, C] +transparent trait SeqOps[+A, +CC[B] <: collection.StrictSeqOps[B, collection.Seq, collection.Seq[B]], +C] extends Any with collection.StrictSeqOps[A, CC, C] /** * $factoryInfo @@ -39,7 +40,7 @@ transparent trait SeqOps[+A, +CC[_], +C] extends Any with collection.SeqOps[A, C */ @SerialVersionUID(3L) object Seq extends SeqFactory.Delegate[Seq](List) { - override def from[E](it: IterableOnce[E]): Seq[E] = it match { + override def from[E](it: IterableOnce[E]^): Seq[E] = it match { case s: Seq[E] => s case _ => super.from(it) } @@ -59,7 +60,7 @@ trait IndexedSeq[+A] extends Seq[A] } - override def sameElements[B >: A](o: IterableOnce[B]): Boolean = o match { + override def sameElements[B >: A](o: IterableOnce[B]^): Boolean = o match { case that: IndexedSeq[_] => (this eq that) || { val length = this.length @@ -112,16 +113,16 @@ object IndexedSeqDefaults { @SerialVersionUID(3L) object IndexedSeq extends SeqFactory.Delegate[IndexedSeq](Vector) { - override def from[E](it: IterableOnce[E]): IndexedSeq[E] = it match { + override def from[E](it: IterableOnce[E]^): IndexedSeq[E] = it match { case is: IndexedSeq[E] => is case _ => super.from(it) } } /** Base trait for immutable indexed Seq operations */ -transparent trait IndexedSeqOps[+A, +CC[_], +C] +transparent trait IndexedSeqOps[+A, +CC[B] <: collection.StrictSeqOps[B, collection.Seq, collection.Seq[B]], +C] extends SeqOps[A, CC, C] - with collection.IndexedSeqOps[A, CC, C] { + with collection.StrictIndexedSeqOps[A, CC, C] { override def slice(from: Int, until: Int): C = { // since we are immutable we can just share the same collection @@ -143,7 +144,7 @@ trait LinearSeq[+A] @SerialVersionUID(3L) object LinearSeq extends SeqFactory.Delegate[LinearSeq](List) { - override def from[E](it: IterableOnce[E]): LinearSeq[E] = it match { + override def from[E](it: IterableOnce[E]^): LinearSeq[E] = it match { case ls: LinearSeq[E] => ls case _ => super.from(it) } diff --git a/library/src/scala/collection/immutable/SeqMap.scala b/library/src/scala/collection/immutable/SeqMap.scala index 50a5dcfce382..bba801ee04a1 100644 --- a/library/src/scala/collection/immutable/SeqMap.scala +++ b/library/src/scala/collection/immutable/SeqMap.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.mutable.{Builder, ReusableBuilder} /** A base trait for ordered, immutable maps. @@ -38,14 +40,14 @@ trait SeqMap[K, +V] with collection.SeqMap[K, V] with MapOps[K, V, SeqMap, SeqMap[K, V]] with MapFactoryDefaults[K, V, SeqMap, Iterable] { - override def mapFactory: MapFactory[SeqMap] = SeqMap + override def mapFactory: StrictMapFactory[SeqMap] = SeqMap } -object SeqMap extends MapFactory[SeqMap] { +object SeqMap extends StrictMapFactory[SeqMap] { def empty[K, V]: SeqMap[K, V] = EmptySeqMap.asInstanceOf[SeqMap[K, V]] - def from[K, V](it: collection.IterableOnce[(K, V)]): SeqMap[K, V] = + def from[K, V](it: collection.IterableOnce[(K, V)]^): SeqMap[K, V] = it match { //case sm: SeqMap[K, V] => sm case m: ListMap[K, V] => m @@ -274,7 +276,7 @@ object SeqMap extends MapFactory[SeqMap] { this } - override def addAll(xs: IterableOnce[(K, V)]): this.type = + override def addAll(xs: IterableOnce[(K, V)]^): this.type = if (switchedToVectorMapBuilder) { vectorMapBuilder.addAll(xs) this diff --git a/library/src/scala/collection/immutable/Set.scala b/library/src/scala/collection/immutable/Set.scala index 08fd4a13e9cd..c35189acbc0d 100644 --- a/library/src/scala/collection/immutable/Set.scala +++ b/library/src/scala/collection/immutable/Set.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.immutable.Set.Set4 import scala.collection.mutable.{Builder, ReusableBuilder} @@ -66,10 +68,10 @@ transparent trait SetOps[A, +CC[X], +C <: SetOps[A, CC, C]] * @param that the collection containing the elements to remove. * @return a new $coll with the given elements removed, omitting duplicates. */ - def removedAll(that: IterableOnce[A]): C = that.iterator.foldLeft[C](coll)(_ - _) + def removedAll(that: IterableOnce[A]^): C = that.iterator.foldLeft[C](coll)(_ - _) /** Alias for removedAll */ - override final def -- (that: IterableOnce[A]): C = removedAll(that) + override final def -- (that: IterableOnce[A]^): C = removedAll(that) } transparent trait StrictOptimizedSetOps[A, +CC[X], +C <: SetOps[A, CC, C]] @@ -77,7 +79,7 @@ transparent trait StrictOptimizedSetOps[A, +CC[X], +C <: SetOps[A, CC, C]] with collection.StrictOptimizedSetOps[A, CC, C] with StrictOptimizedIterableOps[A, CC, C] { - override def concat(that: collection.IterableOnce[A]): C = { + override def concat(that: collection.IterableOnce[A]^): C = { var result: C = coll val it = that.iterator while (it.hasNext) result = result + it.next() @@ -95,7 +97,7 @@ object Set extends IterableFactory[Set] { def empty[A]: Set[A] = EmptySet.asInstanceOf[Set[A]] - def from[E](it: collection.IterableOnce[E]): Set[E] = + def from[E](it: collection.IterableOnce[E]^): Set[E] = it match { case _ if it.knownSize == 0 => empty[E] // Since IterableOnce[E] launders the variance of E, @@ -124,7 +126,7 @@ object Set extends IterableFactory[Set] { override def knownSize: Int = size override def filter(pred: Any => Boolean): Set[Any] = this override def filterNot(pred: Any => Boolean): Set[Any] = this - override def removedAll(that: IterableOnce[Any]): Set[Any] = this + override def removedAll(that: IterableOnce[Any]^): Set[Any] = this override def diff(that: collection.Set[Any]): Set[Any] = this override def subsetOf(that: collection.Set[Any]): Boolean = true override def intersect(that: collection.Set[Any]): Set[Any] = this @@ -398,7 +400,7 @@ private final class SetBuilderImpl[A] extends ReusableBuilder[A, Set[A]] { this } - override def addAll(xs: IterableOnce[A]): this.type = + override def addAll(xs: IterableOnce[A]^): this.type = if (switchedToHashSetBuilder) { hashSetBuilder.addAll(xs) this diff --git a/library/src/scala/collection/immutable/SortedMap.scala b/library/src/scala/collection/immutable/SortedMap.scala index a25321235f89..b045c498d3c9 100644 --- a/library/src/scala/collection/immutable/SortedMap.scala +++ b/library/src/scala/collection/immutable/SortedMap.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.unchecked.uncheckedVariance import scala.collection.mutable.Builder @@ -70,7 +72,7 @@ trait SortedMap[K, +V] * @param d the function mapping keys to values, used for non-present keys * @return a wrapper of the map with a default value */ - override def withDefault[V1 >: V](d: K => V1): SortedMap[K, V1] = new SortedMap.WithDefault[K, V1](this, d) + override def withDefault[V1 >: V](d: K -> V1): SortedMap[K, V1] = new SortedMap.WithDefault[K, V1](this, d) /** The same map with a given default value. * Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc. @@ -124,7 +126,7 @@ transparent trait StrictOptimizedSortedMapOps[K, +V, +CC[X, +Y] <: Map[X, Y] wit with collection.StrictOptimizedSortedMapOps[K, V, CC, C] with StrictOptimizedMapOps[K, V, Map, C] { - override def concat[V2 >: V](xs: collection.IterableOnce[(K, V2)]): CC[K, V2] = { + override def concat[V2 >: V](xs: collection.IterableOnce[(K, V2)]^): CC[K, V2] = { var result: CC[K, V2] = coll val it = xs.iterator while (it.hasNext) result = result + it.next() @@ -135,12 +137,12 @@ transparent trait StrictOptimizedSortedMapOps[K, +V, +CC[X, +Y] <: Map[X, Y] wit @SerialVersionUID(3L) object SortedMap extends SortedMapFactory.Delegate[SortedMap](TreeMap) { - override def from[K: Ordering, V](it: IterableOnce[(K, V)]): SortedMap[K, V] = it match { + override def from[K: Ordering, V](it: IterableOnce[(K, V)]^): SortedMap[K, V] = it match { case sm: SortedMap[K, V] if Ordering[K] == sm.ordering => sm case _ => super.from(it) } - final class WithDefault[K, +V](underlying: SortedMap[K, V], defaultValue: K => V) + final class WithDefault[K, +V](underlying: SortedMap[K, V], defaultValue: K -> V) extends Map.WithDefault[K, V](underlying, defaultValue) with SortedMap[K, V] with SortedMapOps[K, V, SortedMap, WithDefault[K, V]] with Serializable { @@ -162,14 +164,14 @@ object SortedMap extends SortedMapFactory.Delegate[SortedMap](TreeMap) { override def updated[V1 >: V](key: K, value: V1): WithDefault[K, V1] = new WithDefault[K, V1](underlying.updated(key, value), defaultValue) - override def concat [V2 >: V](xs: collection.IterableOnce[(K, V2)]): WithDefault[K, V2] = + override def concat [V2 >: V](xs: collection.IterableOnce[(K, V2)]^): WithDefault[K, V2] = new WithDefault( underlying.concat(xs) , defaultValue) override def removed(key: K): WithDefault[K, V] = new WithDefault[K, V](underlying.removed(key), defaultValue) override def empty: WithDefault[K, V] = new WithDefault[K, V](underlying.empty, defaultValue) - override protected def fromSpecific(coll: scala.collection.IterableOnce[(K, V)] @uncheckedVariance): WithDefault[K, V] = + override protected def fromSpecific(coll: (scala.collection.IterableOnce[(K, V)]^) @uncheckedVariance): WithDefault[K, V] = new WithDefault[K, V](sortedMapFactory.from(coll), defaultValue) override protected def newSpecificBuilder: Builder[(K, V), WithDefault[K, V]] @uncheckedVariance = diff --git a/library/src/scala/collection/immutable/SortedSet.scala b/library/src/scala/collection/immutable/SortedSet.scala index e8bbf1810d48..ceeafeb6296b 100644 --- a/library/src/scala/collection/immutable/SortedSet.scala +++ b/library/src/scala/collection/immutable/SortedSet.scala @@ -15,6 +15,7 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking /** Base trait for sorted sets */ trait SortedSet[A] @@ -52,7 +53,7 @@ transparent trait StrictOptimizedSortedSetOps[A, +CC[X] <: SortedSet[X], +C <: S */ @SerialVersionUID(3L) object SortedSet extends SortedIterableFactory.Delegate[SortedSet](TreeSet) { - override def from[E: Ordering](it: IterableOnce[E]): SortedSet[E] = it match { + override def from[E: Ordering](it: IterableOnce[E]^): SortedSet[E] = it match { case ss: SortedSet[E] if Ordering[E] == ss.ordering => ss case _ => super.from(it) } diff --git a/library/src/scala/collection/immutable/Stream.scala b/library/src/scala/collection/immutable/Stream.scala index a7017a23a16d..b31c5c8018c6 100644 --- a/library/src/scala/collection/immutable/Stream.scala +++ b/library/src/scala/collection/immutable/Stream.scala @@ -25,7 +25,7 @@ import scala.collection.mutable.{ArrayBuffer, StringBuilder} import scala.language.implicitConversions import Stream.cons -@deprecated("Use LazyList (which is fully lazy) instead of Stream (which has a lazy tail only)", "2.13.0") +@deprecated("Use LazyListIterable (which is fully lazy) instead of Stream (which has a lazy tail only)", "2.13.0") @SerialVersionUID(3L) sealed abstract class Stream[+A] extends AbstractSeq[A] with LinearSeq[A] diff --git a/library/src/scala/collection/immutable/StrictOptimizedSeqOps.scala b/library/src/scala/collection/immutable/StrictOptimizedSeqOps.scala index 51be923eb719..a3ca5d25b287 100644 --- a/library/src/scala/collection/immutable/StrictOptimizedSeqOps.scala +++ b/library/src/scala/collection/immutable/StrictOptimizedSeqOps.scala @@ -15,17 +15,19 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.generic.CommonErrors /** Trait that overrides operations to take advantage of strict builders. */ -transparent trait StrictOptimizedSeqOps[+A, +CC[_], +C] +transparent trait StrictOptimizedSeqOps[+A, +CC[B] <: collection.StrictSeqOps[B, collection.Seq, collection.Seq[B]], +C] extends Any - with SeqOps[A, CC, C] + with StrictSeqOps[A, CC, C] with collection.StrictOptimizedSeqOps[A, CC, C] with StrictOptimizedIterableOps[A, CC, C] { - override def distinctBy[B](f: A => B): C = { + override def distinctBy[B](f: A -> B): C = { if (lengthCompare(1) <= 0) coll else { val builder = newSpecificBuilder @@ -62,7 +64,7 @@ transparent trait StrictOptimizedSeqOps[+A, +CC[_], +C] b.result() } - override def patch[B >: A](from: Int, other: IterableOnce[B], replaced: Int): CC[B] = { + override def patch[B >: A](from: Int, other: IterableOnce[B]^, replaced: Int): CC[B] = { val b = iterableFactory.newBuilder[B] var i = 0 val it = iterator diff --git a/library/src/scala/collection/immutable/TreeMap.scala b/library/src/scala/collection/immutable/TreeMap.scala index b4a7e4b6605d..84ca78c48b75 100644 --- a/library/src/scala/collection/immutable/TreeMap.scala +++ b/library/src/scala/collection/immutable/TreeMap.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.tailrec import scala.collection.Stepper.EfficientSplit import scala.collection.generic.DefaultSerializable @@ -149,7 +151,7 @@ final class TreeMap[K, +V] private (private val tree: RB.Tree[K, V])(implicit va def updated[V1 >: V](key: K, value: V1): TreeMap[K, V1] = newMapOrSelf(RB.update(tree, key, value, overwrite = true)) - override def concat[V1 >: V](that: collection.IterableOnce[(K, V1)]): TreeMap[K, V1] = + override def concat[V1 >: V](that: collection.IterableOnce[(K, V1)]^): TreeMap[K, V1] = newMapOrSelf(that match { case tm: TreeMap[K, V] @unchecked if ordering == tm.ordering => RB.union(tree, tm.tree) @@ -169,7 +171,7 @@ final class TreeMap[K, +V] private (private val tree: RB.Tree[K, V])(implicit va adder.finalTree }) - override def removedAll(keys: IterableOnce[K]): TreeMap[K, V] = keys match { + override def removedAll(keys: IterableOnce[K]^): TreeMap[K, V] = keys match { case ts: TreeSet[K] if ordering == ts.ordering => newMapOrSelf(RB.difference(tree, ts.tree)) case _ => super.removedAll(keys) @@ -310,7 +312,7 @@ object TreeMap extends SortedMapFactory[TreeMap] { def empty[K : Ordering, V]: TreeMap[K, V] = new TreeMap() - def from[K, V](it: IterableOnce[(K, V)])(implicit ordering: Ordering[K]): TreeMap[K, V] = + def from[K, V](it: IterableOnce[(K, V)]^)(implicit ordering: Ordering[K]): TreeMap[K, V] = it match { case tm: TreeMap[K, V] if ordering == tm.ordering => tm case sm: scala.collection.SortedMap[K, V] if ordering == sm.ordering => @@ -354,7 +356,7 @@ object TreeMap extends SortedMapFactory[TreeMap] { } } - override def addAll(xs: IterableOnce[(K, V)]): this.type = { + override def addAll(xs: IterableOnce[(K, V)]^): this.type = { xs match { // TODO consider writing a mutable-safe union for TreeSet/TreeMap builder ++= // for the moment we have to force immutability before the union diff --git a/library/src/scala/collection/immutable/TreeSeqMap.scala b/library/src/scala/collection/immutable/TreeSeqMap.scala index e3a7552c5934..e568841188dd 100644 --- a/library/src/scala/collection/immutable/TreeSeqMap.scala +++ b/library/src/scala/collection/immutable/TreeSeqMap.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.tailrec /** This class implements an immutable map that preserves order using @@ -59,7 +61,7 @@ final class TreeSeqMap[K, +V] private ( override protected[this] def className: String = "TreeSeqMap" - override def mapFactory: MapFactory[TreeSeqMap] = TreeSeqMap + override def mapFactory: StrictMapFactory[TreeSeqMap] = TreeSeqMap override val size = mapping.size @@ -234,7 +236,7 @@ final class TreeSeqMap[K, +V] private ( bdr.result() } - override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): TreeSeqMap[K2, V2] = { + override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]^): TreeSeqMap[K2, V2] = { val bdr = newBuilder[K2, V2](orderedBy) val iter = ordering.iterator while (iter.hasNext) { @@ -249,7 +251,7 @@ final class TreeSeqMap[K, +V] private ( bdr.result() } - override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]): TreeSeqMap[K2, V2] = { + override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]^): TreeSeqMap[K2, V2] = { val bdr = newBuilder[K2, V2](orderedBy) val iter = ordering.iterator while (iter.hasNext) { @@ -260,7 +262,7 @@ final class TreeSeqMap[K, +V] private ( bdr.result() } - override def concat[V2 >: V](suffix: IterableOnce[(K, V2)]): TreeSeqMap[K, V2] = { + override def concat[V2 >: V](suffix: IterableOnce[(K, V2)]^): TreeSeqMap[K, V2] = { var ong: Ordering[K] = ordering var mng: Mapping[K, V2] = mapping var ord = increment(ordinal) @@ -287,7 +289,7 @@ final class TreeSeqMap[K, +V] private ( @`inline` private[this] def value(p: (_, V)) = p._2 @`inline` private[this] def binding(k: K) = mapping(k).copy(_1 = k) } -object TreeSeqMap extends MapFactory[TreeSeqMap] { +object TreeSeqMap extends StrictMapFactory[TreeSeqMap] { sealed trait OrderBy object OrderBy { case object Insertion extends OrderBy @@ -303,7 +305,7 @@ object TreeSeqMap extends MapFactory[TreeSeqMap] { else EmptyByInsertion }.asInstanceOf[TreeSeqMap[K, V]] - def from[K, V](it: collection.IterableOnce[(K, V)]): TreeSeqMap[K, V] = + def from[K, V](it: collection.IterableOnce[(K, V)]^): TreeSeqMap[K, V] = it match { case om: TreeSeqMap[K, V] => om case _ => (newBuilder[K, V] ++= it).result() diff --git a/library/src/scala/collection/immutable/TreeSet.scala b/library/src/scala/collection/immutable/TreeSet.scala index a9bf1979700a..d30380b88101 100644 --- a/library/src/scala/collection/immutable/TreeSet.scala +++ b/library/src/scala/collection/immutable/TreeSet.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.Stepper.EfficientSplit import scala.collection.generic.DefaultSerializable import scala.collection.mutable.ReusableBuilder @@ -171,7 +173,7 @@ final class TreeSet[A] private[immutable] (private[immutable] val tree: RB.Tree[ def excl(elem: A): TreeSet[A] = newSetOrSelf(RB.delete(tree, elem)) - override def concat(that: collection.IterableOnce[A]): TreeSet[A] = { + override def concat(that: collection.IterableOnce[A]^): TreeSet[A] = { val t = that match { case ts: TreeSet[A] if ordering == ts.ordering => RB.union(tree, ts.tree) @@ -184,7 +186,7 @@ final class TreeSet[A] private[immutable] (private[immutable] val tree: RB.Tree[ newSetOrSelf(t) } - override def removedAll(that: IterableOnce[A]): TreeSet[A] = that match { + override def removedAll(that: IterableOnce[A]^): TreeSet[A] = that match { case ts: TreeSet[A] if ordering == ts.ordering => newSetOrSelf(RB.difference(tree, ts.tree)) case _ => @@ -240,7 +242,7 @@ object TreeSet extends SortedIterableFactory[TreeSet] { def empty[A: Ordering]: TreeSet[A] = new TreeSet[A] - def from[E](it: scala.collection.IterableOnce[E])(implicit ordering: Ordering[E]): TreeSet[E] = + def from[E](it: scala.collection.IterableOnce[E]^)(implicit ordering: Ordering[E]): TreeSet[E] = it match { case ts: TreeSet[E] if ordering == ts.ordering => ts case ss: scala.collection.SortedSet[E] if ordering == ss.ordering => @@ -270,7 +272,7 @@ object TreeSet extends SortedIterableFactory[TreeSet] { this } - override def addAll(xs: IterableOnce[A]): this.type = { + override def addAll(xs: IterableOnce[A]^): this.type = { xs match { // TODO consider writing a mutable-safe union for TreeSet/TreeMap builder ++= // for the moment we have to force immutability before the union diff --git a/library/src/scala/collection/immutable/Vector.scala b/library/src/scala/collection/immutable/Vector.scala index 4ea962fc8fa9..78768577b99b 100644 --- a/library/src/scala/collection/immutable/Vector.scala +++ b/library/src/scala/collection/immutable/Vector.scala @@ -14,6 +14,8 @@ package scala.collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import java.lang.Math.{abs, max => mmax, min => mmin} import java.util.Arrays.{copyOf, copyOfRange} import java.util.{Arrays, Spliterator} @@ -36,7 +38,7 @@ object Vector extends StrictOptimizedSeqFactory[Vector] { def empty[A]: Vector[A] = Vector0 - def from[E](it: collection.IterableOnce[E]): Vector[E] = + def from[E](it: collection.IterableOnce[E]^): Vector[E] = it match { case v: Vector[E] => v case _ => @@ -195,21 +197,21 @@ sealed abstract class Vector[+A] private[immutable] (private[immutable] final va override def updated[B >: A](index: Int, elem: B): Vector[B] = super.updated(index, elem) override def appended[B >: A](elem: B): Vector[B] = super.appended(elem) override def prepended[B >: A](elem: B): Vector[B] = super.prepended(elem) - override def prependedAll[B >: A](prefix: collection.IterableOnce[B]): Vector[B] = { + override def prependedAll[B >: A](prefix: collection.IterableOnce[B]^): Vector[B] = { val k = prefix.knownSize if (k == 0) this else if (k < 0) super.prependedAll(prefix) else prependedAll0(prefix, k) } - override final def appendedAll[B >: A](suffix: collection.IterableOnce[B]): Vector[B] = { + override final def appendedAll[B >: A](suffix: collection.IterableOnce[B]^): Vector[B] = { val k = suffix.knownSize if (k == 0) this else if (k < 0) super.appendedAll(suffix) else appendedAll0(suffix, k) } - protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = { + protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B]^, k: Int): Vector[B] = { // k >= 0, k = prefix.knownSize val tinyAppendLimit = 4 + vectorSliceCount if (k < tinyAppendLimit /*|| k < (this.size >>> Log2ConcatFaster)*/) { @@ -227,7 +229,7 @@ sealed abstract class Vector[+A] private[immutable] (private[immutable] final va } else super.prependedAll(prefix) } - protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B]^, k: Int): Vector[B] = { // k >= 0, k = suffix.knownSize val tinyAppendLimit = 4 + vectorSliceCount if (k < tinyAppendLimit) { @@ -373,10 +375,10 @@ private object Vector0 extends BigVector[Nothing](empty1, empty1, 0) { } } - override protected[this]def prependedAll0[B >: Nothing](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + override protected[this]def prependedAll0[B >: Nothing](prefix: collection.IterableOnce[B]^, k: Int): Vector[B] = Vector.from(prefix) - override protected[this]def appendedAll0[B >: Nothing](suffix: collection.IterableOnce[B], k: Int): Vector[B] = + override protected[this]def appendedAll0[B >: Nothing](suffix: collection.IterableOnce[B]^, k: Int): Vector[B] = Vector.from(suffix) override protected[this] def ioob(index: Int): IndexOutOfBoundsException = @@ -427,13 +429,13 @@ private final class Vector1[+A](_data1: Arr1) extends VectorImpl[A](_data1) { protected[immutable] def vectorSlice(idx: Int): Array[_ <: AnyRef] = prefix1 protected[immutable] def vectorSlicePrefixLength(idx: Int): Int = prefix1.length - override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B]^, k: Int): Vector[B] = prepend1IfSpace(prefix1, prefix) match { case null => super.prependedAll0(prefix, k) case data1b => new Vector1(data1b) } - override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B]^, k: Int): Vector[B] = { val data1b = append1IfSpace(prefix1, suffix) if(data1b ne null) new Vector1(data1b) else super.appendedAll0(suffix, k) @@ -522,7 +524,7 @@ private final class Vector2[+A](_prefix1: Arr1, private[immutable] val len1: Int case 2 => length0 } - override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B]^, k: Int): Vector[B] = prepend1IfSpace(prefix1, prefix) match { case null => super.prependedAll0(prefix, k) case prefix1b => @@ -533,7 +535,7 @@ private final class Vector2[+A](_prefix1: Arr1, private[immutable] val len1: Int ) } - override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B]^, k: Int): Vector[B] = { val suffix1b = append1IfSpace(suffix1, suffix) if(suffix1b ne null) copy(suffix1 = suffix1b, length0 = length0-suffix1.length+suffix1b.length) else super.appendedAll0(suffix, k) @@ -644,7 +646,7 @@ private final class Vector3[+A](_prefix1: Arr1, private[immutable] val len1: Int case 4 => length0 } - override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B]^, k: Int): Vector[B] = prepend1IfSpace(prefix1, prefix) match { case null => super.prependedAll0(prefix, k) case prefix1b => @@ -656,7 +658,7 @@ private final class Vector3[+A](_prefix1: Arr1, private[immutable] val len1: Int ) } - override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B]^, k: Int): Vector[B] = { val suffix1b = append1IfSpace(suffix1, suffix) if(suffix1b ne null) copy(suffix1 = suffix1b, length0 = length0-suffix1.length+suffix1b.length) else super.appendedAll0(suffix, k) @@ -787,7 +789,7 @@ private final class Vector4[+A](_prefix1: Arr1, private[immutable] val len1: Int case 6 => length0 } - override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B]^, k: Int): Vector[B] = prepend1IfSpace(prefix1, prefix) match { case null => super.prependedAll0(prefix, k) case prefix1b => @@ -800,7 +802,7 @@ private final class Vector4[+A](_prefix1: Arr1, private[immutable] val len1: Int ) } - override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B]^, k: Int): Vector[B] = { val suffix1b = append1IfSpace(suffix1, suffix) if(suffix1b ne null) copy(suffix1 = suffix1b, length0 = length0-suffix1.length+suffix1b.length) else super.appendedAll0(suffix, k) @@ -951,7 +953,7 @@ private final class Vector5[+A](_prefix1: Arr1, private[immutable] val len1: Int case 8 => length0 } - override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B]^, k: Int): Vector[B] = prepend1IfSpace(prefix1, prefix) match { case null => super.prependedAll0(prefix, k) case prefix1b => @@ -965,7 +967,7 @@ private final class Vector5[+A](_prefix1: Arr1, private[immutable] val len1: Int ) } - override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B]^, k: Int): Vector[B] = { val suffix1b = append1IfSpace(suffix1, suffix) if(suffix1b ne null) copy(suffix1 = suffix1b, length0 = length0-suffix1.length+suffix1b.length) else super.appendedAll0(suffix, k) @@ -1136,7 +1138,7 @@ private final class Vector6[+A](_prefix1: Arr1, private[immutable] val len1: Int case 10 => length0 } - override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = + override protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B]^, k: Int): Vector[B] = prepend1IfSpace(prefix1, prefix) match { case null => super.prependedAll0(prefix, k) case prefix1b => @@ -1151,7 +1153,7 @@ private final class Vector6[+A](_prefix1: Arr1, private[immutable] val len1: Int ) } - override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + override protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B]^, k: Int): Vector[B] = { val suffix1b = append1IfSpace(suffix1, suffix) if(suffix1b ne null) copy(suffix1 = suffix1b, length0 = length0-suffix1.length+suffix1b.length) else super.appendedAll0(suffix, k) @@ -1818,7 +1820,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { this } - override def addAll(xs: IterableOnce[A]): this.type = xs match { + override def addAll(xs: IterableOnce[A]^): this.type = xs match { case v: Vector[_] => if(len1 == 0 && lenRest == 0 && !prefixIsRightAligned) initFrom(v) else addVector(v.asInstanceOf[Vector[A]]) @@ -2187,7 +2189,7 @@ private object VectorStatics { ac.asInstanceOf[Array[T]] } - final def prepend1IfSpace(prefix1: Arr1, xs: IterableOnce[_]): Arr1 = xs match { + final def prepend1IfSpace(prefix1: Arr1, xs: IterableOnce[_]^): Arr1 = xs match { case it: Iterable[_] => if(it.sizeCompare(WIDTH-prefix1.length) <= 0) { it.size match { @@ -2212,7 +2214,7 @@ private object VectorStatics { } else null } - final def append1IfSpace(suffix1: Arr1, xs: IterableOnce[_]): Arr1 = xs match { + final def append1IfSpace(suffix1: Arr1, xs: IterableOnce[_]^): Arr1 = xs match { case it: Iterable[_] => if(it.sizeCompare(WIDTH-suffix1.length) <= 0) { it.size match { diff --git a/library/src/scala/collection/immutable/VectorMap.scala b/library/src/scala/collection/immutable/VectorMap.scala index 316d02037ec0..5a89b90e8ac2 100644 --- a/library/src/scala/collection/immutable/VectorMap.scala +++ b/library/src/scala/collection/immutable/VectorMap.scala @@ -15,6 +15,8 @@ package collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.annotation.{nowarn, tailrec} /** This class implements immutable maps using a vector/map-based data structure, which preserves insertion order. @@ -58,7 +60,7 @@ final class VectorMap[K, +V] private ( } } - override def withDefault[V1 >: V](d: K => V1): Map[K, V1] = + override def withDefault[V1 >: V](d: K -> V1): Map[K, V1] = new Map.WithDefault(this, d) override def withDefaultValue[V1 >: V](d: V1): Map[K, V1] = @@ -166,7 +168,7 @@ final class VectorMap[K, +V] private ( } } - override def mapFactory: MapFactory[VectorMap] = VectorMap + override def mapFactory: StrictMapFactory[VectorMap] = VectorMap override def contains(key: K): Boolean = underlying.contains(key) @@ -219,7 +221,7 @@ final class VectorMap[K, +V] private ( } } -object VectorMap extends MapFactory[VectorMap] { +object VectorMap extends StrictMapFactory[VectorMap] { //Class to mark deleted slots in 'fields'. //When one or more consecutive slots are deleted, the 'distance' of the first 'Tombstone' // represents the distance to the location of the next undeleted slot (or the last slot in 'fields' +1 if it does not exist). @@ -233,7 +235,7 @@ object VectorMap extends MapFactory[VectorMap] { def empty[K, V]: VectorMap[K, V] = EmptyMap.asInstanceOf[VectorMap[K, V]] - def from[K, V](it: collection.IterableOnce[(K, V)]): VectorMap[K, V] = + def from[K, V](it: collection.IterableOnce[(K, V)]^): VectorMap[K, V] = it match { case vm: VectorMap[K, V] => vm case _ => (newBuilder[K, V] ++= it).result() diff --git a/library/src/scala/collection/immutable/WrappedString.scala b/library/src/scala/collection/immutable/WrappedString.scala index 76d69c092673..9c88584f8feb 100644 --- a/library/src/scala/collection/immutable/WrappedString.scala +++ b/library/src/scala/collection/immutable/WrappedString.scala @@ -14,6 +14,8 @@ package scala.collection package immutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.Predef.{wrapString => _, assert} import scala.collection.Stepper.EfficientSplit import scala.collection.convert.impl.CharStringStepper @@ -34,12 +36,12 @@ import scala.collection.mutable.{Builder, StringBuilder} */ @SerialVersionUID(3L) final class WrappedString(private val self: String) extends AbstractSeq[Char] with IndexedSeq[Char] - with IndexedSeqOps[Char, IndexedSeq, WrappedString] + with StrictIndexedSeqOps[Char, IndexedSeq, WrappedString] with Serializable { def apply(i: Int): Char = self.charAt(i) - override protected def fromSpecific(coll: scala.collection.IterableOnce[Char]): WrappedString = WrappedString.fromSpecific(coll) + override protected def fromSpecific(coll: scala.collection.IterableOnce[Char]^): WrappedString = WrappedString.fromSpecific(coll) override protected def newSpecificBuilder: Builder[Char, WrappedString] = WrappedString.newBuilder override def empty: WrappedString = WrappedString.empty @@ -66,13 +68,13 @@ final class WrappedString(private val self: String) extends AbstractSeq[Char] wi r.asInstanceOf[S with EfficientSplit] } - override def startsWith[B >: Char](that: IterableOnce[B], offset: Int = 0): Boolean = + override def startsWith[B >: Char](that: IterableOnce[B]^, offset: Int = 0): Boolean = that match { case s: WrappedString => self.startsWith(s.self, offset) case _ => super.startsWith(that, offset) } - override def endsWith[B >: Char](that: collection.Iterable[B]): Boolean = + override def endsWith[B >: Char](that: collection.Iterable[B]^): Boolean = that match { case s: WrappedString => self.endsWith(s.self) case _ => super.endsWith(that) @@ -98,13 +100,13 @@ final class WrappedString(private val self: String) extends AbstractSeq[Char] wi case _ => super.copyToArray(xs, start, len) } - override def appendedAll[B >: Char](suffix: IterableOnce[B]): IndexedSeq[B] = + override def appendedAll[B >: Char](suffix: IterableOnce[B]^): IndexedSeq[B] = suffix match { case s: WrappedString => new WrappedString(self concat s.self) - case _ => super.appendedAll(suffix) + case _ => Predef.??? // TODO super.appendedAll(suffix) } - override def sameElements[B >: Char](o: IterableOnce[B]) = o match { + override def sameElements[B >: Char](o: IterableOnce[B]^) = o match { case s: WrappedString => self == s.self case _ => super.sameElements(o) } @@ -124,7 +126,7 @@ final class WrappedString(private val self: String) extends AbstractSeq[Char] wi */ @SerialVersionUID(3L) object WrappedString extends SpecificIterableFactory[Char, WrappedString] { - def fromSpecific(it: IterableOnce[Char]): WrappedString = { + def fromSpecific(it: IterableOnce[Char]^): WrappedString = { val b = newBuilder b.sizeHint(it) b ++= it diff --git a/library/src/scala/collection/immutable/package.scala b/library/src/scala/collection/immutable/package.scala index 061619cb8b86..5ddc1810d701 100644 --- a/library/src/scala/collection/immutable/package.scala +++ b/library/src/scala/collection/immutable/package.scala @@ -13,6 +13,7 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking package object immutable { type StringOps = scala.collection.StringOps diff --git a/library/src/scala/collection/mutable/AnyRefMap.scala b/library/src/scala/collection/mutable/AnyRefMap.scala index 3caccdbc194d..984fb1ff781e 100644 --- a/library/src/scala/collection/mutable/AnyRefMap.scala +++ b/library/src/scala/collection/mutable/AnyRefMap.scala @@ -15,6 +15,7 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.meta.companionClass import scala.annotation.nowarn import scala.collection.generic.DefaultSerializationProxy @@ -44,7 +45,7 @@ import scala.language.implicitConversions * */ @(deprecated @companionClass)("Use `scala.collection.mutable.HashMap` instead for better performance.", since = "2.13.16") -class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K => V, initialBufferSize: Int, initBlank: Boolean) +class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K -> V, initialBufferSize: Int, initBlank: Boolean) extends AbstractMap[K, V] with MapOps[K, V, Map, AnyRefMap[K, V]] with StrictOptimizedIterableOps[(K, V), Iterable, AnyRefMap[K, V]] @@ -54,7 +55,7 @@ class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K => V, initi def this() = this(AnyRefMap.exceptionDefault, 16, initBlank = true) /** Creates a new `AnyRefMap` that returns default values according to a supplied key-value mapping. */ - def this(defaultEntry: K => V) = this(defaultEntry, 16, initBlank = true) + def this(defaultEntry: K -> V) = this(defaultEntry, 16, initBlank = true) /** Creates a new `AnyRefMap` with an initial buffer of specified size. * @@ -64,7 +65,7 @@ class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K => V, initi def this(initialBufferSize: Int) = this(AnyRefMap.exceptionDefault, initialBufferSize, initBlank = true) /** Creates a new `AnyRefMap` with specified default values and initial buffer size. */ - def this(defaultEntry: K => V, initialBufferSize: Int) = this(defaultEntry, initialBufferSize, initBlank = true) + def this(defaultEntry: K -> V, initialBufferSize: Int) = this(defaultEntry, initialBufferSize, initBlank = true) private[this] var mask = 0 private[this] var _size = 0 @@ -90,7 +91,7 @@ class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K => V, initi mask = m; _size = sz; _vacant = vc; _hashes = hz; _keys = kz; _values = vz } - override protected def fromSpecific(coll: scala.collection.IterableOnce[(K, V)]): AnyRefMap[K,V] = { + override protected def fromSpecific(coll: scala.collection.IterableOnce[(K, V)]^): AnyRefMap[K,V] = { var sz = coll.knownSize if(sz < 0) sz = 4 val arm = new AnyRefMap[K, V](sz * 2) @@ -403,18 +404,19 @@ class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K => V, initi override def + [V1 >: V](kv: (K, V1)): AnyRefMap[K, V1] = AnyRefMap.from(new View.Appended(this, kv)) @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") - override def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): AnyRefMap[K, V1] = { + override def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): AnyRefMap[K, V1]^{} = { + // An empty capture annotation is needed in the result type to satisfy the overriding checker. val m = this + elem1 + elem2 if(elems.isEmpty) m else m.concat(elems) } - override def concat[V2 >: V](xs: scala.collection.IterableOnce[(K, V2)]): AnyRefMap[K, V2] = { + override def concat[V2 >: V](xs: scala.collection.IterableOnce[(K, V2)]^): AnyRefMap[K, V2] = { val arm = clone().asInstanceOf[AnyRefMap[K, V2]] xs.iterator.foreach(kv => arm += kv) arm } - override def ++[V2 >: V](xs: scala.collection.IterableOnce[(K, V2)]): AnyRefMap[K, V2] = concat(xs) + override def ++[V2 >: V](xs: scala.collection.IterableOnce[(K, V2)]^): AnyRefMap[K, V2] = concat(xs) @deprecated("Use m.clone().addOne(k,v) instead of m.updated(k, v)", "2.13.0") override def updated[V1 >: V](key: K, value: V1): AnyRefMap[K, V1] = @@ -498,7 +500,7 @@ class AnyRefMap[K <: AnyRef, V] private[collection] (defaultEntry: K => V, initi * @param f the mapping function must produce key-value pairs where the key is an `AnyRef` * @param dummy an implicit placeholder for purposes of distinguishing the (erased) signature of this method */ - def flatMap[K2 <: AnyRef, V2](f: ((K, V)) => IterableOnce[(K2, V2)])(implicit dummy: DummyImplicit): AnyRefMap[K2, V2] = + def flatMap[K2 <: AnyRef, V2](f: ((K, V)) => IterableOnce[(K2, V2)]^)(implicit dummy: DummyImplicit): AnyRefMap[K2, V2] = AnyRefMap.from(new View.FlatMap(this, f)) /** * An overload of `collect` which produces an `AnyRefMap`. @@ -556,7 +558,7 @@ object AnyRefMap { def newBuilder[K <: AnyRef, V]: ReusableBuilder[(K, V), AnyRefMap[K, V]] = new AnyRefMapBuilder[K, V] - private def buildFromIterableOnce[K <: AnyRef, V](elems: IterableOnce[(K, V)]): AnyRefMap[K, V] = { + private def buildFromIterableOnce[K <: AnyRef, V](elems: IterableOnce[(K, V)]^): AnyRefMap[K, V] = { var sz = elems.knownSize if(sz < 0) sz = 4 val arm = new AnyRefMap[K, V](sz * 2) @@ -569,7 +571,7 @@ object AnyRefMap { def empty[K <: AnyRef, V]: AnyRefMap[K, V] = new AnyRefMap[K, V] /** Creates a new empty `AnyRefMap` with the supplied default */ - def withDefault[K <: AnyRef, V](default: K => V): AnyRefMap[K, V] = new AnyRefMap[K, V](default) + def withDefault[K <: AnyRef, V](default: K -> V): AnyRefMap[K, V] = new AnyRefMap[K, V](default) /** Creates a new `AnyRefMap` from an existing source collection. A source collection * which is already an `AnyRefMap` gets cloned. @@ -579,7 +581,7 @@ object AnyRefMap { * @tparam V the type of the values * @return a new `AnyRefMap` with the elements of `source` */ - def from[K <: AnyRef, V](source: IterableOnce[(K, V)]): AnyRefMap[K, V] = source match { + def from[K <: AnyRef, V](source: IterableOnce[(K, V)]^): AnyRefMap[K, V] = source match { case source: AnyRefMap[_, _] => source.clone().asInstanceOf[AnyRefMap[K, V]] case _ => buildFromIterableOnce(source) } @@ -613,13 +615,13 @@ object AnyRefMap { @SerialVersionUID(3L) private[this] object ToFactory extends Factory[(AnyRef, AnyRef), AnyRefMap[AnyRef, AnyRef]] with Serializable { - def fromSpecific(it: IterableOnce[(AnyRef, AnyRef)]): AnyRefMap[AnyRef, AnyRef] = AnyRefMap.from[AnyRef, AnyRef](it) + def fromSpecific(it: IterableOnce[(AnyRef, AnyRef)]^): AnyRefMap[AnyRef, AnyRef] = AnyRefMap.from[AnyRef, AnyRef](it) def newBuilder: Builder[(AnyRef, AnyRef), AnyRefMap[AnyRef, AnyRef]] = AnyRefMap.newBuilder[AnyRef, AnyRef] } implicit def toBuildFrom[K <: AnyRef, V](factory: AnyRefMap.type): BuildFrom[Any, (K, V), AnyRefMap[K, V]] = ToBuildFrom.asInstanceOf[BuildFrom[Any, (K, V), AnyRefMap[K, V]]] private[this] object ToBuildFrom extends BuildFrom[Any, (AnyRef, AnyRef), AnyRefMap[AnyRef, AnyRef]] { - def fromSpecific(from: Any)(it: IterableOnce[(AnyRef, AnyRef)]): AnyRefMap[AnyRef, AnyRef] = AnyRefMap.from(it) + def fromSpecific(from: Any)(it: IterableOnce[(AnyRef, AnyRef)]^): AnyRefMap[AnyRef, AnyRef] = AnyRefMap.from(it) def newBuilder(from: Any): ReusableBuilder[(AnyRef, AnyRef), AnyRefMap[AnyRef, AnyRef]] = AnyRefMap.newBuilder[AnyRef, AnyRef] } diff --git a/library/src/scala/collection/mutable/ArrayBuffer.scala b/library/src/scala/collection/mutable/ArrayBuffer.scala index 72384e3f65a2..b8fa9c806e5e 100644 --- a/library/src/scala/collection/mutable/ArrayBuffer.scala +++ b/library/src/scala/collection/mutable/ArrayBuffer.scala @@ -15,6 +15,7 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import java.util.Arrays import scala.annotation.{nowarn, tailrec} import scala.collection.Stepper.EfficientSplit @@ -147,7 +148,7 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int) } // Overridden to use array copying for efficiency where possible. - override def addAll(elems: IterableOnce[A]): this.type = { + override def addAll(elems: IterableOnce[A]^): this.type = { elems match { case elems: ArrayBuffer[_] => val elemsLength = elems.size0 @@ -176,7 +177,7 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int) this } - def insertAll(@deprecatedName("n", "2.13.0") index: Int, elems: IterableOnce[A]): Unit = { + def insertAll(@deprecatedName("n", "2.13.0") index: Int, elems: IterableOnce[A]^): Unit = { checkWithinBounds(index, index) elems match { case elems: collection.Iterable[A] => @@ -230,7 +231,7 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int) @deprecated("Use 'new GrowableBuilder(this).mapResult(f)' instead", "2.13.0") @deprecatedOverriding("ArrayBuffer[A] no longer extends Builder[A, ArrayBuffer[A]]", "2.13.0") - @inline def mapResult[NewTo](f: (ArrayBuffer[A]) => NewTo): Builder[A, NewTo] = new GrowableBuilder[A, ArrayBuffer[A]](this).mapResult(f) + @inline def mapResult[NewTo](f: (ArrayBuffer[A]) => NewTo): Builder[A, NewTo]^{f} = new GrowableBuilder[A, ArrayBuffer[A]](this).mapResult(f) @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") override protected[this] def stringPrefix = "ArrayBuffer" @@ -294,7 +295,7 @@ object ArrayBuffer extends StrictOptimizedSeqFactory[ArrayBuffer] { final val DefaultInitialSize = 16 private[this] val emptyArray = new Array[AnyRef](0) - def from[B](coll: collection.IterableOnce[B]): ArrayBuffer[B] = { + def from[B](coll: collection.IterableOnce[B]^): ArrayBuffer[B] = { val k = coll.knownSize if (k >= 0) { // Avoid reallocation of buffer if length is known @@ -372,10 +373,11 @@ final class ArrayBufferView[A] private[mutable](underlying: ArrayBuffer[A], muta this({ val _array = array val _length = length - new ArrayBuffer[A](0) { + val buf = new ArrayBuffer[A](0) { this.array = _array this.size0 = _length } + buf }, () => 0) } @@ -388,21 +390,21 @@ final class ArrayBufferView[A] private[mutable](underlying: ArrayBuffer[A], muta override protected[this] def className = "ArrayBufferView" // we could inherit all these from `CheckedIndexedSeqView`, except this class is public - override def iterator: Iterator[A] = new CheckedIndexedSeqView.CheckedIterator(this, mutationCount()) - override def reverseIterator: Iterator[A] = new CheckedIndexedSeqView.CheckedReverseIterator(this, mutationCount()) - - override def appended[B >: A](elem: B): IndexedSeqView[B] = new CheckedIndexedSeqView.Appended(this, elem)(mutationCount) - override def prepended[B >: A](elem: B): IndexedSeqView[B] = new CheckedIndexedSeqView.Prepended(elem, this)(mutationCount) - override def take(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Take(this, n)(mutationCount) - override def takeRight(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.TakeRight(this, n)(mutationCount) - override def drop(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Drop(this, n)(mutationCount) - override def dropRight(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.DropRight(this, n)(mutationCount) - override def map[B](f: A => B): IndexedSeqView[B] = new CheckedIndexedSeqView.Map(this, f)(mutationCount) - override def reverse: IndexedSeqView[A] = new CheckedIndexedSeqView.Reverse(this)(mutationCount) - override def slice(from: Int, until: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Slice(this, from, until)(mutationCount) - override def tapEach[U](f: A => U): IndexedSeqView[A] = new CheckedIndexedSeqView.Map(this, { (a: A) => f(a); a})(mutationCount) - - override def concat[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount) - override def appendedAll[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount) - override def prependedAll[B >: A](prefix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(prefix, this)(mutationCount) + override def iterator: Iterator[A]^{this} = new CheckedIndexedSeqView.CheckedIterator(this, mutationCount()) + override def reverseIterator: Iterator[A]^{this} = new CheckedIndexedSeqView.CheckedReverseIterator(this, mutationCount()) + + override def appended[B >: A](elem: B): IndexedSeqView[B]^{this} = new CheckedIndexedSeqView.Appended(this, elem)(mutationCount) + override def prepended[B >: A](elem: B): IndexedSeqView[B]^{this} = new CheckedIndexedSeqView.Prepended(elem, this)(mutationCount) + override def take(n: Int): IndexedSeqView[A]^{this} = new CheckedIndexedSeqView.Take(this, n)(mutationCount) + override def takeRight(n: Int): IndexedSeqView[A]^{this} = new CheckedIndexedSeqView.TakeRight(this, n)(mutationCount) + override def drop(n: Int): IndexedSeqView[A]^{this} = new CheckedIndexedSeqView.Drop(this, n)(mutationCount) + override def dropRight(n: Int): IndexedSeqView[A]^{this} = new CheckedIndexedSeqView.DropRight(this, n)(mutationCount) + override def map[B](f: A => B): IndexedSeqView[B]^{this, f} = new CheckedIndexedSeqView.Map(this, f)(mutationCount) + override def reverse: IndexedSeqView[A]^{this} = new CheckedIndexedSeqView.Reverse(this)(mutationCount) + override def slice(from: Int, until: Int): IndexedSeqView[A]^{this} = new CheckedIndexedSeqView.Slice(this, from, until)(mutationCount) + override def tapEach[U](f: A => U): IndexedSeqView[A]^{this, f} = new CheckedIndexedSeqView.Map(this, { (a: A) => f(a); a})(mutationCount) + + override def concat[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]^): IndexedSeqView[B]^{suffix, this} = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount) + override def appendedAll[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]^): IndexedSeqView[B]^{suffix, this} = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount) + override def prependedAll[B >: A](prefix: IndexedSeqView.SomeIndexedSeqOps[B]^): IndexedSeqView[B]^{prefix, this} = new CheckedIndexedSeqView.Concat(prefix, this)(mutationCount) } diff --git a/library/src/scala/collection/mutable/ArrayBuilder.scala b/library/src/scala/collection/mutable/ArrayBuilder.scala index c6bd34a4001c..608fb85564a3 100644 --- a/library/src/scala/collection/mutable/ArrayBuilder.scala +++ b/library/src/scala/collection/mutable/ArrayBuilder.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.collection.mutable.ArrayBuffer.resizeUp import scala.reflect.ClassTag @@ -66,7 +67,7 @@ sealed abstract class ArrayBuilder[T] this } - override def addAll(xs: IterableOnce[T]): this.type = { + override def addAll(xs: IterableOnce[T]^): this.type = { val k = xs.knownSize if (k > 0) { ensureSize(this.size + k) @@ -452,6 +453,7 @@ object ArrayBuilder { /** A class for array builders for arrays of `boolean`s. It can be reused. */ @SerialVersionUID(3L) class ofBoolean extends ArrayBuilder[Boolean] { + this: ofBoolean^{} => protected var elems: Array[Boolean] = _ @@ -504,7 +506,7 @@ object ArrayBuilder { this } - override def addAll(xs: IterableOnce[Unit]): this.type = { + override def addAll(xs: IterableOnce[Unit]^): this.type = { val newSize = size + xs.iterator.size ensureSize(newSize) size = newSize diff --git a/library/src/scala/collection/mutable/ArrayDeque.scala b/library/src/scala/collection/mutable/ArrayDeque.scala index 186df5b21c74..e395a651778c 100644 --- a/library/src/scala/collection/mutable/ArrayDeque.scala +++ b/library/src/scala/collection/mutable/ArrayDeque.scala @@ -15,6 +15,7 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.nowarn import scala.collection.Stepper.EfficientSplit import scala.collection.generic.{CommonErrors, DefaultSerializable} @@ -100,7 +101,7 @@ class ArrayDeque[A] protected ( this } - override def prependAll(elems: IterableOnce[A]): this.type = { + override def prependAll(elems: IterableOnce[A]^): this.type = { val it = elems.iterator if (it.nonEmpty) { val n = length @@ -132,7 +133,7 @@ class ArrayDeque[A] protected ( this } - override def addAll(elems: IterableOnce[A]): this.type = { + override def addAll(elems: IterableOnce[A]^): this.type = { elems.knownSize match { case srcLength if srcLength > 0 => ensureSize(srcLength + length) @@ -178,7 +179,7 @@ class ArrayDeque[A] protected ( } } - def insertAll(idx: Int, elems: IterableOnce[A]): Unit = { + def insertAll(idx: Int, elems: IterableOnce[A]^): Unit = { requireBounds(idx, length+1) val n = length if (idx == 0) { @@ -528,7 +529,7 @@ class ArrayDeque[A] protected ( @SerialVersionUID(3L) object ArrayDeque extends StrictOptimizedSeqFactory[ArrayDeque] { - def from[B](coll: collection.IterableOnce[B]): ArrayDeque[B] = { + def from[B](coll: collection.IterableOnce[B]^): ArrayDeque[B] = { val s = coll.knownSize if (s >= 0) { val array = alloc(s) @@ -569,7 +570,7 @@ object ArrayDeque extends StrictOptimizedSeqFactory[ArrayDeque] { } } -transparent trait ArrayDequeOps[A, +CC[_], +C <: AnyRef] extends StrictOptimizedSeqOps[A, CC, C] { +transparent trait ArrayDequeOps[A, +CC[_] <: caps.Pure, +C <: AnyRef] extends StrictOptimizedSeqOps[A, CC, C] { protected def array: Array[AnyRef] final override def clone(): C = klone() diff --git a/library/src/scala/collection/mutable/ArraySeq.scala b/library/src/scala/collection/mutable/ArraySeq.scala index a6af2be04fff..53afc6cf66be 100644 --- a/library/src/scala/collection/mutable/ArraySeq.scala +++ b/library/src/scala/collection/mutable/ArraySeq.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import java.util.Arrays import scala.collection.Stepper.EfficientSplit import scala.collection.convert.impl._ @@ -43,7 +44,7 @@ sealed abstract class ArraySeq[T] override def iterableFactory: scala.collection.SeqFactory[ArraySeq] = ArraySeq.untagged - override protected def fromSpecific(coll: scala.collection.IterableOnce[T]): ArraySeq[T] = { + override protected def fromSpecific(coll: scala.collection.IterableOnce[T]^): ArraySeq[T] = { val b = ArrayBuilder.make(using elemTag).asInstanceOf[ArrayBuilder[T]] b.sizeHint(coll, delta = 0) b ++= coll @@ -106,7 +107,7 @@ object ArraySeq extends StrictOptimizedClassTagSeqFactory[ArraySeq] { self => private[this] val EmptyArraySeq = new ofRef[AnyRef](new Array[AnyRef](0)) def empty[T : ClassTag]: ArraySeq[T] = EmptyArraySeq.asInstanceOf[ArraySeq[T]] - def from[A : ClassTag](it: scala.collection.IterableOnce[A]): ArraySeq[A] = make(Array.from[A](it)) + def from[A : ClassTag](it: scala.collection.IterableOnce[A]^): ArraySeq[A] = make(Array.from[A](it)) def newBuilder[A : ClassTag]: Builder[A, ArraySeq[A]] = ArrayBuilder.make[A].mapResult(make) diff --git a/library/src/scala/collection/mutable/BitSet.scala b/library/src/scala/collection/mutable/BitSet.scala index 39124537758f..03f81bf64907 100644 --- a/library/src/scala/collection/mutable/BitSet.scala +++ b/library/src/scala/collection/mutable/BitSet.scala @@ -15,6 +15,8 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking + import scala.collection.immutable.Range import BitSetOps.{LogWL, MaxSize} import scala.annotation.implicitNotFound @@ -48,7 +50,7 @@ class BitSet(protected[collection] final var elems: Array[Long]) def this() = this(0) - override protected def fromSpecific(coll: IterableOnce[Int]): BitSet = bitSetFactory.fromSpecific(coll) + override protected def fromSpecific(coll: IterableOnce[Int]^): BitSet = bitSetFactory.fromSpecific(coll) override protected def newSpecificBuilder: Builder[Int, BitSet] = bitSetFactory.newBuilder override def empty: BitSet = bitSetFactory.empty @@ -176,19 +178,19 @@ class BitSet(protected[collection] final var elems: Array[Long]) override def map[B](f: Int => B)(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = super[StrictOptimizedSortedSetOps].map(f) - override def flatMap(f: Int => IterableOnce[Int]): BitSet = strictOptimizedFlatMap(newSpecificBuilder, f) - override def flatMap[B](f: Int => IterableOnce[B])(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = + override def flatMap(f: Int => IterableOnce[Int]^): BitSet = strictOptimizedFlatMap(newSpecificBuilder, f) + override def flatMap[B](f: Int => IterableOnce[B]^)(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = super[StrictOptimizedSortedSetOps].flatMap(f) - override def collect(pf: PartialFunction[Int, Int]): BitSet = strictOptimizedCollect(newSpecificBuilder, pf) - override def collect[B](pf: scala.PartialFunction[Int, B])(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = + override def collect(pf: PartialFunction[Int, Int]^): BitSet = strictOptimizedCollect(newSpecificBuilder, pf) + override def collect[B](pf: scala.PartialFunction[Int, B]^)(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] = super[StrictOptimizedSortedSetOps].collect(pf) // necessary for disambiguation - override def zip[B](that: IterableOnce[B])(implicit @implicitNotFound(collection.BitSet.zipOrdMsg) ev: Ordering[(Int, B)]): SortedSet[(Int, B)] = + override def zip[B](that: IterableOnce[B]^)(implicit @implicitNotFound(collection.BitSet.zipOrdMsg) ev: Ordering[(Int, B)]): SortedSet[(Int, B)] = super.zip(that) - override def addAll(xs: IterableOnce[Int]): this.type = xs match { + override def addAll(xs: IterableOnce[Int]^): this.type = xs match { case bs: collection.BitSet => this |= bs case range: Range => @@ -261,7 +263,7 @@ class BitSet(protected[collection] final var elems: Array[Long]) super.subsetOf(other) } - override def subtractAll(xs: IterableOnce[Int]): this.type = xs match { + override def subtractAll(xs: IterableOnce[Int]^): this.type = xs match { case bs: collection.BitSet => this &~= bs case other => super.subtractAll(other) } @@ -361,7 +363,7 @@ class BitSet(protected[collection] final var elems: Array[Long]) @SerialVersionUID(3L) object BitSet extends SpecificIterableFactory[Int, BitSet] { - def fromSpecific(it: scala.collection.IterableOnce[Int]): BitSet = Growable.from(empty, it) + def fromSpecific(it: scala.collection.IterableOnce[Int]^): BitSet = Growable.from(empty, it) def empty: BitSet = new BitSet() diff --git a/library/src/scala/collection/mutable/Buffer.scala b/library/src/scala/collection/mutable/Buffer.scala index 333d86fce772..bfed8dd0add4 100644 --- a/library/src/scala/collection/mutable/Buffer.scala +++ b/library/src/scala/collection/mutable/Buffer.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.nowarn @@ -27,7 +28,7 @@ trait Buffer[A] with SeqOps[A, Buffer, Buffer[A]] with Growable[A] with Shrinkable[A] - with IterableFactoryDefaults[A, Buffer] { + with IterableFactoryDefaults[A, Buffer] { self => override def iterableFactory: SeqFactory[Buffer] = Buffer @@ -55,7 +56,7 @@ trait Buffer[A] * @param elems the iterable object containing the elements to append. * @return this $coll */ - @`inline` final def appendAll(@deprecatedName("xs") elems: IterableOnce[A]): this.type = addAll(elems) + @`inline` final def appendAll(@deprecatedName("xs") elems: IterableOnce[A]^): this.type = addAll(elems) /** Alias for `prepend` */ @`inline` final def +=: (elem: A): this.type = prepend(elem) @@ -64,13 +65,13 @@ trait Buffer[A] * @param elems the iterable object containing the elements to append. * @return this $coll */ - def prependAll(elems: IterableOnce[A]): this.type = { insertAll(0, elems); this } + def prependAll(elems: IterableOnce[A]^): this.type = { insertAll(0, elems); this } @deprecated("Use prependAll instead", "2.13.0") @`inline` final def prepend(elems: A*): this.type = prependAll(elems) /** Alias for `prependAll` */ - @inline final def ++=:(elems: IterableOnce[A]): this.type = prependAll(elems) + @inline final def ++=:(elems: IterableOnce[A]^): this.type = prependAll(elems) /** Inserts a new element at a given index into this buffer. * @@ -91,7 +92,7 @@ trait Buffer[A] * @throws IndexOutOfBoundsException if `idx` is out of bounds. */ @throws[IndexOutOfBoundsException] - def insertAll(idx: Int, elems: IterableOnce[A]): Unit + def insertAll(idx: Int, elems: IterableOnce[A]^): Unit /** Removes the element at a given index position. * @@ -153,7 +154,7 @@ trait Buffer[A] * @param replaced the number of elements to drop in the original $coll * @return this $coll */ - def patchInPlace(from: Int, patch: scala.collection.IterableOnce[A], replaced: Int): this.type + def patchInPlace(from: Int, patch: scala.collection.IterableOnce[A]^, replaced: Int): this.type // +=, ++=, clear inherited from Growable // Per remark of @ichoran, we should preferably not have these: @@ -262,11 +263,11 @@ trait IndexedBuffer[A] extends IndexedSeq[A] * @param f the mapping function * @return this $coll */ - def flatMapInPlace(f: A => IterableOnce[A]): this.type = { + def flatMapInPlace(f: A => IterableOnce[A]^): this.type = { // There's scope for a better implementation which copies elements in place. var i = 0 val s = size - val newElems = new Array[IterableOnce[A]](s) + val newElems = new Array[IterableOnce[A]^{f}](s) while (i < s) { newElems(i) = f(this(i)); i += 1 } clear() i = 0 @@ -294,7 +295,7 @@ trait IndexedBuffer[A] extends IndexedSeq[A] if (i == j) this else takeInPlace(j) } - def patchInPlace(from: Int, patch: scala.collection.IterableOnce[A], replaced: Int): this.type = { + def patchInPlace(from: Int, patch: scala.collection.IterableOnce[A]^, replaced: Int): this.type = { val replaced0 = math.min(math.max(replaced, 0), length) val i = math.min(math.max(from, 0), length) var j = 0 diff --git a/library/src/scala/collection/mutable/Builder.scala b/library/src/scala/collection/mutable/Builder.scala index c545929fa33f..715ae02bc519 100644 --- a/library/src/scala/collection/mutable/Builder.scala +++ b/library/src/scala/collection/mutable/Builder.scala @@ -13,6 +13,7 @@ package scala.collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking /** Base trait for collection builders. * @@ -22,7 +23,7 @@ import scala.language.`2.13` * * @see [[scala.collection.mutable.ReusableBuilder]] for Builders which can be reused after calling `result()` */ -trait Builder[-A, +To] extends Growable[A] { self => +trait Builder[-A, +To] extends Growable[A] { self: Builder[A, To]^ => /** Clears the contents of this builder. * After execution of this method the builder will contain no elements. @@ -67,7 +68,7 @@ trait Builder[-A, +To] extends Growable[A] { self => * @param coll the collection which serves as a hint for the result's size. * @param delta a correction to add to the `coll.size` to produce the size hint (zero if omitted). */ - final def sizeHint(coll: scala.collection.IterableOnce[_], delta: Int = 0): Unit = + final def sizeHint(coll: scala.collection.IterableOnce[_]^, delta: Int = 0): Unit = coll.knownSize match { case -1 => case sz => sizeHint(0 max sz + delta) @@ -86,7 +87,7 @@ trait Builder[-A, +To] extends Growable[A] { self => * than collection's size are reduced. */ // should probably be `boundingColl: IterableOnce[_]`, but binary compatibility - final def sizeHintBounded(size: Int, boundingColl: scala.collection.Iterable[_]): Unit = { + final def sizeHintBounded(size: Int, boundingColl: scala.collection.Iterable[_]^): Unit = { val s = boundingColl.knownSize if (s != -1) { sizeHint(scala.math.min(s, size)) @@ -94,10 +95,10 @@ trait Builder[-A, +To] extends Growable[A] { self => } /** A builder resulting from this builder by mapping the result using `f`. */ - def mapResult[NewTo](f: To => NewTo): Builder[A, NewTo] = new Builder[A, NewTo] { + def mapResult[NewTo](f: To => NewTo): Builder[A, NewTo]^{this, f} = new Builder[A, NewTo] { def addOne(x: A): this.type = { self += x; this } def clear(): Unit = self.clear() - override def addAll(xs: IterableOnce[A]): this.type = { self ++= xs; this } + override def addAll(xs: IterableOnce[A]^): this.type = { self ++= xs; this } override def sizeHint(size: Int): Unit = self.sizeHint(size) def result(): NewTo = f(self.result()) override def knownSize: Int = self.knownSize diff --git a/library/src/scala/collection/mutable/CheckedIndexedSeqView.scala b/library/src/scala/collection/mutable/CheckedIndexedSeqView.scala index 720e8ce90b95..3c7afaf0b79f 100644 --- a/library/src/scala/collection/mutable/CheckedIndexedSeqView.scala +++ b/library/src/scala/collection/mutable/CheckedIndexedSeqView.scala @@ -15,34 +15,35 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking private[mutable] trait CheckedIndexedSeqView[+A] extends IndexedSeqView[A] { protected val mutationCount: () => Int - override def iterator: Iterator[A] = new CheckedIndexedSeqView.CheckedIterator(this, mutationCount()) - override def reverseIterator: Iterator[A] = new CheckedIndexedSeqView.CheckedReverseIterator(this, mutationCount()) - - override def appended[B >: A](elem: B): IndexedSeqView[B] = new CheckedIndexedSeqView.Appended(this, elem)(mutationCount) - override def prepended[B >: A](elem: B): IndexedSeqView[B] = new CheckedIndexedSeqView.Prepended(elem, this)(mutationCount) - override def take(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Take(this, n)(mutationCount) - override def takeRight(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.TakeRight(this, n)(mutationCount) - override def drop(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Drop(this, n)(mutationCount) - override def dropRight(n: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.DropRight(this, n)(mutationCount) - override def map[B](f: A => B): IndexedSeqView[B] = new CheckedIndexedSeqView.Map(this, f)(mutationCount) - override def reverse: IndexedSeqView[A] = new CheckedIndexedSeqView.Reverse(this)(mutationCount) - override def slice(from: Int, until: Int): IndexedSeqView[A] = new CheckedIndexedSeqView.Slice(this, from, until)(mutationCount) - override def tapEach[U](f: A => U): IndexedSeqView[A] = new CheckedIndexedSeqView.Map(this, { (a: A) => f(a); a})(mutationCount) - - override def concat[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount) - override def appendedAll[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount) - override def prependedAll[B >: A](prefix: IndexedSeqView.SomeIndexedSeqOps[B]): IndexedSeqView[B] = new CheckedIndexedSeqView.Concat(prefix, this)(mutationCount) + override def iterator: Iterator[A]^{this} = new CheckedIndexedSeqView.CheckedIterator(this, mutationCount()) + override def reverseIterator: Iterator[A]^{this} = new CheckedIndexedSeqView.CheckedReverseIterator(this, mutationCount()) + + override def appended[B >: A](elem: B): IndexedSeqView[B]^{this} = new CheckedIndexedSeqView.Appended(this, elem)(mutationCount) + override def prepended[B >: A](elem: B): IndexedSeqView[B]^{this} = new CheckedIndexedSeqView.Prepended(elem, this)(mutationCount) + override def take(n: Int): IndexedSeqView[A]^{this} = new CheckedIndexedSeqView.Take(this, n)(mutationCount) + override def takeRight(n: Int): IndexedSeqView[A]^{this} = new CheckedIndexedSeqView.TakeRight(this, n)(mutationCount) + override def drop(n: Int): IndexedSeqView[A]^{this} = new CheckedIndexedSeqView.Drop(this, n)(mutationCount) + override def dropRight(n: Int): IndexedSeqView[A]^{this} = new CheckedIndexedSeqView.DropRight(this, n)(mutationCount) + override def map[B](f: A => B): IndexedSeqView[B]^{this, f} = new CheckedIndexedSeqView.Map(this, f)(mutationCount) + override def reverse: IndexedSeqView[A]^{this} = new CheckedIndexedSeqView.Reverse(this)(mutationCount) + override def slice(from: Int, until: Int): IndexedSeqView[A]^{this} = new CheckedIndexedSeqView.Slice(this, from, until)(mutationCount) + override def tapEach[U](f: A => U): IndexedSeqView[A]^{this, f} = new CheckedIndexedSeqView.Map(this, { (a: A) => f(a); a})(mutationCount) + + override def concat[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]^): IndexedSeqView[B]^{this, suffix} = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount) + override def appendedAll[B >: A](suffix: IndexedSeqView.SomeIndexedSeqOps[B]^): IndexedSeqView[B]^{this, suffix} = new CheckedIndexedSeqView.Concat(this, suffix)(mutationCount) + override def prependedAll[B >: A](prefix: IndexedSeqView.SomeIndexedSeqOps[B]^): IndexedSeqView[B]^{this, prefix} = new CheckedIndexedSeqView.Concat(prefix, this)(mutationCount) } private[mutable] object CheckedIndexedSeqView { import IndexedSeqView.SomeIndexedSeqOps @SerialVersionUID(3L) - private[mutable] class CheckedIterator[A](self: IndexedSeqView[A], mutationCount: => Int) + private[mutable] class CheckedIterator[A](self: IndexedSeqView[A]^, mutationCount: => Int) extends IndexedSeqView.IndexedSeqViewIterator[A](self) { private[this] val expectedCount = mutationCount override def hasNext: Boolean = { @@ -52,7 +53,7 @@ private[mutable] object CheckedIndexedSeqView { } @SerialVersionUID(3L) - private[mutable] class CheckedReverseIterator[A](self: IndexedSeqView[A], mutationCount: => Int) + private[mutable] class CheckedReverseIterator[A](self: IndexedSeqView[A]^, mutationCount: => Int) extends IndexedSeqView.IndexedSeqViewReverseIterator[A](self) { private[this] val expectedCount = mutationCount override def hasNext: Boolean = { @@ -62,43 +63,43 @@ private[mutable] object CheckedIndexedSeqView { } @SerialVersionUID(3L) - class Id[+A](underlying: SomeIndexedSeqOps[A])(protected val mutationCount: () => Int) + class Id[+A](underlying: SomeIndexedSeqOps[A]^)(protected val mutationCount: () => Int) extends IndexedSeqView.Id(underlying) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class Appended[+A](underlying: SomeIndexedSeqOps[A], elem: A)(protected val mutationCount: () => Int) + class Appended[+A](underlying: SomeIndexedSeqOps[A]^, elem: A)(protected val mutationCount: () => Int) extends IndexedSeqView.Appended(underlying, elem) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class Prepended[+A](elem: A, underlying: SomeIndexedSeqOps[A])(protected val mutationCount: () => Int) + class Prepended[+A](elem: A, underlying: SomeIndexedSeqOps[A]^)(protected val mutationCount: () => Int) extends IndexedSeqView.Prepended(elem, underlying) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class Concat[A](prefix: SomeIndexedSeqOps[A], suffix: SomeIndexedSeqOps[A])(protected val mutationCount: () => Int) + class Concat[A](prefix: SomeIndexedSeqOps[A]^, suffix: SomeIndexedSeqOps[A]^)(protected val mutationCount: () => Int) extends IndexedSeqView.Concat[A](prefix, suffix) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class Take[A](underlying: SomeIndexedSeqOps[A], n: Int)(protected val mutationCount: () => Int) + class Take[A](underlying: SomeIndexedSeqOps[A]^, n: Int)(protected val mutationCount: () => Int) extends IndexedSeqView.Take(underlying, n) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class TakeRight[A](underlying: SomeIndexedSeqOps[A], n: Int)(protected val mutationCount: () => Int) + class TakeRight[A](underlying: SomeIndexedSeqOps[A]^, n: Int)(protected val mutationCount: () => Int) extends IndexedSeqView.TakeRight(underlying, n) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class Drop[A](underlying: SomeIndexedSeqOps[A], n: Int)(protected val mutationCount: () => Int) + class Drop[A](underlying: SomeIndexedSeqOps[A]^, n: Int)(protected val mutationCount: () => Int) extends IndexedSeqView.Drop[A](underlying, n) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class DropRight[A](underlying: SomeIndexedSeqOps[A], n: Int)(protected val mutationCount: () => Int) + class DropRight[A](underlying: SomeIndexedSeqOps[A]^, n: Int)(protected val mutationCount: () => Int) extends IndexedSeqView.DropRight[A](underlying, n) with CheckedIndexedSeqView[A] @SerialVersionUID(3L) - class Map[A, B](underlying: SomeIndexedSeqOps[A], f: A => B)(protected val mutationCount: () => Int) + class Map[A, B](underlying: SomeIndexedSeqOps[A]^, f: A => B)(protected val mutationCount: () => Int) extends IndexedSeqView.Map(underlying, f) with CheckedIndexedSeqView[B] @SerialVersionUID(3L) - class Reverse[A](underlying: SomeIndexedSeqOps[A])(protected val mutationCount: () => Int) + class Reverse[A](underlying: SomeIndexedSeqOps[A]^)(protected val mutationCount: () => Int) extends IndexedSeqView.Reverse[A](underlying) with CheckedIndexedSeqView[A] { override def reverse: IndexedSeqView[A] = underlying match { case x: IndexedSeqView[A] => x @@ -107,7 +108,7 @@ private[mutable] object CheckedIndexedSeqView { } @SerialVersionUID(3L) - class Slice[A](underlying: SomeIndexedSeqOps[A], from: Int, until: Int)(protected val mutationCount: () => Int) + class Slice[A](underlying: SomeIndexedSeqOps[A]^, from: Int, until: Int)(protected val mutationCount: () => Int) extends AbstractIndexedSeqView[A] with CheckedIndexedSeqView[A] { protected val lo = from max 0 protected val hi = (until max 0) min underlying.length diff --git a/library/src/scala/collection/mutable/Cloneable.scala b/library/src/scala/collection/mutable/Cloneable.scala index 7480b5bae613..b84c5b225bb1 100644 --- a/library/src/scala/collection/mutable/Cloneable.scala +++ b/library/src/scala/collection/mutable/Cloneable.scala @@ -13,6 +13,7 @@ package scala.collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking /** A trait for cloneable collections. * diff --git a/library/src/scala/collection/mutable/CollisionProofHashMap.scala b/library/src/scala/collection/mutable/CollisionProofHashMap.scala index 18d469c42900..35fa6393deab 100644 --- a/library/src/scala/collection/mutable/CollisionProofHashMap.scala +++ b/library/src/scala/collection/mutable/CollisionProofHashMap.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.{unchecked => uc} import scala.annotation.{implicitNotFound, tailrec, unused} import scala.annotation.unchecked.uncheckedVariance @@ -64,7 +65,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double @`inline` private[this] final def index(hash: Int) = hash & (table.length - 1) - override protected def fromSpecific(coll: IterableOnce[(K, V)] @uncheckedVariance): CollisionProofHashMap[K, V] @uncheckedVariance = CollisionProofHashMap.from(coll) + override protected def fromSpecific(coll: (IterableOnce[(K, V)] @uncheckedVariance)^): CollisionProofHashMap[K, V] @uncheckedVariance = CollisionProofHashMap.from(coll) override protected def newSpecificBuilder: Builder[(K, V), CollisionProofHashMap[K, V]] @uncheckedVariance = CollisionProofHashMap.newBuilder[K, V] override def empty: CollisionProofHashMap[K, V] = new CollisionProofHashMap[K, V] @@ -174,7 +175,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double } } - override def addAll(xs: IterableOnce[(K, V)]): this.type = { + override def addAll(xs: IterableOnce[(K, V)]^): this.type = { sizeHint(xs, delta = contentSize) super.addAll(xs) } @@ -426,7 +427,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double * @return a new $coll resulting from applying the given collection-valued function * `f` to each element of this $coll and concatenating the results. */ - def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]) + def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]^) (implicit @implicitNotFound(CollisionProofHashMap.ordMsg) ordering: Ordering[K2]): CollisionProofHashMap[K2, V2] = sortedMapFactory.from(new View.FlatMap(this, f)) @@ -442,13 +443,13 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double (implicit @implicitNotFound(CollisionProofHashMap.ordMsg) ordering: Ordering[K2]): CollisionProofHashMap[K2, V2] = sortedMapFactory.from(new View.Collect(this, pf)) - override def concat[V2 >: V](suffix: IterableOnce[(K, V2)]): CollisionProofHashMap[K, V2] = sortedMapFactory.from(suffix match { + override def concat[V2 >: V](suffix: IterableOnce[(K, V2)]^): CollisionProofHashMap[K, V2] = sortedMapFactory.from(suffix match { case it: Iterable[(K, V2)] => new View.Concat(this, it) case _ => iterator.concat(suffix.iterator) }) /** Alias for `concat` */ - @`inline` override final def ++ [V2 >: V](xs: IterableOnce[(K, V2)]): CollisionProofHashMap[K, V2] = concat(xs) + @`inline` override final def ++ [V2 >: V](xs: IterableOnce[(K, V2)]^): CollisionProofHashMap[K, V2] = concat(xs) @deprecated("Consider requiring an immutable Map or fall back to Map.concat", "2.13.0") override def + [V1 >: V](kv: (K, V1)): CollisionProofHashMap[K, V1] = @@ -743,7 +744,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double object CollisionProofHashMap extends SortedMapFactory[CollisionProofHashMap] { private[collection] final val ordMsg = "No implicit Ordering[${K2}] found to build a CollisionProofHashMap[${K2}, ${V2}]. You may want to upcast to a Map[${K}, ${V}] first by calling `unsorted`." - def from[K : Ordering, V](it: scala.collection.IterableOnce[(K, V)]): CollisionProofHashMap[K, V] = { + def from[K : Ordering, V](it: scala.collection.IterableOnce[(K, V)]^): CollisionProofHashMap[K, V] = { val k = it.knownSize val cap = if(k > 0) ((k + 1).toDouble / defaultLoadFactor).toInt else defaultInitialCapacity new CollisionProofHashMap[K, V](cap, defaultLoadFactor) ++= it @@ -766,7 +767,7 @@ object CollisionProofHashMap extends SortedMapFactory[CollisionProofHashMap] { @SerialVersionUID(3L) private final class DeserializationFactory[K, V](val tableLength: Int, val loadFactor: Double, val ordering: Ordering[K]) extends Factory[(K, V), CollisionProofHashMap[K, V]] with Serializable { - def fromSpecific(it: IterableOnce[(K, V)]): CollisionProofHashMap[K, V] = new CollisionProofHashMap[K, V](tableLength, loadFactor)(ordering) ++= it + def fromSpecific(it: IterableOnce[(K, V)]^): CollisionProofHashMap[K, V] = new CollisionProofHashMap[K, V](tableLength, loadFactor)(ordering) ++= it def newBuilder: Builder[(K, V), CollisionProofHashMap[K, V]] = CollisionProofHashMap.newBuilder(tableLength, loadFactor)(using ordering) } diff --git a/library/src/scala/collection/mutable/Growable.scala b/library/src/scala/collection/mutable/Growable.scala index 9caed94240e2..1ce3b3b0d99e 100644 --- a/library/src/scala/collection/mutable/Growable.scala +++ b/library/src/scala/collection/mutable/Growable.scala @@ -15,6 +15,7 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking /** This trait forms part of collections that can be augmented * using a `+=` operator and that can be cleared of all elements using @@ -56,7 +57,7 @@ trait Growable[-A] extends Clearable { * @param elems the IterableOnce producing the elements to $add. * @return the $coll itself. */ - def addAll(@deprecatedName("xs") elems: IterableOnce[A]): this.type = { + def addAll(@deprecatedName("xs") elems: IterableOnce[A]^): this.type = { if (elems.asInstanceOf[AnyRef] eq this) addAll(Buffer.from(elems)) // avoid mutating under our own iterator else { val it = elems.iterator @@ -68,7 +69,7 @@ trait Growable[-A] extends Clearable { } /** Alias for `addAll` */ - @inline final def ++= (@deprecatedName("xs") elems: IterableOnce[A]): this.type = addAll(elems) + @inline final def ++= (@deprecatedName("xs") elems: IterableOnce[A]^): this.type = addAll(elems) /** The number of elements in the collection under construction, if it can be cheaply computed, -1 otherwise. * @@ -86,7 +87,7 @@ object Growable { * @tparam A Element type * @return The filled instance */ - def from[A](empty: Growable[A], it: collection.IterableOnce[A]): empty.type = empty ++= it + def from[A](empty: Growable[A], it: collection.IterableOnce[A]^): empty.type = empty ++= it } diff --git a/library/src/scala/collection/mutable/GrowableBuilder.scala b/library/src/scala/collection/mutable/GrowableBuilder.scala index e98aa16c2581..cb18a6838b0f 100644 --- a/library/src/scala/collection/mutable/GrowableBuilder.scala +++ b/library/src/scala/collection/mutable/GrowableBuilder.scala @@ -14,6 +14,7 @@ package scala package collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking /** The canonical builder for collections that are growable, i.e. that support an * efficient `+=` method which adds an element to the collection. @@ -32,7 +33,7 @@ class GrowableBuilder[Elem, To <: Growable[Elem]](protected val elems: To) def addOne(elem: Elem): this.type = { elems += elem; this } - override def addAll(xs: IterableOnce[Elem]): this.type = { elems.addAll(xs); this } + override def addAll(xs: IterableOnce[Elem]^): this.type = { elems.addAll(xs); this } override def knownSize: Int = elems.knownSize } diff --git a/library/src/scala/collection/mutable/HashMap.scala b/library/src/scala/collection/mutable/HashMap.scala index 7980ffe08bd7..8024347f9a03 100644 --- a/library/src/scala/collection/mutable/HashMap.scala +++ b/library/src/scala/collection/mutable/HashMap.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.{nowarn, tailrec} import scala.collection.Stepper.EfficientSplit import scala.collection.generic.DefaultSerializationProxy @@ -95,7 +96,7 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) if(target > table.length) growTable(target) } - override def addAll(xs: IterableOnce[(K, V)]): this.type = { + override def addAll(xs: IterableOnce[(K, V)]^): this.type = { sizeHint(xs) xs match { @@ -183,7 +184,7 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) } } - override def subtractAll(xs: IterableOnce[K]): this.type = { + override def subtractAll(xs: IterableOnce[K]^): this.type = { if (size == 0) { return this } @@ -568,7 +569,7 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) this } - override def mapFactory: MapFactory[HashMap] = HashMap + override def mapFactory: StrictMapFactory[HashMap] = HashMap @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") override protected[this] def stringPrefix = "HashMap" @@ -595,11 +596,11 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) * @define coll mutable hash map */ @SerialVersionUID(3L) -object HashMap extends MapFactory[HashMap] { +object HashMap extends StrictMapFactory[HashMap] { def empty[K, V]: HashMap[K, V] = new HashMap[K, V] - def from[K, V](it: collection.IterableOnce[(K, V)]): HashMap[K, V] = { + def from[K, V](it: collection.IterableOnce[(K, V)]^): HashMap[K, V] = { val k = it.knownSize val cap = if(k > 0) ((k + 1).toDouble / defaultLoadFactor).toInt else defaultInitialCapacity new HashMap[K, V](cap, defaultLoadFactor).addAll(it) @@ -620,7 +621,7 @@ object HashMap extends MapFactory[HashMap] { @SerialVersionUID(3L) private final class DeserializationFactory[K, V](val tableLength: Int, val loadFactor: Double) extends Factory[(K, V), HashMap[K, V]] with Serializable { - def fromSpecific(it: IterableOnce[(K, V)]): HashMap[K, V] = new HashMap[K, V](tableLength, loadFactor).addAll(it) + def fromSpecific(it: IterableOnce[(K, V)]^): HashMap[K, V] = new HashMap[K, V](tableLength, loadFactor).addAll(it) def newBuilder: Builder[(K, V), HashMap[K, V]] = HashMap.newBuilder(tableLength, loadFactor) } diff --git a/library/src/scala/collection/mutable/HashSet.scala b/library/src/scala/collection/mutable/HashSet.scala index df0cb0a9ee33..a92db177c110 100644 --- a/library/src/scala/collection/mutable/HashSet.scala +++ b/library/src/scala/collection/mutable/HashSet.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.tailrec import scala.collection.Stepper.EfficientSplit import scala.collection.generic.DefaultSerializationProxy @@ -91,7 +92,7 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) addElem(elem, computeHash(elem)) } - override def addAll(xs: IterableOnce[A]): this.type = { + override def addAll(xs: IterableOnce[A]^): this.type = { sizeHint(xs, delta = 0) xs match { case hs: immutable.HashSet[A] => @@ -115,7 +116,7 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) } } - override def subtractAll(xs: IterableOnce[A]): this.type = { + override def subtractAll(xs: IterableOnce[A]^): this.type = { if (size == 0) { return this } @@ -407,7 +408,7 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) @SerialVersionUID(3L) object HashSet extends IterableFactory[HashSet] { - def from[B](it: scala.collection.IterableOnce[B]): HashSet[B] = { + def from[B](it: scala.collection.IterableOnce[B]^): HashSet[B] = { val k = it.knownSize val cap = if(k > 0) ((k + 1).toDouble / defaultLoadFactor).toInt else defaultInitialCapacity new HashSet[B](cap, defaultLoadFactor) ++= it @@ -430,7 +431,7 @@ object HashSet extends IterableFactory[HashSet] { @SerialVersionUID(3L) private final class DeserializationFactory[A](val tableLength: Int, val loadFactor: Double) extends Factory[A, HashSet[A]] with Serializable { - def fromSpecific(it: IterableOnce[A]): HashSet[A] = new HashSet[A](tableLength, loadFactor) ++= it + def fromSpecific(it: IterableOnce[A]^): HashSet[A] = new HashSet[A](tableLength, loadFactor) ++= it def newBuilder: Builder[A, HashSet[A]] = HashSet.newBuilder(tableLength, loadFactor) } diff --git a/library/src/scala/collection/mutable/HashTable.scala b/library/src/scala/collection/mutable/HashTable.scala index 08bdd68eece3..738adce7dfaa 100644 --- a/library/src/scala/collection/mutable/HashTable.scala +++ b/library/src/scala/collection/mutable/HashTable.scala @@ -14,6 +14,7 @@ package scala package collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking import collection.{AbstractIterator, Iterator} import java.lang.Integer.{numberOfLeadingZeros, rotateRight} diff --git a/library/src/scala/collection/mutable/ImmutableBuilder.scala b/library/src/scala/collection/mutable/ImmutableBuilder.scala index 6806367e53a4..5c263dab8ed7 100644 --- a/library/src/scala/collection/mutable/ImmutableBuilder.scala +++ b/library/src/scala/collection/mutable/ImmutableBuilder.scala @@ -15,6 +15,7 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking /** * Reusable builder for immutable collections diff --git a/library/src/scala/collection/mutable/IndexedSeq.scala b/library/src/scala/collection/mutable/IndexedSeq.scala index 31feb34395f8..bb1af54d3088 100644 --- a/library/src/scala/collection/mutable/IndexedSeq.scala +++ b/library/src/scala/collection/mutable/IndexedSeq.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking trait IndexedSeq[T] extends Seq[T] with scala.collection.IndexedSeq[T] @@ -26,8 +27,8 @@ trait IndexedSeq[T] extends Seq[T] @SerialVersionUID(3L) object IndexedSeq extends SeqFactory.Delegate[IndexedSeq](ArrayBuffer) -transparent trait IndexedSeqOps[A, +CC[_], +C <: AnyRef] - extends scala.collection.IndexedSeqOps[A, CC, C] +transparent trait IndexedSeqOps[A, +CC[_] <: caps.Pure, +C <: AnyRef] + extends scala.collection.StrictIndexedSeqOps[A, CC, C] with SeqOps[A, CC, C] { /** Modifies this $coll by applying a function to all elements of this $coll. diff --git a/library/src/scala/collection/mutable/Iterable.scala b/library/src/scala/collection/mutable/Iterable.scala index 7f018b00c17f..14de48a768b5 100644 --- a/library/src/scala/collection/mutable/Iterable.scala +++ b/library/src/scala/collection/mutable/Iterable.scala @@ -13,6 +13,7 @@ package scala.collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.collection.{IterableFactory, IterableFactoryDefaults} trait Iterable[A] diff --git a/library/src/scala/collection/mutable/LinkedHashMap.scala b/library/src/scala/collection/mutable/LinkedHashMap.scala index 5a4f9285972d..0b5ebedf9a23 100644 --- a/library/src/scala/collection/mutable/LinkedHashMap.scala +++ b/library/src/scala/collection/mutable/LinkedHashMap.scala @@ -15,6 +15,7 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.{nowarn, tailrec} import scala.collection.generic.DefaultSerializable import scala.util.hashing.MurmurHash3 @@ -43,7 +44,7 @@ class LinkedHashMap[K, V] with MapFactoryDefaults[K, V, LinkedHashMap, Iterable] with DefaultSerializable { - override def mapFactory: MapFactory[LinkedHashMap] = LinkedHashMap + override def mapFactory: StrictMapFactory[LinkedHashMap] = LinkedHashMap // stepper / keyStepper / valueStepper are not overridden to use XTableStepper because that stepper // would not return the elements in insertion order @@ -475,11 +476,11 @@ class LinkedHashMap[K, V] * @define coll linked hash map */ @SerialVersionUID(3L) -object LinkedHashMap extends MapFactory[LinkedHashMap] { +object LinkedHashMap extends StrictMapFactory[LinkedHashMap] { def empty[K, V] = new LinkedHashMap[K, V] - def from[K, V](it: collection.IterableOnce[(K, V)]) = { + def from[K, V](it: collection.IterableOnce[(K, V)]^) = { val newlhm = empty[K, V] newlhm.sizeHint(it, delta = 0) newlhm.addAll(it) diff --git a/library/src/scala/collection/mutable/LinkedHashSet.scala b/library/src/scala/collection/mutable/LinkedHashSet.scala index 79d3d62a41d1..f0c2d2d843c3 100644 --- a/library/src/scala/collection/mutable/LinkedHashSet.scala +++ b/library/src/scala/collection/mutable/LinkedHashSet.scala @@ -15,6 +15,7 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.{nowarn, tailrec} import scala.collection.generic.DefaultSerializable import scala.util.hashing.MurmurHash3 @@ -317,7 +318,7 @@ object LinkedHashSet extends IterableFactory[LinkedHashSet] { override def empty[A]: LinkedHashSet[A] = new LinkedHashSet[A] - def from[E](it: collection.IterableOnce[E]) = { + def from[E](it: collection.IterableOnce[E]^) = { val newlhs = empty[E] newlhs.sizeHint(it, delta = 0) newlhs.addAll(it) diff --git a/library/src/scala/collection/mutable/ListBuffer.scala b/library/src/scala/collection/mutable/ListBuffer.scala index e5858f79eb5e..f90423b40f67 100644 --- a/library/src/scala/collection/mutable/ListBuffer.scala +++ b/library/src/scala/collection/mutable/ListBuffer.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.{nowarn, tailrec} import scala.collection.generic.CommonErrors import scala.collection.immutable.{::, List, Nil} @@ -123,7 +124,7 @@ class ListBuffer[A] } // MUST only be called on fresh instances - private def freshFrom(xs: IterableOnce[A]): this.type = { + private def freshFrom(xs: IterableOnce[A]^): this.type = { val it = xs.iterator if (it.hasNext) { var len = 1 @@ -142,7 +143,7 @@ class ListBuffer[A] this } - override final def addAll(xs: IterableOnce[A]): this.type = { + override final def addAll(xs: IterableOnce[A]^): this.type = { val it = xs.iterator if (it.hasNext) { val fresh = new ListBuffer[A].freshFrom(it) @@ -251,7 +252,7 @@ class ListBuffer[A] } } - def insertAll(idx: Int, elems: IterableOnce[A]): Unit = { + def insertAll(idx: Int, elems: IterableOnce[A]^): Unit = { if (idx < 0 || idx > len) throw CommonErrors.indexOutOfBounds(index = idx, max = len - 1) val it = elems.iterator if (it.hasNext) { @@ -318,7 +319,7 @@ class ListBuffer[A] * @param f the mapping function * @return this $coll */ - def flatMapInPlace(f: A => IterableOnce[A]): this.type = { + def flatMapInPlace(f: A => IterableOnce[A]^): this.type = { mutationCount += 1 var src = first var dst: List[A] = null @@ -363,7 +364,7 @@ class ListBuffer[A] this } - def patchInPlace(from: Int, patch: collection.IterableOnce[A], replaced: Int): this.type = { + def patchInPlace(from: Int, patch: collection.IterableOnce[A]^, replaced: Int): this.type = { val _len = len val _from = math.max(from, 0) // normalized val _replaced = math.max(replaced, 0) // normalized @@ -413,7 +414,7 @@ class ListBuffer[A] @SerialVersionUID(3L) object ListBuffer extends StrictOptimizedSeqFactory[ListBuffer] { - def from[A](coll: collection.IterableOnce[A]): ListBuffer[A] = new ListBuffer[A].freshFrom(coll) + def from[A](coll: collection.IterableOnce[A]^): ListBuffer[A] = new ListBuffer[A].freshFrom(coll) def newBuilder[A]: Builder[A, ListBuffer[A]] = new GrowableBuilder(empty[A]) diff --git a/library/src/scala/collection/mutable/ListMap.scala b/library/src/scala/collection/mutable/ListMap.scala index 3bb7e9bd54bb..494eebdadd73 100644 --- a/library/src/scala/collection/mutable/ListMap.scala +++ b/library/src/scala/collection/mutable/ListMap.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.tailrec import scala.collection.generic.DefaultSerializable import scala.collection.immutable.List @@ -39,7 +40,7 @@ class ListMap[K, V] with MapFactoryDefaults[K, V, ListMap, Iterable] with DefaultSerializable { - override def mapFactory: MapFactory[ListMap] = ListMap + override def mapFactory: StrictMapFactory[ListMap] = ListMap private[this] var elems: List[(K, V)] = List() private[this] var siz: Int = 0 @@ -76,8 +77,8 @@ class ListMap[K, V] */ @SerialVersionUID(3L) @deprecated("Use an immutable.ListMap assigned to a var instead of mutable.ListMap", "2.13.0") -object ListMap extends MapFactory[ListMap] { +object ListMap extends StrictMapFactory[ListMap] { def empty[K, V]: ListMap[K, V] = new ListMap[K, V] - def from[K, V](it: IterableOnce[(K, V)]): ListMap[K,V] = Growable.from(empty[K, V], it) + def from[K, V](it: IterableOnce[(K, V)]^): ListMap[K,V] = Growable.from(empty[K, V], it) def newBuilder[K, V]: Builder[(K, V), ListMap[K,V]] = new GrowableBuilder(empty[K, V]) } diff --git a/library/src/scala/collection/mutable/LongMap.scala b/library/src/scala/collection/mutable/LongMap.scala index a56d874a5fc2..208c4be0910e 100644 --- a/library/src/scala/collection/mutable/LongMap.scala +++ b/library/src/scala/collection/mutable/LongMap.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.collection.generic.DefaultSerializationProxy import scala.language.implicitConversions @@ -37,7 +38,7 @@ import scala.language.implicitConversions * rapidly as 2^30 is approached. * */ -final class LongMap[V] private[collection] (defaultEntry: Long => V, initialBufferSize: Int, initBlank: Boolean) +final class LongMap[V] private[collection] (defaultEntry: Long -> V, initialBufferSize: Int, initBlank: Boolean) extends AbstractMap[Long, V] with MapOps[Long, V, Map, LongMap[V]] with StrictOptimizedIterableOps[(Long, V), Iterable, LongMap[V]] @@ -47,7 +48,7 @@ final class LongMap[V] private[collection] (defaultEntry: Long => V, initialBuff def this() = this(LongMap.exceptionDefault, 16, initBlank = true) // TODO: override clear() with an optimization more tailored for efficiency. - override protected def fromSpecific(coll: scala.collection.IterableOnce[(Long, V)]): LongMap[V] = { + override protected def fromSpecific(coll: scala.collection.IterableOnce[(Long, V)]^): LongMap[V] = { //TODO should this be the default implementation of this method in StrictOptimizedIterableOps? val b = newSpecificBuilder b.sizeHint(coll) @@ -57,7 +58,7 @@ final class LongMap[V] private[collection] (defaultEntry: Long => V, initialBuff override protected def newSpecificBuilder: Builder[(Long, V),LongMap[V]] = new GrowableBuilder(LongMap.empty[V]) /** Creates a new `LongMap` that returns default values according to a supplied key-value mapping. */ - def this(defaultEntry: Long => V) = this(defaultEntry, 16, initBlank = true) + def this(defaultEntry: Long -> V) = this(defaultEntry, 16, initBlank = true) /** Creates a new `LongMap` with an initial buffer of specified size. * @@ -67,7 +68,7 @@ final class LongMap[V] private[collection] (defaultEntry: Long => V, initialBuff def this(initialBufferSize: Int) = this(LongMap.exceptionDefault, initialBufferSize, initBlank = true) /** Creates a new `LongMap` with specified default values and initial buffer size. */ - def this(defaultEntry: Long => V, initialBufferSize: Int) = this(defaultEntry, initialBufferSize, initBlank = true) + def this(defaultEntry: Long -> V, initialBufferSize: Int) = this(defaultEntry, initialBufferSize, initBlank = true) private[this] var mask = 0 private[this] var extraKeys: Int = 0 @@ -468,18 +469,19 @@ final class LongMap[V] private[collection] (defaultEntry: Long => V, initialBuff } @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") - override def + [V1 >: V](elem1: (Long, V1), elem2: (Long, V1), elems: (Long, V1)*): LongMap[V1] = { + override def + [V1 >: V](elem1: (Long, V1), elem2: (Long, V1), elems: (Long, V1)*): LongMap[V1]^{} = { + // An empty capture annotation is needed in the result type to satisfy the overriding checker. val m = this + elem1 + elem2 if(elems.isEmpty) m else m.concat(elems) } - override def concat[V1 >: V](xs: scala.collection.IterableOnce[(Long, V1)]): LongMap[V1] = { + override def concat[V1 >: V](xs: scala.collection.IterableOnce[(Long, V1)]^): LongMap[V1] = { val lm = clone().asInstanceOf[LongMap[V1]] xs.iterator.foreach(kv => lm += kv) lm } - override def ++ [V1 >: V](xs: scala.collection.IterableOnce[(Long, V1)]): LongMap[V1] = concat(xs) + override def ++ [V1 >: V](xs: scala.collection.IterableOnce[(Long, V1)]^): LongMap[V1] = concat(xs) @deprecated("Use m.clone().addOne(k,v) instead of m.updated(k, v)", "2.13.0") override def updated[V1 >: V](key: Long, value: V1): LongMap[V1] = @@ -572,7 +574,7 @@ final class LongMap[V] private[collection] (defaultEntry: Long => V, initialBuff * * @param f the mapping function */ - def flatMap[V2](f: ((Long, V)) => IterableOnce[(Long, V2)]): LongMap[V2] = LongMap.from(new View.FlatMap(coll, f)) + def flatMap[V2](f: ((Long, V)) => IterableOnce[(Long, V2)]^): LongMap[V2] = LongMap.from(new View.FlatMap(coll, f)) /** An overload of `collect` which produces a `LongMap`. * @@ -612,7 +614,7 @@ object LongMap { /** Creates a new `LongMap` with zero or more key/value pairs. */ def apply[V](elems: (Long, V)*): LongMap[V] = buildFromIterableOnce(elems) - private def buildFromIterableOnce[V](elems: IterableOnce[(Long, V)]): LongMap[V] = { + private def buildFromIterableOnce[V](elems: IterableOnce[(Long, V)]^): LongMap[V] = { var sz = elems.knownSize if(sz < 0) sz = 4 val lm = new LongMap[V](sz * 2) @@ -625,7 +627,7 @@ object LongMap { def empty[V]: LongMap[V] = new LongMap[V] /** Creates a new empty `LongMap` with the supplied default */ - def withDefault[V](default: Long => V): LongMap[V] = new LongMap[V](default) + def withDefault[V](default: Long -> V): LongMap[V] = new LongMap[V](default) /** Creates a new `LongMap` from an existing source collection. A source collection * which is already a `LongMap` gets cloned. @@ -634,7 +636,7 @@ object LongMap { * @tparam A the type of the collection’s elements * @return a new `LongMap` with the elements of `source` */ - def from[V](source: IterableOnce[(Long, V)]): LongMap[V] = source match { + def from[V](source: IterableOnce[(Long, V)]^): LongMap[V] = source match { case source: LongMap[_] => source.clone().asInstanceOf[LongMap[V]] case _ => buildFromIterableOnce(source) } @@ -670,13 +672,13 @@ object LongMap { @SerialVersionUID(3L) private[this] object ToFactory extends Factory[(Long, AnyRef), LongMap[AnyRef]] with Serializable { - def fromSpecific(it: IterableOnce[(Long, AnyRef)]): LongMap[AnyRef] = LongMap.from[AnyRef](it) + def fromSpecific(it: IterableOnce[(Long, AnyRef)]^): LongMap[AnyRef] = LongMap.from[AnyRef](it) def newBuilder: Builder[(Long, AnyRef), LongMap[AnyRef]] = LongMap.newBuilder[AnyRef] } implicit def toBuildFrom[V](factory: LongMap.type): BuildFrom[Any, (Long, V), LongMap[V]] = ToBuildFrom.asInstanceOf[BuildFrom[Any, (Long, V), LongMap[V]]] private object ToBuildFrom extends BuildFrom[Any, (Long, AnyRef), LongMap[AnyRef]] { - def fromSpecific(from: Any)(it: IterableOnce[(Long, AnyRef)]) = LongMap.from(it) + def fromSpecific(from: Any)(it: IterableOnce[(Long, AnyRef)]^) = LongMap.from(it) def newBuilder(from: Any): ReusableBuilder[(Long, AnyRef), LongMap[AnyRef]] = LongMap.newBuilder[AnyRef] } diff --git a/library/src/scala/collection/mutable/Map.scala b/library/src/scala/collection/mutable/Map.scala index df6f036b62d9..617c5f2040be 100644 --- a/library/src/scala/collection/mutable/Map.scala +++ b/library/src/scala/collection/mutable/Map.scala @@ -14,6 +14,8 @@ package scala package collection package mutable +import language.experimental.captureChecking + import scala.language.`2.13` /** Base type of mutable Maps */ @@ -25,7 +27,7 @@ trait Map[K, V] with Shrinkable[K] with MapFactoryDefaults[K, V, Map, Iterable] { - override def mapFactory: scala.collection.MapFactory[Map] = Map + override def mapFactory: scala.collection.StrictMapFactory[Map] = Map /* //TODO consider keeping `remove` because it returns the removed entry @@ -46,7 +48,7 @@ trait Map[K, V] * @param d the function mapping keys to values, used for non-present keys * @return a wrapper of the map with a default value */ - def withDefault(d: K => V): Map[K, V] = new Map.WithDefault[K, V](this, d) + def withDefault(d: K -> V): Map[K, V] = new Map.WithDefault[K, V](this, d) /** The same map with a given default value. * Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc. @@ -66,7 +68,7 @@ trait Map[K, V] */ transparent trait MapOps[K, V, +CC[X, Y] <: MapOps[X, Y, CC, _], +C <: MapOps[K, V, CC, C]] extends IterableOps[(K, V), Iterable, C] - with collection.MapOps[K, V, CC, C] + with collection.StrictMapOps[K, V, CC, C] with Cloneable[C] with Builder[(K, V), C] with Growable[(K, V)] @@ -233,7 +235,7 @@ transparent trait MapOps[K, V, +CC[X, Y] <: MapOps[X, Y, CC, _], +C <: MapOps[K, object Map extends MapFactory.Delegate[Map](HashMap) { @SerialVersionUID(3L) - class WithDefault[K, V](val underlying: Map[K, V], val defaultValue: K => V) + class WithDefault[K, V](val underlying: Map[K, V], val defaultValue: K -> V) extends AbstractMap[K, V] with MapOps[K, V, Map, WithDefault[K, V]] with Serializable { @@ -242,7 +244,7 @@ object Map extends MapFactory.Delegate[Map](HashMap) { def iterator: scala.collection.Iterator[(K, V)] = underlying.iterator override def isEmpty: Boolean = underlying.isEmpty override def knownSize: Int = underlying.knownSize - override def mapFactory: MapFactory[Map] = underlying.mapFactory + override def mapFactory: StrictMapFactory[Map] = underlying.mapFactory override def clear(): Unit = underlying.clear() @@ -252,12 +254,12 @@ object Map extends MapFactory.Delegate[Map](HashMap) { def addOne(elem: (K, V)): WithDefault.this.type = { underlying.addOne(elem); this } - override def concat[V2 >: V](suffix: collection.IterableOnce[(K, V2)]): Map[K, V2] = + override def concat[V2 >: V](suffix: collection.IterableOnce[(K, V2)]^): Map[K, V2] = underlying.concat(suffix).withDefault(defaultValue) override def empty: WithDefault[K, V] = new WithDefault[K, V](underlying.empty, defaultValue) - override protected def fromSpecific(coll: scala.collection.IterableOnce[(K, V)]): WithDefault[K, V] = + override protected def fromSpecific(coll: scala.collection.IterableOnce[(K, V)]^): WithDefault[K, V] = new WithDefault[K, V](mapFactory.from(coll), defaultValue) override protected def newSpecificBuilder: Builder[(K, V), WithDefault[K, V]] = diff --git a/library/src/scala/collection/mutable/MultiMap.scala b/library/src/scala/collection/mutable/MultiMap.scala index f96a2ca94754..a92f5a8f5712 100644 --- a/library/src/scala/collection/mutable/MultiMap.scala +++ b/library/src/scala/collection/mutable/MultiMap.scala @@ -13,6 +13,7 @@ package scala.collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking /** A trait for mutable maps with multiple values assigned to a key. * diff --git a/library/src/scala/collection/mutable/MutationTracker.scala b/library/src/scala/collection/mutable/MutationTracker.scala index 6b41ac22db37..ddd3a912a840 100644 --- a/library/src/scala/collection/mutable/MutationTracker.scala +++ b/library/src/scala/collection/mutable/MutationTracker.scala @@ -15,6 +15,7 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import java.util.ConcurrentModificationException /** diff --git a/library/src/scala/collection/mutable/OpenHashMap.scala b/library/src/scala/collection/mutable/OpenHashMap.scala index 36d77098512b..5c37d22d5cb0 100644 --- a/library/src/scala/collection/mutable/OpenHashMap.scala +++ b/library/src/scala/collection/mutable/OpenHashMap.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import java.lang.Integer.numberOfLeadingZeros import java.util.ConcurrentModificationException import scala.collection.generic.DefaultSerializable @@ -24,10 +25,10 @@ import scala.collection.generic.DefaultSerializable */ @deprecated("Use HashMap or one of the specialized versions (LongMap, AnyRefMap) instead of OpenHashMap", "2.13.0") @SerialVersionUID(3L) -object OpenHashMap extends MapFactory[OpenHashMap] { +object OpenHashMap extends StrictMapFactory[OpenHashMap] { def empty[K, V] = new OpenHashMap[K, V] - def from[K, V](it: IterableOnce[(K, V)]): OpenHashMap[K,V] = empty ++= it + def from[K, V](it: IterableOnce[(K, V)]^): OpenHashMap[K,V] = empty ++= it def newBuilder[K, V]: Builder[(K, V), OpenHashMap[K,V]] = new GrowableBuilder[(K, V), OpenHashMap[K, V]](empty) @@ -76,7 +77,7 @@ class OpenHashMap[Key, Value](initialSize : Int) */ def this() = this(8) - override def mapFactory: MapFactory[OpenHashMap] = OpenHashMap + override def mapFactory: StrictMapFactory[OpenHashMap] = OpenHashMap private[this] val actualInitialSize = OpenHashMap.nextPositivePowerOfTwo(initialSize) diff --git a/library/src/scala/collection/mutable/PriorityQueue.scala b/library/src/scala/collection/mutable/PriorityQueue.scala index f0146450e50f..050fa5159ee6 100644 --- a/library/src/scala/collection/mutable/PriorityQueue.scala +++ b/library/src/scala/collection/mutable/PriorityQueue.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.collection.generic.DefaultSerializationProxy import scala.math.Ordering @@ -112,7 +113,7 @@ sealed class PriorityQueue[A](implicit val ord: Ordering[A]) override def isEmpty: Boolean = resarr.p_size0 < 2 // not eligible for EvidenceIterableFactoryDefaults since C != CC[A] (PriorityQueue[A] != Iterable[A]) - override protected def fromSpecific(coll: scala.collection.IterableOnce[A]): PriorityQueue[A] = PriorityQueue.from(coll) + override protected def fromSpecific(coll: scala.collection.IterableOnce[A]^): PriorityQueue[A] = PriorityQueue.from(coll) override protected def newSpecificBuilder: Builder[A, PriorityQueue[A]] = PriorityQueue.newBuilder override def empty: PriorityQueue[A] = PriorityQueue.empty @@ -172,7 +173,7 @@ sealed class PriorityQueue[A](implicit val ord: Ordering[A]) this } - override def addAll(xs: IterableOnce[A]): this.type = { + override def addAll(xs: IterableOnce[A]^): this.type = { val from = resarr.p_size0 for (x <- xs.iterator) unsafeAdd(x) heapify(from) @@ -238,7 +239,7 @@ sealed class PriorityQueue[A](implicit val ord: Ordering[A]) * @param xs a iterable object. * @return a new priority queue containing elements of both `xs` and `this`. */ - def ++(xs: IterableOnce[A]): PriorityQueue[A] = { this.clone() ++= xs } + def ++(xs: IterableOnce[A]^): PriorityQueue[A] = { this.clone() ++= xs } /** Adds all elements to the queue. * @@ -406,7 +407,7 @@ object PriorityQueue extends SortedIterableFactory[PriorityQueue] { def empty[A : Ordering]: PriorityQueue[A] = new PriorityQueue[A] - def from[E : Ordering](it: IterableOnce[E]): PriorityQueue[E] = { + def from[E : Ordering](it: IterableOnce[E]^): PriorityQueue[E] = { val b = newBuilder[E] b ++= it b.result() diff --git a/library/src/scala/collection/mutable/Queue.scala b/library/src/scala/collection/mutable/Queue.scala index 7a0c25c049b6..91c2fdd81334 100644 --- a/library/src/scala/collection/mutable/Queue.scala +++ b/library/src/scala/collection/mutable/Queue.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.nowarn import scala.collection.generic.DefaultSerializable @@ -67,7 +68,7 @@ class Queue[A] protected (array: Array[AnyRef], start: Int, end: Int) * @param elems the iterable object. * @return this */ - def enqueueAll(elems: scala.collection.IterableOnce[A]): this.type = this ++= elems + def enqueueAll(elems: scala.collection.IterableOnce[A]^): this.type = this ++= elems /** * Removes the first element from this queue and returns it @@ -130,7 +131,7 @@ class Queue[A] protected (array: Array[AnyRef], start: Int, end: Int) @SerialVersionUID(3L) object Queue extends StrictOptimizedSeqFactory[Queue] { - def from[A](source: IterableOnce[A]): Queue[A] = empty ++= source + def from[A](source: IterableOnce[A]^): Queue[A] = empty ++= source def empty[A]: Queue[A] = new Queue diff --git a/library/src/scala/collection/mutable/RedBlackTree.scala b/library/src/scala/collection/mutable/RedBlackTree.scala index e4f2c54017a8..7a64730d7449 100644 --- a/library/src/scala/collection/mutable/RedBlackTree.scala +++ b/library/src/scala/collection/mutable/RedBlackTree.scala @@ -14,6 +14,7 @@ package scala package collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.tailrec import collection.{AbstractIterator, Iterator} import java.lang.String diff --git a/library/src/scala/collection/mutable/ReusableBuilder.scala b/library/src/scala/collection/mutable/ReusableBuilder.scala index 1cba786c749a..0523c2c3066e 100644 --- a/library/src/scala/collection/mutable/ReusableBuilder.scala +++ b/library/src/scala/collection/mutable/ReusableBuilder.scala @@ -15,6 +15,7 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking /** `ReusableBuilder` is a marker trait that indicates that a `Builder` * can be reused to build more than one instance of a collection. In diff --git a/library/src/scala/collection/mutable/Seq.scala b/library/src/scala/collection/mutable/Seq.scala index ed5416ab7fe6..fed5d12e1a94 100644 --- a/library/src/scala/collection/mutable/Seq.scala +++ b/library/src/scala/collection/mutable/Seq.scala @@ -13,6 +13,7 @@ package scala.collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.collection.{IterableFactoryDefaults, SeqFactory} trait Seq[A] @@ -36,8 +37,8 @@ object Seq extends SeqFactory.Delegate[Seq](ArrayBuffer) * @define coll mutable sequence * @define Coll `mutable.Seq` */ -transparent trait SeqOps[A, +CC[_], +C <: AnyRef] - extends collection.SeqOps[A, CC, C] +transparent trait SeqOps[A, +CC[_] <: caps.Pure, +C <: AnyRef] + extends collection.StrictSeqOps[A, CC, C] with Cloneable[C] { override def clone(): C = { diff --git a/library/src/scala/collection/mutable/SeqMap.scala b/library/src/scala/collection/mutable/SeqMap.scala index bad3ea10b243..2519c09b95b6 100644 --- a/library/src/scala/collection/mutable/SeqMap.scala +++ b/library/src/scala/collection/mutable/SeqMap.scala @@ -15,6 +15,7 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking /** * A generic trait for ordered mutable maps. Concrete classes have to provide @@ -34,7 +35,7 @@ trait SeqMap[K, V] extends Map[K, V] with collection.SeqMap[K, V] with MapOps[K, V, SeqMap, SeqMap[K, V]] with MapFactoryDefaults[K, V, SeqMap, Iterable] { - override def mapFactory: MapFactory[SeqMap] = SeqMap + override def mapFactory: StrictMapFactory[SeqMap] = SeqMap } object SeqMap extends MapFactory.Delegate[SeqMap](LinkedHashMap) diff --git a/library/src/scala/collection/mutable/Set.scala b/library/src/scala/collection/mutable/Set.scala index 299cab4ac23c..808132ea2dbc 100644 --- a/library/src/scala/collection/mutable/Set.scala +++ b/library/src/scala/collection/mutable/Set.scala @@ -13,6 +13,7 @@ package scala.collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.collection.{IterableFactory, IterableFactoryDefaults, IterableOps} /** Base trait for mutable sets */ diff --git a/library/src/scala/collection/mutable/Shrinkable.scala b/library/src/scala/collection/mutable/Shrinkable.scala index 96171c6df024..587530dba044 100644 --- a/library/src/scala/collection/mutable/Shrinkable.scala +++ b/library/src/scala/collection/mutable/Shrinkable.scala @@ -14,6 +14,7 @@ package scala package collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.tailrec /** This trait forms part of collections that can be reduced @@ -53,7 +54,7 @@ trait Shrinkable[-A] { * @param xs the iterator producing the elements to remove. * @return the $coll itself */ - def subtractAll(xs: collection.IterableOnce[A]): this.type = { + def subtractAll(xs: collection.IterableOnce[A]^): this.type = { @tailrec def loop(xs: collection.LinearSeq[A]): Unit = { if (xs.nonEmpty) { subtractOne(xs.head) @@ -75,6 +76,6 @@ trait Shrinkable[-A] { } /** Alias for `subtractAll` */ - @`inline` final def --= (xs: collection.IterableOnce[A]): this.type = subtractAll(xs) + @`inline` final def --= (xs: collection.IterableOnce[A]^): this.type = subtractAll(xs) } diff --git a/library/src/scala/collection/mutable/SortedMap.scala b/library/src/scala/collection/mutable/SortedMap.scala index 534149aaf053..c16a01384785 100644 --- a/library/src/scala/collection/mutable/SortedMap.scala +++ b/library/src/scala/collection/mutable/SortedMap.scala @@ -14,6 +14,7 @@ package scala package collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.collection.{SortedMapFactory, SortedMapFactoryDefaults} /** @@ -38,7 +39,7 @@ trait SortedMap[K, V] * @param d the function mapping keys to values, used for non-present keys * @return a wrapper of the map with a default value */ - override def withDefault(d: K => V): SortedMap[K, V] = new SortedMap.WithDefault[K, V](this, d) + override def withDefault(d: K -> V): SortedMap[K, V] = new SortedMap.WithDefault[K, V](this, d) /** The same map with a given default value. * Note: The default is only used for `apply`. Other methods like `get`, `contains`, `iterator`, `keys`, etc. @@ -67,7 +68,7 @@ transparent trait SortedMapOps[K, V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, object SortedMap extends SortedMapFactory.Delegate[SortedMap](TreeMap) { @SerialVersionUID(3L) - final class WithDefault[K, V](underlying: SortedMap[K, V], defaultValue: K => V) + final class WithDefault[K, V](underlying: SortedMap[K, V], defaultValue: K -> V) extends Map.WithDefault[K, V](underlying, defaultValue) with SortedMap[K, V] with SortedMapOps[K, V, SortedMap, WithDefault[K, V]] @@ -92,10 +93,10 @@ object SortedMap extends SortedMapFactory.Delegate[SortedMap](TreeMap) { override def empty: WithDefault[K, V] = new WithDefault[K, V](underlying.empty, defaultValue) - override def concat[V2 >: V](suffix: collection.IterableOnce[(K, V2)]): SortedMap[K, V2] = + override def concat[V2 >: V](suffix: collection.IterableOnce[(K, V2)]^): SortedMap[K, V2] = underlying.concat(suffix).withDefault(defaultValue) - override protected def fromSpecific(coll: scala.collection.IterableOnce[(K, V)]): WithDefault[K, V] = + override protected def fromSpecific(coll: scala.collection.IterableOnce[(K, V)]^): WithDefault[K, V] = new WithDefault[K, V](sortedMapFactory.from(coll), defaultValue) override protected def newSpecificBuilder: Builder[(K, V), WithDefault[K, V]] = diff --git a/library/src/scala/collection/mutable/SortedSet.scala b/library/src/scala/collection/mutable/SortedSet.scala index dbb31ba5a18d..e56fdc2859f6 100644 --- a/library/src/scala/collection/mutable/SortedSet.scala +++ b/library/src/scala/collection/mutable/SortedSet.scala @@ -15,6 +15,7 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking /** Base type for mutable sorted set collections */ diff --git a/library/src/scala/collection/mutable/Stack.scala b/library/src/scala/collection/mutable/Stack.scala index f888475c7544..8ca4b58a7af6 100644 --- a/library/src/scala/collection/mutable/Stack.scala +++ b/library/src/scala/collection/mutable/Stack.scala @@ -13,6 +13,7 @@ package scala.collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.{migration, nowarn} import scala.collection.generic.DefaultSerializable import scala.collection.{IterableFactoryDefaults, IterableOnce, SeqFactory, StrictOptimizedSeqFactory, StrictOptimizedSeqOps} @@ -77,7 +78,7 @@ class Stack[A] protected (array: Array[AnyRef], start: Int, end: Int) * @param elems the iterable object. * @return the stack with the new elements on top. */ - def pushAll(elems: scala.collection.IterableOnce[A]): this.type = + def pushAll(elems: scala.collection.IterableOnce[A]^): this.type = prependAll(elems match { case it: scala.collection.Seq[A] => it.view.reverse case it => IndexedSeq.from(it).view.reverse @@ -134,7 +135,7 @@ class Stack[A] protected (array: Array[AnyRef], start: Int, end: Int) @SerialVersionUID(3L) object Stack extends StrictOptimizedSeqFactory[Stack] { - def from[A](source: IterableOnce[A]): Stack[A] = empty ++= source + def from[A](source: IterableOnce[A]^): Stack[A] = empty ++= source def empty[A]: Stack[A] = new Stack diff --git a/library/src/scala/collection/mutable/StringBuilder.scala b/library/src/scala/collection/mutable/StringBuilder.scala index dfd778b44b34..91baa747f219 100644 --- a/library/src/scala/collection/mutable/StringBuilder.scala +++ b/library/src/scala/collection/mutable/StringBuilder.scala @@ -13,6 +13,7 @@ package scala.collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.collection.{IterableFactoryDefaults, IterableOnce} import scala.collection.immutable.WrappedString @@ -82,7 +83,7 @@ final class StringBuilder(val underlying: java.lang.StringBuilder) extends Abstr // Methods required to make this an IndexedSeq: def apply(i: Int): Char = underlying.charAt(i) - override protected def fromSpecific(coll: scala.collection.IterableOnce[Char]): StringBuilder = + override protected def fromSpecific(coll: scala.collection.IterableOnce[Char]^): StringBuilder = new StringBuilder() appendAll coll override protected def newSpecificBuilder: Builder[Char, StringBuilder] = @@ -185,7 +186,7 @@ final class StringBuilder(val underlying: java.lang.StringBuilder) extends Abstr * @param xs the characters to be appended. * @return this StringBuilder. */ - def appendAll(xs: IterableOnce[Char]): this.type = { + def appendAll(xs: IterableOnce[Char]^): this.type = { xs match { case x: WrappedString => underlying append x.unwrap case x: ArraySeq.ofChar => underlying append x.array @@ -314,7 +315,7 @@ final class StringBuilder(val underlying: java.lang.StringBuilder) extends Abstr * @return this StringBuilder. * @throws StringIndexOutOfBoundsException if the index is out of bounds. */ - def insertAll(index: Int, xs: IterableOnce[Char]): this.type = + def insertAll(index: Int, xs: IterableOnce[Char]^): this.type = insertAll(index, (ArrayBuilder.make[Char] ++= xs).result()) /** Inserts the given Array[Char] into this sequence at the given index. diff --git a/library/src/scala/collection/mutable/TreeMap.scala b/library/src/scala/collection/mutable/TreeMap.scala index 85b8d897f22a..9ebea9d63956 100644 --- a/library/src/scala/collection/mutable/TreeMap.scala +++ b/library/src/scala/collection/mutable/TreeMap.scala @@ -15,6 +15,7 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.collection.Stepper.EfficientSplit import scala.collection.generic.DefaultSerializable import scala.collection.mutable.{RedBlackTree => RB} @@ -167,6 +168,7 @@ sealed class TreeMap[K, V] private (tree: RB.Tree[K, V])(implicit val ordering: * bound. */ private[this] final class TreeMapProjection(from: Option[K], until: Option[K]) extends TreeMap[K, V](tree) { + this: TreeMapProjection^{} => /** * Given a possible new lower bound, chooses and returns the most constraining one (the maximum). @@ -248,7 +250,7 @@ sealed class TreeMap[K, V] private (tree: RB.Tree[K, V])(implicit val ordering: @SerialVersionUID(3L) object TreeMap extends SortedMapFactory[TreeMap] { - def from[K : Ordering, V](it: IterableOnce[(K, V)]): TreeMap[K, V] = + def from[K : Ordering, V](it: IterableOnce[(K, V)]^): TreeMap[K, V] = Growable.from(empty[K, V], it) def empty[K : Ordering, V]: TreeMap[K, V] = new TreeMap[K, V]() diff --git a/library/src/scala/collection/mutable/TreeSet.scala b/library/src/scala/collection/mutable/TreeSet.scala index 4f302d46906e..f54423cbe12d 100644 --- a/library/src/scala/collection/mutable/TreeSet.scala +++ b/library/src/scala/collection/mutable/TreeSet.scala @@ -14,6 +14,7 @@ package scala package collection.mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.collection.Stepper.EfficientSplit import scala.collection.generic.DefaultSerializable import scala.collection.mutable.{RedBlackTree => RB} @@ -115,6 +116,7 @@ sealed class TreeSet[A] private (private val tree: RB.Tree[A, Null])(implicit va * bound. */ private[this] final class TreeSetProjection(from: Option[A], until: Option[A]) extends TreeSet[A](tree) { + self: TreeSetProjection^{} => /** * Given a possible new lower bound, chooses and returns the most constraining one (the maximum). @@ -194,7 +196,7 @@ object TreeSet extends SortedIterableFactory[TreeSet] { def empty[A : Ordering]: TreeSet[A] = new TreeSet[A]() - def from[E](it: IterableOnce[E])(implicit ordering: Ordering[E]): TreeSet[E] = + def from[E](it: IterableOnce[E]^)(implicit ordering: Ordering[E]): TreeSet[E] = it match { case ts: TreeSet[E] if ordering == ts.ordering => new TreeSet[E](ts.tree.treeCopy()) diff --git a/library/src/scala/collection/mutable/UnrolledBuffer.scala b/library/src/scala/collection/mutable/UnrolledBuffer.scala index 4e60b3555a07..aee908369f0f 100644 --- a/library/src/scala/collection/mutable/UnrolledBuffer.scala +++ b/library/src/scala/collection/mutable/UnrolledBuffer.scala @@ -14,6 +14,7 @@ package scala.collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.tailrec import scala.collection.generic.{CommonErrors, DefaultSerializable} import scala.reflect.ClassTag @@ -196,7 +197,7 @@ sealed class UnrolledBuffer[T](implicit val tag: ClassTag[T]) def insert(idx: Int, elem: T): Unit = insertAll(idx, elem :: Nil) - def insertAll(idx: Int, elems: IterableOnce[T]): Unit = + def insertAll(idx: Int, elems: IterableOnce[T]^): Unit = if (idx >= 0 && idx <= sz) { sz += headptr.insertAll(idx, elems, this) } else throw CommonErrors.indexOutOfBounds(index = idx, max = sz - 1) @@ -208,7 +209,7 @@ sealed class UnrolledBuffer[T](implicit val tag: ClassTag[T]) this } - def patchInPlace(from: Int, patch: collection.IterableOnce[T], replaced: Int): this.type = { + def patchInPlace(from: Int, patch: collection.IterableOnce[T]^, replaced: Int): this.type = { remove(from, replaced) insertAll(from, patch) this @@ -248,7 +249,7 @@ object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] def empty[A : ClassTag]: UnrolledBuffer[A] = new UnrolledBuffer[A] - def from[A : ClassTag](source: scala.collection.IterableOnce[A]): UnrolledBuffer[A] = newBuilder[A].addAll(source) + def from[A : ClassTag](source: scala.collection.IterableOnce[A]^): UnrolledBuffer[A] = newBuilder[A].addAll(source) def newBuilder[A : ClassTag]: UnrolledBuffer[A] = new UnrolledBuffer[A] @@ -264,6 +265,7 @@ object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] /** Unrolled buffer node. */ class Unrolled[T: ClassTag] private[collection] (var size: Int, var array: Array[T], var next: Unrolled[T], val buff: UnrolledBuffer[T] = null) { + this: Unrolled[T]^{} => private[collection] def this() = this(0, new Array[T](unrolledlength), null, null) private[collection] def this(b: UnrolledBuffer[T]) = this(0, new Array[T](unrolledlength), null, b) @@ -378,7 +380,7 @@ object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] if (next eq null) true else false // checks if last node was thrown out } else false - @tailrec final def insertAll(idx: Int, t: scala.collection.IterableOnce[T], buffer: UnrolledBuffer[T]): Int = { + @tailrec final def insertAll(idx: Int, t: scala.collection.IterableOnce[T]^, buffer: UnrolledBuffer[T]): Int = { if (idx < size) { // divide this node at the appropriate position and insert all into head // update new next diff --git a/library/src/scala/collection/mutable/WeakHashMap.scala b/library/src/scala/collection/mutable/WeakHashMap.scala index e0f592faf9f1..37879befb945 100644 --- a/library/src/scala/collection/mutable/WeakHashMap.scala +++ b/library/src/scala/collection/mutable/WeakHashMap.scala @@ -15,6 +15,7 @@ package collection package mutable import scala.language.`2.13` +import language.experimental.captureChecking import scala.annotation.nowarn import scala.collection.convert.JavaCollectionWrappers.{JMapWrapper, JMapWrapperLike} @@ -38,7 +39,7 @@ class WeakHashMap[K, V] extends JMapWrapper[K, V](new java.util.WeakHashMap) with JMapWrapperLike[K, V, WeakHashMap, WeakHashMap[K, V]] with MapFactoryDefaults[K, V, WeakHashMap, Iterable] { override def empty = new WeakHashMap[K, V] - override def mapFactory: MapFactory[WeakHashMap] = WeakHashMap + override def mapFactory: StrictMapFactory[WeakHashMap] = WeakHashMap @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") override protected[this] def stringPrefix = "WeakHashMap" } @@ -48,9 +49,9 @@ class WeakHashMap[K, V] extends JMapWrapper[K, V](new java.util.WeakHashMap) * @define coll weak hash map */ @SerialVersionUID(3L) -object WeakHashMap extends MapFactory[WeakHashMap] { +object WeakHashMap extends StrictMapFactory[WeakHashMap] { def empty[K, V]: WeakHashMap[K,V] = new WeakHashMap[K, V] - def from[K, V](it: collection.IterableOnce[(K, V)]): WeakHashMap[K,V] = Growable.from(empty[K, V], it) + def from[K, V](it: collection.IterableOnce[(K, V)]^): WeakHashMap[K,V] = Growable.from(empty[K, V], it) def newBuilder[K, V]: Builder[(K, V), WeakHashMap[K,V]] = new GrowableBuilder(WeakHashMap.empty[K, V]) } diff --git a/library/src/scala/collection/mutable/package.scala b/library/src/scala/collection/mutable/package.scala index 475aec0afea7..5013839d5587 100644 --- a/library/src/scala/collection/mutable/package.scala +++ b/library/src/scala/collection/mutable/package.scala @@ -13,6 +13,7 @@ package scala.collection import scala.language.`2.13` +import language.experimental.captureChecking package object mutable { @deprecated("Use ArraySeq instead of WrappedArray; it can represent both, boxed and unboxed arrays", "2.13.0") diff --git a/library/src/scala/collection/package.scala b/library/src/scala/collection/package.scala index aced3cb25e03..f43e22b531a7 100644 --- a/library/src/scala/collection/package.scala +++ b/library/src/scala/collection/package.scala @@ -13,6 +13,7 @@ package scala import scala.language.`2.13` +import language.experimental.captureChecking package object collection { @deprecated("Use Iterable instead of Traversable", "2.13.0") @@ -65,7 +66,7 @@ package object collection { /** Splits a sequence into head +: tail. * @return Some((head, tail)) if sequence is non-empty. None otherwise. */ - def unapply[A, CC[_] <: Seq[_], C <: SeqOps[A, CC, C]](t: C with SeqOps[A, CC, C]): Option[(A, C)] = + def unapply[A, CC[_] <: Seq[_], C <: SeqOps[A, CC, C]](t: (C with SeqOps[A, CC, C])^): Option[(A, C^{t})] = if(t.isEmpty) None else Some(t.head -> t.tail) } @@ -75,7 +76,7 @@ package object collection { /** Splits a sequence into init :+ last. * @return Some((init, last)) if sequence is non-empty. None otherwise. */ - def unapply[A, CC[_] <: Seq[_], C <: SeqOps[A, CC, C]](t: C with SeqOps[A, CC, C]): Option[(C, A)] = + def unapply[A, CC[_] <: Seq[_], C <: SeqOps[A, CC, C]](t: (C with SeqOps[A, CC, C])^): Option[(C^{t}, A)] = if(t.isEmpty) None else Some(t.init -> t.last) } diff --git a/library/src/scala/compiletime/ops/any.scala b/library/src/scala/compiletime/ops/any.scala index b3c1930f9715..26e38d234b78 100644 --- a/library/src/scala/compiletime/ops/any.scala +++ b/library/src/scala/compiletime/ops/any.scala @@ -1,6 +1,8 @@ package scala.compiletime package ops +import language.experimental.captureChecking + object any: /** Equality comparison of two singleton types. * ```scala diff --git a/library/src/scala/compiletime/ops/boolean.scala b/library/src/scala/compiletime/ops/boolean.scala index 3e1b5650a519..0ef6785e2fcd 100644 --- a/library/src/scala/compiletime/ops/boolean.scala +++ b/library/src/scala/compiletime/ops/boolean.scala @@ -1,6 +1,8 @@ package scala.compiletime package ops +import language.experimental.captureChecking + object boolean: /** Negation of a `Boolean` singleton type. diff --git a/library/src/scala/compiletime/ops/double.scala b/library/src/scala/compiletime/ops/double.scala index 4bb4527f14a9..789df9c6462c 100644 --- a/library/src/scala/compiletime/ops/double.scala +++ b/library/src/scala/compiletime/ops/double.scala @@ -1,6 +1,8 @@ package scala.compiletime package ops +import language.experimental.captureChecking + object double: /** Addition of two `Double` singleton types. * ```scala diff --git a/library/src/scala/compiletime/ops/float.scala b/library/src/scala/compiletime/ops/float.scala index bd9b5c75f1f1..70a06522f526 100644 --- a/library/src/scala/compiletime/ops/float.scala +++ b/library/src/scala/compiletime/ops/float.scala @@ -1,6 +1,8 @@ package scala.compiletime package ops +import language.experimental.captureChecking + object float: /** Addition of two `Float` singleton types. * ```scala diff --git a/library/src/scala/compiletime/ops/int.scala b/library/src/scala/compiletime/ops/int.scala index b8ec370421e2..b74ee054da6d 100644 --- a/library/src/scala/compiletime/ops/int.scala +++ b/library/src/scala/compiletime/ops/int.scala @@ -1,6 +1,8 @@ package scala.compiletime package ops +import language.experimental.captureChecking + object int: /** Successor of a natural number where zero is the type 0 and successors are reduced as if the definition was: * diff --git a/library/src/scala/compiletime/ops/long.scala b/library/src/scala/compiletime/ops/long.scala index 3bda31e4979c..7188dffb5af8 100644 --- a/library/src/scala/compiletime/ops/long.scala +++ b/library/src/scala/compiletime/ops/long.scala @@ -1,6 +1,8 @@ package scala.compiletime package ops +import language.experimental.captureChecking + object long: /** Successor of a natural number where zero is the type 0 and successors are reduced as if the definition was: * diff --git a/library/src/scala/compiletime/ops/string.scala b/library/src/scala/compiletime/ops/string.scala index 90515c62e55b..9ef5a3fb7b79 100644 --- a/library/src/scala/compiletime/ops/string.scala +++ b/library/src/scala/compiletime/ops/string.scala @@ -1,6 +1,8 @@ package scala.compiletime package ops +import language.experimental.captureChecking + object string: /** Concatenation of two `String` singleton types. * ```scala diff --git a/library/src/scala/compiletime/package.scala b/library/src/scala/compiletime/package.scala index 1a161ebd4a03..3821b9dbd2e7 100644 --- a/library/src/scala/compiletime/package.scala +++ b/library/src/scala/compiletime/package.scala @@ -1,6 +1,8 @@ package scala package compiletime +import language.experimental.captureChecking + import annotation.{compileTimeOnly, experimental} /** Use this method when you have a type, do not have a value for it but want to diff --git a/library/src/scala/compiletime/testing/Error.scala b/library/src/scala/compiletime/testing/Error.scala index fc6ca0bcaabc..65ad302771db 100644 --- a/library/src/scala/compiletime/testing/Error.scala +++ b/library/src/scala/compiletime/testing/Error.scala @@ -1,5 +1,7 @@ package scala.compiletime.testing +import language.experimental.captureChecking + /** Represents a compile-time error. * * @see scala.compiletime.testing.typeCheckErrors diff --git a/library/src/scala/compiletime/testing/ErrorKind.scala b/library/src/scala/compiletime/testing/ErrorKind.scala index c7e38ae22d83..1e45e3250f94 100644 --- a/library/src/scala/compiletime/testing/ErrorKind.scala +++ b/library/src/scala/compiletime/testing/ErrorKind.scala @@ -1,5 +1,7 @@ package scala.compiletime.testing +import language.experimental.captureChecking + /** An error can be either a parse-time or a typecheck-time */ sealed trait ErrorKind // This should be an enum but currently, Dotty lib fails to // compile with an obscure error. diff --git a/library/src/scala/compiletime/testing/package.scala b/library/src/scala/compiletime/testing/package.scala index ebe629244fb3..b2c4cbedf9df 100644 --- a/library/src/scala/compiletime/testing/package.scala +++ b/library/src/scala/compiletime/testing/package.scala @@ -1,6 +1,8 @@ package scala.compiletime package testing +import language.experimental.captureChecking + /** Whether the code type checks in the current context? * * An inline definition with a call to `typeChecks` should be transparent. diff --git a/library/src/scala/deriving/Mirror.scala b/library/src/scala/deriving/Mirror.scala index a7477cf0fb2d..85351e9b7f23 100644 --- a/library/src/scala/deriving/Mirror.scala +++ b/library/src/scala/deriving/Mirror.scala @@ -1,5 +1,7 @@ package scala.deriving +// import language.experimental.captureChecking + /** Mirrors allows typelevel access to enums, case classes and objects, and their sealed parents. */ sealed trait Mirror { @@ -23,7 +25,7 @@ object Mirror { } /** The Mirror for a product type */ - trait Product extends Mirror { + trait Product extends Mirror { self => /** Create a new instance of type `T` with elements taken from product `p`. */ def fromProduct(p: scala.Product): MirroredMonoType diff --git a/library/src/scala/main.scala b/library/src/scala/main.scala index 057fdad4c2fb..f0494bf975e0 100644 --- a/library/src/scala/main.scala +++ b/library/src/scala/main.scala @@ -8,6 +8,8 @@ package scala +import language.experimental.captureChecking + /** An annotation that designates a main function */ class main extends scala.annotation.Annotation {} diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index bfb26023e2c8..9d297ed27d0e 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -1,5 +1,7 @@ package scala.quoted +import language.experimental.captureChecking + /** Quoted expression of type `T`. * * `Expr` has extension methods that are defined in `scala.quoted.Quotes`. diff --git a/library/src/scala/quoted/ExprMap.scala b/library/src/scala/quoted/ExprMap.scala index fbe5dee2b342..8aa513eec4ac 100644 --- a/library/src/scala/quoted/ExprMap.scala +++ b/library/src/scala/quoted/ExprMap.scala @@ -1,5 +1,7 @@ package scala.quoted +import language.experimental.captureChecking + trait ExprMap: /** Map an expression `e` with a type `T` */ diff --git a/library/src/scala/quoted/Exprs.scala b/library/src/scala/quoted/Exprs.scala index 48b0200236bf..2cc2e2944761 100644 --- a/library/src/scala/quoted/Exprs.scala +++ b/library/src/scala/quoted/Exprs.scala @@ -1,5 +1,7 @@ package scala.quoted +import language.experimental.captureChecking + object Exprs: /** Matches literal sequence of literal constant value expressions and return a sequence of values. diff --git a/library/src/scala/quoted/FromExpr.scala b/library/src/scala/quoted/FromExpr.scala index bcd10e4f09ed..2d9e3ebc5ab6 100644 --- a/library/src/scala/quoted/FromExpr.scala +++ b/library/src/scala/quoted/FromExpr.scala @@ -1,5 +1,7 @@ package scala.quoted +import language.experimental.captureChecking + /** A type class for types that can convert a `quoted.Expr[T]` to a `T`. * * - Converts expression containing literal values to their values: diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index b4ea133e47eb..61902c1842f0 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -1,5 +1,7 @@ package scala.quoted +import language.experimental.captureChecking + import scala.annotation.{experimental, implicitNotFound, unused} import scala.reflect.TypeTest diff --git a/library/src/scala/quoted/ToExpr.scala b/library/src/scala/quoted/ToExpr.scala index 6c167c353d87..fbe590c660de 100644 --- a/library/src/scala/quoted/ToExpr.scala +++ b/library/src/scala/quoted/ToExpr.scala @@ -1,5 +1,7 @@ package scala.quoted +import language.experimental.captureChecking + import scala.reflect.ClassTag /** A type class for types that can convert a value of `T` into `quoted.Expr[T]` diff --git a/library/src/scala/quoted/Type.scala b/library/src/scala/quoted/Type.scala index b035bdd6e52f..2d3a0b4fcabb 100644 --- a/library/src/scala/quoted/Type.scala +++ b/library/src/scala/quoted/Type.scala @@ -1,5 +1,7 @@ package scala.quoted +import language.experimental.captureChecking + import scala.annotation.{compileTimeOnly, experimental} /** Type (or type constructor) `T` needed contextually when using `T` in a quoted expression `'{... T ...}` */ diff --git a/library/src/scala/quoted/Varargs.scala b/library/src/scala/quoted/Varargs.scala index 61937fc8fecb..3ffd0b64120d 100644 --- a/library/src/scala/quoted/Varargs.scala +++ b/library/src/scala/quoted/Varargs.scala @@ -1,5 +1,7 @@ package scala.quoted +import language.experimental.captureChecking + /** Expression representation of literal sequence of expressions. * * `Varargs` can be used to create the an expression `args` that will be used as varargs `'{ f($args: _*) }` diff --git a/library/src/scala/quoted/runtime/Expr.scala b/library/src/scala/quoted/runtime/Expr.scala index b95f225c13b3..e3a54a64a06a 100644 --- a/library/src/scala/quoted/runtime/Expr.scala +++ b/library/src/scala/quoted/runtime/Expr.scala @@ -1,6 +1,8 @@ package scala.quoted package runtime +import language.experimental.captureChecking + import scala.annotation.{Annotation, compileTimeOnly} @compileTimeOnly("Illegal reference to `scala.quoted.runtime.Expr`") diff --git a/library/src/scala/quoted/runtime/Patterns.scala b/library/src/scala/quoted/runtime/Patterns.scala index f8e172d30f62..cdbbb224424e 100644 --- a/library/src/scala/quoted/runtime/Patterns.scala +++ b/library/src/scala/quoted/runtime/Patterns.scala @@ -1,5 +1,7 @@ package scala.quoted.runtime +import language.experimental.captureChecking + import scala.annotation.{Annotation, compileTimeOnly} import scala.annotation.experimental diff --git a/library/src/scala/quoted/runtime/QuoteMatching.scala b/library/src/scala/quoted/runtime/QuoteMatching.scala index c95ffe87b5dc..90205266d01a 100644 --- a/library/src/scala/quoted/runtime/QuoteMatching.scala +++ b/library/src/scala/quoted/runtime/QuoteMatching.scala @@ -1,5 +1,7 @@ package scala.quoted.runtime +import language.experimental.captureChecking + import scala.quoted.{Expr, Type} /** Part of the Quotes interface that needs to be implemented by the compiler but is not visible to users */ diff --git a/library/src/scala/quoted/runtime/QuoteUnpickler.scala b/library/src/scala/quoted/runtime/QuoteUnpickler.scala index 63e62658cbb4..aa83ceea2e28 100644 --- a/library/src/scala/quoted/runtime/QuoteUnpickler.scala +++ b/library/src/scala/quoted/runtime/QuoteUnpickler.scala @@ -1,5 +1,7 @@ package scala.quoted.runtime +import language.experimental.captureChecking + import scala.quoted.{Quotes, Expr, Type} /** Part of the Quotes interface that needs to be implemented by the compiler but is not visible to users */ diff --git a/library/src/scala/quoted/runtime/SplicedType.scala b/library/src/scala/quoted/runtime/SplicedType.scala index 1a7b74740d33..0a4242dffa6c 100644 --- a/library/src/scala/quoted/runtime/SplicedType.scala +++ b/library/src/scala/quoted/runtime/SplicedType.scala @@ -1,5 +1,7 @@ package scala.quoted.runtime +import language.experimental.captureChecking + import scala.annotation.{Annotation, compileTimeOnly} /** Artifact of pickled type splices diff --git a/library/src/scala/quoted/runtime/StopMacroExpansion.scala b/library/src/scala/quoted/runtime/StopMacroExpansion.scala index b4d570167621..678c8f93ee66 100644 --- a/library/src/scala/quoted/runtime/StopMacroExpansion.scala +++ b/library/src/scala/quoted/runtime/StopMacroExpansion.scala @@ -1,5 +1,7 @@ package scala.quoted.runtime +import language.experimental.captureChecking + /** Throwable used to abort the expansion of a macro after an error was reported */ class StopMacroExpansion extends Throwable: diff --git a/library/src/scala/reflect/Enum.scala b/library/src/scala/reflect/Enum.scala index 8fb9135f6478..3be8965c97a6 100644 --- a/library/src/scala/reflect/Enum.scala +++ b/library/src/scala/reflect/Enum.scala @@ -1,5 +1,7 @@ package scala.reflect +import language.experimental.captureChecking + /** A base trait of all Scala enum definitions */ transparent trait Enum extends Any, Product, Serializable: diff --git a/library/src/scala/reflect/Selectable.scala b/library/src/scala/reflect/Selectable.scala index ac8e502128bb..52b584bf815e 100644 --- a/library/src/scala/reflect/Selectable.scala +++ b/library/src/scala/reflect/Selectable.scala @@ -1,5 +1,7 @@ package scala.reflect +import language.experimental.captureChecking + /** A class that implements structural selections using Java reflection. * * It can be used as a supertrait of a class or be made available diff --git a/library/src/scala/reflect/TypeTest.scala b/library/src/scala/reflect/TypeTest.scala index 23dea5ec9ced..b122d3b92bc2 100644 --- a/library/src/scala/reflect/TypeTest.scala +++ b/library/src/scala/reflect/TypeTest.scala @@ -1,5 +1,7 @@ package scala.reflect +import language.experimental.captureChecking + /** A `TypeTest[S, T]` contains the logic needed to know at runtime if a value of * type `S` is an instance of `T`. * diff --git a/library/src/scala/reflect/Typeable.scala b/library/src/scala/reflect/Typeable.scala index 3214de77123e..a298d91041c6 100644 --- a/library/src/scala/reflect/Typeable.scala +++ b/library/src/scala/reflect/Typeable.scala @@ -1,5 +1,7 @@ package scala.reflect +import language.experimental.captureChecking + /** A shorhand for `TypeTest[Any, T]`. A `Typeable[T] contains the logic needed to * know at runtime if a value can be downcasted to `T`. * diff --git a/library/src/scala/runtime/$throws.scala b/library/src/scala/runtime/$throws.scala index af35f7402ca3..f02b5b205f47 100644 --- a/library/src/scala/runtime/$throws.scala +++ b/library/src/scala/runtime/$throws.scala @@ -1,4 +1,7 @@ package scala.runtime + +import language.experimental.captureChecking + import annotation.experimental /** A helper type to allow syntax like diff --git a/library/src/scala/runtime/AbstractPartialFunction.scala b/library/src/scala/runtime/AbstractPartialFunction.scala index 895dac79058b..a3506e45c114 100644 --- a/library/src/scala/runtime/AbstractPartialFunction.scala +++ b/library/src/scala/runtime/AbstractPartialFunction.scala @@ -15,6 +15,8 @@ package runtime import scala.language.`2.13` +import language.experimental.captureChecking + /** `AbstractPartialFunction` reformulates all operations of its supertrait `PartialFunction` * in terms of `isDefinedAt` and `applyOrElse`. * @@ -26,7 +28,7 @@ import scala.language.`2.13` * * This trait is used as a basis for implementation of all partial function literals. */ -abstract class AbstractPartialFunction[@specialized(Specializable.Arg) -T1, @specialized(Specializable.Return) +R] extends Function1[T1, R] with PartialFunction[T1, R] { self => +abstract class AbstractPartialFunction[@specialized(Specializable.Arg) -T1, @specialized(Specializable.Return) +R] extends Function1[T1, R] with PartialFunction[T1, R] { // this method must be overridden for better performance, // for backwards compatibility, fall back to the one inherited from PartialFunction // this assumes the old-school partial functions override the apply method, though diff --git a/library/src/scala/runtime/Arrays.scala b/library/src/scala/runtime/Arrays.scala index 085b36c08a1f..2e8b62d76a56 100644 --- a/library/src/scala/runtime/Arrays.scala +++ b/library/src/scala/runtime/Arrays.scala @@ -1,5 +1,7 @@ package scala.runtime +import language.experimental.captureChecking + import scala.annotation.unused import scala.reflect.ClassTag diff --git a/library/src/scala/runtime/EnumValue.scala b/library/src/scala/runtime/EnumValue.scala index 1cd13605279e..62b3af3584ba 100644 --- a/library/src/scala/runtime/EnumValue.scala +++ b/library/src/scala/runtime/EnumValue.scala @@ -1,5 +1,7 @@ package scala.runtime +import language.experimental.captureChecking + transparent trait EnumValue extends Product, Serializable: override def canEqual(that: Any) = this eq that.asInstanceOf[AnyRef] override def productArity: Int = 0 diff --git a/library/src/scala/runtime/FunctionXXL.scala b/library/src/scala/runtime/FunctionXXL.scala index 250e03577e08..be23632e7eff 100644 --- a/library/src/scala/runtime/FunctionXXL.scala +++ b/library/src/scala/runtime/FunctionXXL.scala @@ -1,5 +1,7 @@ package scala.runtime +import language.experimental.captureChecking + /** A function with all parameters grouped in an array. */ trait FunctionXXL { diff --git a/library/src/scala/runtime/LazyVals.scala b/library/src/scala/runtime/LazyVals.scala index deea2201414a..a92712c71a05 100644 --- a/library/src/scala/runtime/LazyVals.scala +++ b/library/src/scala/runtime/LazyVals.scala @@ -1,5 +1,6 @@ package scala.runtime +import language.experimental.captureChecking import java.util.concurrent.CountDownLatch import scala.annotation.* @@ -9,7 +10,7 @@ import scala.annotation.* */ object LazyVals { @nowarn - private val unsafe: sun.misc.Unsafe = { + private val unsafe: sun.misc.Unsafe^ = { // do not let unsafe leak def throwInitializationException() = throw new ExceptionInInitializerError( new IllegalStateException("Can't find instance of sun.misc.Unsafe") diff --git a/library/src/scala/runtime/MatchCase.scala b/library/src/scala/runtime/MatchCase.scala index 2f1a27328bbc..f163413df2a3 100644 --- a/library/src/scala/runtime/MatchCase.scala +++ b/library/src/scala/runtime/MatchCase.scala @@ -1,5 +1,7 @@ package scala.runtime +import language.experimental.captureChecking + /** A type constructor for a case in a match type. */ final abstract class MatchCase[Pat, +Body] diff --git a/library/src/scala/runtime/Scala3RunTime.scala b/library/src/scala/runtime/Scala3RunTime.scala index e503b2d7ec7e..685947f293e0 100644 --- a/library/src/scala/runtime/Scala3RunTime.scala +++ b/library/src/scala/runtime/Scala3RunTime.scala @@ -1,5 +1,7 @@ package scala.runtime +import language.experimental.captureChecking + object Scala3RunTime: // Called by inline def assert's. Extracted to minimize the bytecode size at call site. diff --git a/library/src/scala/runtime/TupleMirror.scala b/library/src/scala/runtime/TupleMirror.scala index e6981df54134..2abe4a5004d5 100644 --- a/library/src/scala/runtime/TupleMirror.scala +++ b/library/src/scala/runtime/TupleMirror.scala @@ -1,5 +1,7 @@ package scala.runtime +import language.experimental.captureChecking + /** A concrete subclass of `scala.deriving.Mirror.Product`, enabling reduction of bytecode size. * as we do not need to synthesize an anonymous Mirror class at every callsite. */ diff --git a/library/src/scala/runtime/TupleXXL.scala b/library/src/scala/runtime/TupleXXL.scala index b4a81d60f817..2da0cbb15396 100644 --- a/library/src/scala/runtime/TupleXXL.scala +++ b/library/src/scala/runtime/TupleXXL.scala @@ -1,5 +1,7 @@ package scala.runtime +import language.experimental.captureChecking + final class TupleXXL private (es: IArray[Object]) extends Product { assert(es.length > 22) diff --git a/library/src/scala/runtime/TupledFunctions.scala b/library/src/scala/runtime/TupledFunctions.scala index 52b39dedbf76..48a3a63f48a4 100644 --- a/library/src/scala/runtime/TupledFunctions.scala +++ b/library/src/scala/runtime/TupledFunctions.scala @@ -1,5 +1,7 @@ package scala.runtime +import language.experimental.captureChecking + import scala.util.TupledFunction import scala.annotation.experimental diff --git a/library/src/scala/runtime/Tuples.scala b/library/src/scala/runtime/Tuples.scala index 66dc486d2a1d..d9a078a5456a 100644 --- a/library/src/scala/runtime/Tuples.scala +++ b/library/src/scala/runtime/Tuples.scala @@ -1,5 +1,7 @@ package scala.runtime +import language.experimental.captureChecking + object Tuples { inline val MaxSpecialized = 22 diff --git a/library/src/scala/runtime/TypeBox.scala b/library/src/scala/runtime/TypeBox.scala index ec2e42eeafc7..629242460564 100644 --- a/library/src/scala/runtime/TypeBox.scala +++ b/library/src/scala/runtime/TypeBox.scala @@ -1,5 +1,7 @@ package scala.runtime +import language.experimental.captureChecking + /** A type for skolems that are generated during capture conversion. Capture conversion * narrows the type of a tree whose type has wildcard arguments. A typical situation * is a tree `t` of type `C[_ >: L <: U]` and an expected type `C[X]` where `X` is an diff --git a/library/src/scala/runtime/coverage/Invoker.scala b/library/src/scala/runtime/coverage/Invoker.scala index 98f71076b2f6..e8156c57095d 100644 --- a/library/src/scala/runtime/coverage/Invoker.scala +++ b/library/src/scala/runtime/coverage/Invoker.scala @@ -1,5 +1,7 @@ package scala.runtime.coverage +import language.experimental.captureChecking + import scala.annotation.internal.sharable import scala.annotation.nowarn import scala.collection.concurrent.TrieMap diff --git a/library/src/scala/runtime/stdLibPatches/Predef.scala b/library/src/scala/runtime/stdLibPatches/Predef.scala index 3ea13255ac73..ed4cf207c925 100644 --- a/library/src/scala/runtime/stdLibPatches/Predef.scala +++ b/library/src/scala/runtime/stdLibPatches/Predef.scala @@ -1,5 +1,7 @@ package scala.runtime.stdLibPatches +import scala.language.experimental.captureChecking + import scala.annotation.experimental import scala.annotation.internal.RuntimeChecked diff --git a/library/src/scala/runtime/stdLibPatches/language.scala b/library/src/scala/runtime/stdLibPatches/language.scala index c16f9eec7fe7..202afbbe5064 100644 --- a/library/src/scala/runtime/stdLibPatches/language.scala +++ b/library/src/scala/runtime/stdLibPatches/language.scala @@ -1,5 +1,7 @@ package scala.runtime.stdLibPatches +import scala.language.experimental.captureChecking + import scala.annotation.compileTimeOnly /** Scala 3 additions and replacements to the `scala.language` object. diff --git a/library/src/scala/util/CommandLineParser.scala b/library/src/scala/util/CommandLineParser.scala index fd239ef231c5..a43148c3699b 100644 --- a/library/src/scala/util/CommandLineParser.scala +++ b/library/src/scala/util/CommandLineParser.scala @@ -1,5 +1,7 @@ package scala.util +import language.experimental.captureChecking + /** A utility object to support command line parsing for @main methods */ object CommandLineParser { @@ -12,7 +14,7 @@ object CommandLineParser { /** Parse command line argument `s`, which has index `n`, as a value of type `T` * @throws ParseError if argument cannot be converted to type `T`. */ - def parseString[T](str: String, n: Int)(using fs: FromString[T]): T = { + def parseString[T](str: String, n: Int)(using fs: FromString[T]^): T = { try fs.fromString(str) catch { case ex: IllegalArgumentException => throw ParseError(n, ex.toString) @@ -22,14 +24,14 @@ object CommandLineParser { /** Parse `n`'th argument in `args` (counting from 0) as a value of type `T` * @throws ParseError if argument does not exist or cannot be converted to type `T`. */ - def parseArgument[T](args: Array[String], n: Int)(using fs: FromString[T]): T = + def parseArgument[T](args: Array[String], n: Int)(using fs: FromString[T]^): T = if n < args.length then parseString(args(n), n) else throw ParseError(n, "more arguments expected") /** Parse all arguments from `n`'th one (counting from 0) as a list of values of type `T` * @throws ParseError if some of the arguments cannot be converted to type `T`. */ - def parseRemainingArguments[T](args: Array[String], n: Int)(using fs: FromString[T]): List[T] = + def parseRemainingArguments[T](args: Array[String], n: Int)(using fs: FromString[T]^): List[T] = if n < args.length then parseString(args(n), n) :: parseRemainingArguments(args, n + 1) else Nil diff --git a/library/src/scala/util/FromDigits.scala b/library/src/scala/util/FromDigits.scala index 44532649868d..73b5a79b7546 100644 --- a/library/src/scala/util/FromDigits.scala +++ b/library/src/scala/util/FromDigits.scala @@ -3,6 +3,7 @@ import scala.math.{BigInt} import quoted.* import annotation.internal.sharable +import language.experimental.captureChecking /** A type class for types that admit numeric literals. */ diff --git a/library/src/scala/util/NotGiven.scala b/library/src/scala/util/NotGiven.scala index 973e709042cb..4896a1c44c80 100644 --- a/library/src/scala/util/NotGiven.scala +++ b/library/src/scala/util/NotGiven.scala @@ -1,5 +1,7 @@ package scala.util +import language.experimental.captureChecking + /** A special class used to implement negation in implicit search. * * Consider the problem of using implicit `i1` for a query type `D` if an implicit diff --git a/library/src/scala/util/TupledFunction.scala b/library/src/scala/util/TupledFunction.scala index 99df6b5b7cae..b5a6bfd7f65b 100644 --- a/library/src/scala/util/TupledFunction.scala +++ b/library/src/scala/util/TupledFunction.scala @@ -1,5 +1,7 @@ package scala.util +import language.experimental.captureChecking + import scala.annotation.implicitNotFound import scala.annotation.experimental @@ -16,7 +18,7 @@ sealed trait TupledFunction[F, G]: @experimental private[scala] object TupledFunction: - def apply[F, G](tupledImpl: F => G, untupledImpl: G => F): TupledFunction[F, G] = + def apply[F, G](tupledImpl: F => G, untupledImpl: G => F): TupledFunction[F, G]^{tupledImpl, untupledImpl} = new TupledFunction[F, G]: def tupled(f: F): G = tupledImpl(f) def untupled(g: G): F = untupledImpl(g) diff --git a/library/src/scala/util/boundary.scala b/library/src/scala/util/boundary.scala index 3039fc70be90..75df7f9b2121 100644 --- a/library/src/scala/util/boundary.scala +++ b/library/src/scala/util/boundary.scala @@ -1,4 +1,6 @@ package scala.util + +import language.experimental.captureChecking import scala.annotation.implicitNotFound /** A boundary that can be exited by `break` calls. @@ -30,15 +32,26 @@ object boundary: /** User code should call `break.apply` instead of throwing this exception * directly. + * + * Note that it is **capability unsafe** to access `label` from a `Break`. + * This field will be marked private in a future release. */ - final class Break[T] private[boundary](val label: Label[T], val value: T) + final class Break[T] private[boundary](val label: Label[T]^{}, val value: T) extends RuntimeException( - /*message*/ null, /*cause*/ null, /*enableSuppression=*/ false, /*writableStackTrace*/ false) + /*message*/ null, /*cause*/ null, /*enableSuppression=*/ false, /*writableStackTrace*/ false): + /** Compare the given [[Label]] to the one this [[Break]] was constructed with. */ + def isSameLabelAs(other: Label[T]) = label eq other + + object Break: + import caps.unsafe.unsafeAssumePure + def apply[T](label: Label[T], value: T) = + // SAFETY: labels cannot leak from [[Break]], and is only used for equality comparison. + new Break(label.unsafeAssumePure, value) /** Labels are targets indicating which boundary will be exited by a `break`. */ @implicitNotFound("explain=A Label is generated from an enclosing `scala.util.boundary` call.\nMaybe that boundary is missing?") - final class Label[-T] + final class Label[-T] extends caps.Control /** Abort current computation and instead return `value` as the value of * the enclosing `boundary` call that created `label`. @@ -60,7 +73,7 @@ object boundary: val local = Label[T]() try body(using local) catch case ex: Break[T] @unchecked => - if ex.label eq local then ex.value + if ex.isSameLabelAs(local) then ex.value else throw ex end boundary diff --git a/library/src/scala/util/control/NonLocalReturns.scala b/library/src/scala/util/control/NonLocalReturns.scala index f59b4c22a4c2..aa446ce33004 100644 --- a/library/src/scala/util/control/NonLocalReturns.scala +++ b/library/src/scala/util/control/NonLocalReturns.scala @@ -1,5 +1,7 @@ package scala.util.control +import language.experimental.captureChecking + import scala.compiletime.uninitialized /** Library implementation of nonlocal return. @@ -22,7 +24,7 @@ import scala.compiletime.uninitialized @deprecated("Use scala.util.boundary instead", "3.3") object NonLocalReturns { @deprecated("Use scala.util.boundary.Break instead", "3.3") - class ReturnThrowable[T] extends ControlThrowable { + class ReturnThrowable[T] extends ControlThrowable, caps.Control { private var myResult: T = uninitialized def throwReturn(result: T): Nothing = { myResult = result diff --git a/project/Build.scala b/project/Build.scala index 9acb1c7d55aa..f9e678eab13f 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1471,6 +1471,7 @@ object Build { /* Configuration of the org.scala-lang:scala3-sbt-bridge:*.**.**-bootstrapped project */ lazy val `scala3-sbt-bridge-bootstrapped` = project.in(file("sbt-bridge")) .dependsOn(`scala3-compiler-bootstrapped-new`) // TODO: Would this actually evict the reference compiler in scala-tool? + .settings(publishSettings) .settings( name := "scala3-sbt-bridge-bootstrapped", moduleName := "scala3-sbt-bridge", @@ -1542,6 +1543,7 @@ object Build { // when compiling a project that depends on scala3-staging (see sbt-test/sbt-dotty/quoted-example-project), // but we always need it to be present on the JVM classpath at runtime. .dependsOn(`scala3-compiler-bootstrapped-new` % "provided; compile->runtime; test->test") + .settings(publishSettings) .settings( name := "scala3-staging", moduleName := "scala3-staging", @@ -1599,6 +1601,7 @@ object Build { // when compiling a project that depends on scala3-tasty-inspector (see sbt-test/sbt-dotty/tasty-inspector-example-project), // but we always need it to be present on the JVM classpath at runtime. .dependsOn(`scala3-compiler-bootstrapped-new` % "provided; compile->runtime; test->test") + .settings(publishSettings) .settings( name := "scala3-tasty-inspector", moduleName := "scala3-tasty-inspector", @@ -1737,6 +1740,7 @@ object Build { /* Configuration of the org.scala-lang:scala-library:*.**.**-bootstrapped project */ lazy val `scala-library-bootstrapped` = project.in(file("library")) .enablePlugins(ScalaLibraryPlugin) + .settings(publishSettings) .settings( name := "scala-library-bootstrapped", moduleName := "scala-library", @@ -1809,6 +1813,7 @@ object Build { /* Configuration of the org.scala-lang:scala3-library_3:*.**.**-bootstrapped project */ lazy val `scala3-library-bootstrapped-new` = project.in(file("library")) .dependsOn(`scala-library-bootstrapped`) + .settings(publishSettings) .settings( name := "scala3-library-bootstrapped", moduleName := "scala3-library", @@ -1851,6 +1856,7 @@ object Build { // We add a dependency to the JVM library to have the classfile available // (as they are not part of this artifact) .dependsOn(`scala3-library-bootstrapped-new`) + .settings(publishSettings) .settings( name := "scala-library-sjs", organization := "org.scala-js", @@ -1961,6 +1967,7 @@ object Build { /* Configuration of the org.scala-lang:scala3-library_sjs1_3:*.**.**-bootstrapped project */ lazy val `scala3-library-sjs` = project.in(file("library-js")) .dependsOn(`scala-library-sjs`) + .settings(publishSettings) .settings( name := "scala3-library-sjs", moduleName := "scala3-library_sjs1", @@ -2064,6 +2071,7 @@ object Build { /* Configuration of the org.scala-lang:tasty-core_3:*.**.**-bootstrapped project */ lazy val `tasty-core-bootstrapped-new` = project.in(file("tasty")) .dependsOn(`scala3-library-bootstrapped-new`) + .settings(publishSettings) .settings( name := "tasty-core-bootstrapped", moduleName := "tasty-core", @@ -2256,6 +2264,7 @@ object Build { /* Configuration of the org.scala-lang:scala3-compiler_3:*.**.**-bootstrapped project */ lazy val `scala3-compiler-bootstrapped-new` = project.in(file("compiler")) .dependsOn(`scala3-interfaces`, `tasty-core-bootstrapped-new`, `scala3-library-bootstrapped-new`) + .settings(publishSettings) .settings( name := "scala3-compiler-bootstrapped", moduleName := "scala3-compiler", @@ -2390,6 +2399,7 @@ object Build { /* Configuration of the org.scala-lang:scaladoc_3:*.**.**-bootstrapped project */ lazy val `scaladoc-new` = project.in(file("scaladoc")) .dependsOn(`scala3-compiler-bootstrapped-new`, `scala3-tasty-inspector-new`) + .settings(publishSettings) .settings( name := "scaladoc", moduleName := "scaladoc", @@ -3527,10 +3537,24 @@ object Build { publishMavenStyle := true, isSnapshot := version.value.contains("SNAPSHOT"), publishTo := { - val centralSnapshots = "https://central.sonatype.com/repository/maven-snapshots/" - if (isSnapshot.value) Some("central-snapshots" at centralSnapshots) - else localStaging.value + if (sys.env.get("NEWNIGHTLY").contains("yes")) { + Some(sys.env("MAVEN_REPOSITORY_REALM") at sys.env("MAVEN_REPOSITORY_URL")) + } else if (isSnapshot.value) { + Some("central-snapshots" at "https://central.sonatype.com/repository/maven-snapshots/") + } else + localStaging.value }, + credentials ++= ( + if (sys.env.get("NEWNIGHTLY").contains("yes")) { + for { + username <- sys.env.get("MAVEN_REPOSITORY_USER") + token <- sys.env.get("MAVEN_REPOSITORY_TOKEN") + } yield Credentials(sys.env("MAVEN_REPOSITORY_REALM"), sys.env("MAVEN_REPOSITORY_HOST"), username, token) + } + else + // The old build credentials are configured differently + None + ).toList, publishConfiguration ~= (_.withOverwrite(true)), publishLocalConfiguration ~= (_.withOverwrite(true)), projectID ~= {id => @@ -3539,76 +3563,8 @@ object Build { }, Test / publishArtifact := false, homepage := Some(url(dottyGithubUrl)), - licenses += (("Apache-2.0", - url("https://www.apache.org/licenses/LICENSE-2.0"))), - scmInfo := Some( - ScmInfo( - url(dottyGithubUrl), - "scm:git:git@github.com:scala/scala3.git" - ) - ), - developers := List( - Developer( - id = "odersky", - name = "Martin Odersky", - email = "martin.odersky@epfl.ch", - url = url("https://github.com/odersky") - ), - Developer( - id = "DarkDimius", - name = "Dmitry Petrashko", - email = "me@d-d.me", - url = url("https://d-d.me") - ), - Developer( - id = "smarter", - name = "Guillaume Martres", - email = "smarter@ubuntu.com", - url = url("http://guillaume.martres.me") - ), - Developer( - id = "felixmulder", - name = "Felix Mulder", - email = "felix.mulder@gmail.com", - url = url("http://felixmulder.com") - ), - Developer( - id = "liufengyun", - name = "Liu Fengyun", - email = "liu@fengy.me", - url = url("https://fengy.me") - ), - Developer( - id = "nicolasstucki", - name = "Nicolas Stucki", - email = "nicolas.stucki@gmail.com", - url = url("https://github.com/nicolasstucki") - ), - Developer( - id = "OlivierBlanvillain", - name = "Olivier Blanvillain", - email = "olivier.blanvillain@gmail.com", - url = url("https://github.com/OlivierBlanvillain") - ), - Developer( - id = "biboudis", - name = "Aggelos Biboudis", - email = "aggelos.biboudis@epfl.ch", - url = url("http://biboudis.github.io") - ), - Developer( - id = "allanrenucci", - name = "Allan Renucci", - email = "allan.renucci@gmail.com", - url = url("https://github.com/allanrenucci") - ), - Developer( - id = "Duhemm", - name = "Martin Duhem", - email = "martin.duhem@gmail.com", - url = url("https://github.com/Duhemm") - ) - ) + licenses += (("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0"))), + scmInfo := Some(ScmInfo(url(dottyGithubUrl), "scm:git:git@github.com:scala/scala3.git")), ) lazy val commonDistSettings = Seq( diff --git a/project/resources/referenceReplacements/sidebar.yml b/project/resources/referenceReplacements/sidebar.yml index 4e40642858c9..2f766e17e715 100644 --- a/project/resources/referenceReplacements/sidebar.yml +++ b/project/resources/referenceReplacements/sidebar.yml @@ -149,9 +149,20 @@ subsection: - page: reference/experimental/numeric-literals.md - page: reference/experimental/explicit-nulls.md - page: reference/experimental/main-annotation.md - - page: reference/experimental/cc.md - - page: reference/experimental/cc-advanced.md - hidden: true + - title: Capture Checking + index: reference/experimental/capture-checking/cc.md + subsection: + - page: reference/experimental/capture-checking/basics.md + - page: reference/experimental/capture-checking/classes.md + - page: reference/experimental/capture-checking/polymorphism.md + - page: reference/experimental/capture-checking/advanced.md + hidden: true + - page: reference/experimental/capture-checking/scoped-caps.md + - page: reference/experimental/capture-checking/classifiers.md + - page: reference/experimental/capture-checking/checked-exceptions.md + - page: reference/experimental/capture-checking/separation-checking.md + - page: reference/experimental/capture-checking/how-to-use.md + - page: reference/experimental/capture-checking/internals.md - page: reference/experimental/tupled-function.md - page: reference/syntax.md - title: Language Versions diff --git a/scala2-library-cc/src/scala/collection/IndexedSeqView.scala b/scala2-library-cc/src/scala/collection/IndexedSeqView.scala index 78f8abb8e327..07698bc09951 100644 --- a/scala2-library-cc/src/scala/collection/IndexedSeqView.scala +++ b/scala2-library-cc/src/scala/collection/IndexedSeqView.scala @@ -160,7 +160,7 @@ object IndexedSeqView { @SerialVersionUID(3L) class Reverse[A](underlying: SomeIndexedSeqOps[A]^) extends SeqView.Reverse[A](underlying) with IndexedSeqView[A] { - override def reverse: IndexedSeqView[A] = underlying match { + override def reverse: IndexedSeqView[A]^{underlying} = underlying match { case x: IndexedSeqView[A] => x case _ => super.reverse } diff --git a/scala2-library-cc/src/scala/collection/View.scala b/scala2-library-cc/src/scala/collection/View.scala index 8002e7c2bb02..612e43fe540a 100644 --- a/scala2-library-cc/src/scala/collection/View.scala +++ b/scala2-library-cc/src/scala/collection/View.scala @@ -152,15 +152,13 @@ object View extends IterableFactory[View] { def apply[A](underlying: Iterable[A]^, p: A => Boolean, isFlipped: Boolean): Filter[A]^{underlying, p} = underlying match { case filter: Filter[A] if filter.isFlipped == isFlipped => - new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped) - .asInstanceOf[Filter[A]^{underlying, p}] + unsafeAssumeSeparate: + new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped) + .asInstanceOf[Filter[A]^{underlying, p}] // !!! asInstanceOf needed once paths were added, see path-patmat-should-be-pos.scala for minimization - //case filter: Filter[A]^{underlying} if filter.isFlipped == isFlipped => - // unsafeAssumeSeparate: // See filter-iterable.scala for a test where a variant of Filter // works without the unsafeAssumeSeparate. But it requires significant // changes compared to the version here. See also Filter in colltest5.CollectionStrawManCC5_1. - // new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped) case _ => new Filter(underlying, p, isFlipped) } } diff --git a/scala2-library-cc/src/scala/collection/mutable/CheckedIndexedSeqView.scala b/scala2-library-cc/src/scala/collection/mutable/CheckedIndexedSeqView.scala index 1c3f669f5358..89e3dfb78d8e 100644 --- a/scala2-library-cc/src/scala/collection/mutable/CheckedIndexedSeqView.scala +++ b/scala2-library-cc/src/scala/collection/mutable/CheckedIndexedSeqView.scala @@ -101,7 +101,7 @@ private[mutable] object CheckedIndexedSeqView { @SerialVersionUID(3L) class Reverse[A](underlying: SomeIndexedSeqOps[A]^)(protected val mutationCount: () ->{cap.rd} Int) extends IndexedSeqView.Reverse[A](underlying) with CheckedIndexedSeqView[A] { - override def reverse: IndexedSeqView[A] = underlying match { + override def reverse: IndexedSeqView[A]^{underlying} = underlying match { case x: IndexedSeqView[A] => x case _ => super.reverse } diff --git a/scaladoc/src/dotty/tools/scaladoc/api.scala b/scaladoc/src/dotty/tools/scaladoc/api.scala index 41ccd8fb2280..8106bd349e34 100644 --- a/scaladoc/src/dotty/tools/scaladoc/api.scala +++ b/scaladoc/src/dotty/tools/scaladoc/api.scala @@ -45,6 +45,7 @@ enum Modifier(val name: String, val prefix: Boolean): case Infix extends Modifier("infix", true) case AbsOverride extends Modifier("abstract override", true) case Update extends Modifier("update", true) + case Consume extends Modifier("consume", true) case class ExtensionTarget(name: String, typeParams: Seq[TypeParameter], argsLists: Seq[TermParameterList], signature: Signature, dri: DRI, position: Long) case class ImplicitConversion(from: DRI, to: DRI) diff --git a/scaladoc/src/dotty/tools/scaladoc/cc/CaptureOps.scala b/scaladoc/src/dotty/tools/scaladoc/cc/CaptureOps.scala index 626162db55ec..3867e1e37ac7 100644 --- a/scaladoc/src/dotty/tools/scaladoc/cc/CaptureOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/cc/CaptureOps.scala @@ -27,7 +27,7 @@ object CaptureDefs: def UseAnnot(using qctx: Quotes) = qctx.reflect.Symbol.requiredClass("scala.caps.use") def ConsumeAnnot(using qctx: Quotes) = - qctx.reflect.Symbol.requiredClass("scala.caps.consume") + qctx.reflect.Symbol.requiredClass("scala.caps.internal.consume") def ReachCapabilityAnnot(using qctx: Quotes) = qctx.reflect.Symbol.requiredClass("scala.annotation.internal.reachCapability") def RootCapabilityAnnot(using qctx: Quotes) = diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala index 06fe658b850e..c96b6899501d 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala @@ -57,7 +57,7 @@ trait BasicSupport: ) val fqNameAllowlist = if ccEnabled then - fqNameAllowlist0 + CaptureDefs.useAnnotFullName + CaptureDefs.consumeAnnotFullName + fqNameAllowlist0 + CaptureDefs.useAnnotFullName else fqNameAllowlist0 val documentedSymbol = summon[Quotes].reflect.Symbol.requiredClass("java.lang.annotation.Documented") val annotations = sym.annotations.filter { a => diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index aa860558f871..0d122bfe37e5 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -23,11 +23,13 @@ trait ClassLikeSupport: extension (symbol: Symbol) { def getExtraModifiers(): Seq[Modifier] = - val mods = SymOps.getExtraModifiers(symbol)() - if ccEnabled && symbol.flags.is(Flags.Mutable)then - mods :+ Modifier.Update - else - mods + var mods = SymOps.getExtraModifiers(symbol)() + if ccEnabled && symbol.flags.is(Flags.Mutable) then + if symbol.hasAnnotation(cc.CaptureDefs.ConsumeAnnot) then + mods :+= Modifier.Consume + else + mods :+= Modifier.Update + mods } private def bareClasslikeKind(using Quotes)(symbol: reflect.Symbol): Kind = @@ -427,12 +429,13 @@ trait ClassLikeSupport: ) = val symbol = argument.symbol val inlinePrefix = if symbol.flags.is(Flags.Inline) then "inline " else "" + val comsumePrefix = if self.ccEnabled && symbol.hasAnnotation(cc.CaptureDefs.ConsumeAnnot) then "consume " else "" val name = symbol.normalizedName val nameIfNotSynthetic = Option.when(!symbol.flags.is(Flags.Synthetic))(name) val defaultValue = Option.when(symbol.flags.is(Flags.HasDefault))(Plain(" = ...")) api.TermParameter( symbol.getAnnotations(), - inlinePrefix + prefix(symbol), + comsumePrefix + inlinePrefix + prefix(symbol), nameIfNotSynthetic, symbol.dri, argument.tpt.asSignature(classDef, symbol.owner) :++ defaultValue, diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala index 0ec9a150235f..4546d05adf6d 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala @@ -535,6 +535,7 @@ trait TypesSupport: private def isCapturedInContext(using Quotes)(ref: reflect.TypeRepr)(using elideThis: reflect.ClassDef): Boolean = import reflect._ ref match + case t if t.isCaptureRoot => true case ReachCapability(c) => isCapturedInContext(c) case ReadOnlyCapability(c) => isCapturedInContext(c) case ThisType(tr) => !elideThis.symbol.typeRef.isPureClass(elideThis) /* is the current class pure? */ diff --git a/tests/neg-custom-args/captures/bad-uses-2.scala b/tests/neg-custom-args/captures/bad-uses-2.scala index d8bc3eca93e6..7a4f26b506a9 100644 --- a/tests/neg-custom-args/captures/bad-uses-2.scala +++ b/tests/neg-custom-args/captures/bad-uses-2.scala @@ -1,13 +1,12 @@ -import caps.{use, consume} +import caps.{use, Mutable} class TestUse: @use def F = ??? // error @use val x = ??? // error @use type T // error def foo[@use T](@use c: T): Unit = ??? // error // error -class TestConsume: - @consume def F = ??? // ok - @consume val x = ??? // error - @consume type T // error - def foo[@consume T](@use c: T): Unit = ??? // error // error +class TestConsume extends Mutable: + consume def F = ??? // ok + consume val x = ??? // error + consume type T // error diff --git a/tests/neg-custom-args/captures/boundary.check b/tests/neg-custom-args/captures/boundary.check new file mode 100644 index 000000000000..dd0b6716adc7 --- /dev/null +++ b/tests/neg-custom-args/captures/boundary.check @@ -0,0 +1,50 @@ +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/boundary.scala:5:4 --------------------------------------- + 4 | boundary[AnyRef^]: + 5 | l1 ?=> // error // error + | ^ + |Found: scala.util.boundary.Label[Object^'s1]^{cap.rd} + |Required: scala.util.boundary.Label[Object^]^² + | + |Note that capability cap² cannot be included in outer capture set 's1. + | + |where: ^ refers to the universal root capability + | ^² refers to a fresh root capability classified as Control in the type of value local + | cap is a fresh root capability classified as Control created in value local when constructing Capability instance scala.util.boundary.Label[Object^'s1] + | cap² is the universal root capability + 6 | boundary[Unit]: l2 ?=> + 7 | boundary.break(l2)(using l1) + 8 | ??? + |-------------------------------------------------------------------------------------------------------------------- + |Inline stack trace + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from boundary.scala:73 +73 | val local = Label[T]() + | ^^^^^^^^^^ + -------------------------------------------------------------------------------------------------------------------- + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/boundary.scala:5:4 --------------------------------------- + 4 | boundary[AnyRef^]: + 5 | l1 ?=> // error // error + | ^ + | Found: scala.util.boundary.Break[AnyRef^] @unchecked + | Required: scala.util.boundary.Break[Object^²] @unchecked + | + | Note that capability cap is not included in capture set {cap²}. + | + | where: ^ refers to the universal root capability + | ^² refers to a fresh root capability created in package + | cap is a fresh root capability created in package + | cap² is the universal root capability + 6 | boundary[Unit]: l2 ?=> + 7 | boundary.break(l2)(using l1) + 8 | ??? + |-------------------------------------------------------------------------------------------------------------------- + |Inline stack trace + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from boundary.scala:75 +75 | catch case ex: Break[T] @unchecked => + | ^ + -------------------------------------------------------------------------------------------------------------------- + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/boundary.scala b/tests/neg-custom-args/captures/boundary.scala new file mode 100644 index 000000000000..9792a0e49fa3 --- /dev/null +++ b/tests/neg-custom-args/captures/boundary.scala @@ -0,0 +1,8 @@ +import scala.util.boundary + +object test: + boundary[AnyRef^]: + l1 ?=> // error // error + boundary[Unit]: l2 ?=> + boundary.break(l2)(using l1) + ??? diff --git a/tests/neg-custom-args/captures/box-adapt-contra.scala b/tests/neg-custom-args/captures/box-adapt-contra.scala index 9c18a9a2c50a..660c82ac8410 100644 --- a/tests/neg-custom-args/captures/box-adapt-contra.scala +++ b/tests/neg-custom-args/captures/box-adapt-contra.scala @@ -1,5 +1,5 @@ -import caps.consume + trait Cap @@ -8,7 +8,7 @@ def useCap[X](x: X): (X -> Unit) -> Unit = ??? def test1(c: Cap^): Unit = val f: (Cap^{c} -> Unit) -> Unit = useCap[Cap^{c}](c) // error -def test2(@consume c: Cap^, d: Cap^): Unit = +def test2(consume c: Cap^, d: Cap^): Unit = def useCap1[X](x: X): (X => Unit) -> Unit = ??? val f1: (Cap^{c} => Unit) ->{c} Unit = useCap1[Cap^{c}](c) // error, was ok when cap was a root diff --git a/tests/neg-custom-args/captures/cc-annot-value-classes2.scala b/tests/neg-custom-args/captures/cc-annot-value-classes2.scala index 5821f9664f6b..f8449b49a444 100644 --- a/tests/neg-custom-args/captures/cc-annot-value-classes2.scala +++ b/tests/neg-custom-args/captures/cc-annot-value-classes2.scala @@ -1,13 +1,13 @@ import language.experimental.captureChecking import caps.* trait Ref extends Mutable -def kill(@consume x: Ref^): Unit = () +def kill(consume x: Ref^): Unit = () class C1: - def myKill(@consume x: Ref^): Unit = kill(x) // ok + def myKill(consume x: Ref^): Unit = kill(x) // ok class C2(val dummy: Int) extends AnyVal: - def myKill(@consume x: Ref^): Unit = kill(x) // ok, too + def myKill(consume x: Ref^): Unit = kill(x) // ok, too class C3: def myKill(x: Ref^): Unit = kill(x) // error diff --git a/tests/neg-custom-args/captures/cc-this-shared.scala b/tests/neg-custom-args/captures/cc-this-shared.scala index 769bae9b5ee6..cd4b6f93af07 100644 --- a/tests/neg-custom-args/captures/cc-this-shared.scala +++ b/tests/neg-custom-args/captures/cc-this-shared.scala @@ -1,4 +1,4 @@ -import caps.consume + class Cap extends caps.SharedCapability @@ -19,7 +19,7 @@ def test(using Cap) = class C4(val f: () => Int) extends C3 // error // The following is a variation of pos/cc-this.scala -def test2(using @consume cc: Cap) = +def test2(using consume cc: Cap) = class C(val x: () => Int): val y: C^ = this diff --git a/tests/neg-custom-args/captures/cc-this.scala b/tests/neg-custom-args/captures/cc-this.scala index d90c0084a14f..3d9f8ca5cf53 100644 --- a/tests/neg-custom-args/captures/cc-this.scala +++ b/tests/neg-custom-args/captures/cc-this.scala @@ -1,4 +1,4 @@ -import caps.consume + class Cap extends caps.ExclusiveCapability @@ -19,7 +19,7 @@ def test(using Cap) = class C4(val f: () => Int) extends C3 // error // The following is a variation of pos/cc-this.scala -def test2(using @consume cc: Cap) = +def test2(using consume cc: Cap) = class C(val x: () => Int): val y: C^ = this diff --git a/tests/neg-custom-args/captures/consume-overrides.scala b/tests/neg-custom-args/captures/consume-overrides.scala index 78f013349a31..f3cd371b0cd7 100644 --- a/tests/neg-custom-args/captures/consume-overrides.scala +++ b/tests/neg-custom-args/captures/consume-overrides.scala @@ -1,15 +1,15 @@ -import caps.consume + trait A[X]: - def foo(@consume x: X): X + def foo(consume x: X): X def bar(x: X): X trait B extends A[C]: def foo(x: C): C // error - def bar(@consume x: C): C // error + def bar(consume x: C): C // error trait B2: def foo(x: C): C - def bar(@consume x: C): C + def bar(consume x: C): C abstract class C extends A[C], B2 // error diff --git a/tests/neg-custom-args/captures/delayedRunops.check b/tests/neg-custom-args/captures/delayedRunops.check index df578f4c6146..05d02af6b5a7 100644 --- a/tests/neg-custom-args/captures/delayedRunops.check +++ b/tests/neg-custom-args/captures/delayedRunops.check @@ -1,31 +1,31 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/delayedRunops.scala:14:4 --------------------------------- -14 | () => // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/delayedRunops.scala:12:4 --------------------------------- +12 | () => // error | ^ | Found: () ->{ops*} Unit | Required: () -> Unit | | Note that capability ops* is not included in capture set {}. -15 | val ops1 = ops -16 | runOps(ops1) +13 | val ops1 = ops +14 | runOps(ops1) | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/delayedRunops.scala:26:4 --------------------------------- -26 | () => // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/delayedRunops.scala:24:4 --------------------------------- +24 | () => // error | ^ | Found: () ->{ops*} Unit | Required: () -> Unit | | Note that capability ops* is not included in capture set {}. -27 | val ops1: List[() ->{ops*} Unit] = ops -28 | runOps(ops1) +25 | val ops1: List[() ->{ops*} Unit] = ops +26 | runOps(ops1) | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/delayedRunops.scala:22:6 ------------------------------------------------------ -22 | runOps(ops1) // error +-- Error: tests/neg-custom-args/captures/delayedRunops.scala:20:6 ------------------------------------------------------ +20 | runOps(ops1) // error | ^^^^^^ | Local reach capability ops* leaks into capture scope of method delayedRunOps2. | You could try to abstract the capabilities referred to by ops* in a capset variable. --- Error: tests/neg-custom-args/captures/delayedRunops.scala:21:16 ----------------------------------------------------- -21 | val ops1: List[() => Unit] = ops // error +-- Error: tests/neg-custom-args/captures/delayedRunops.scala:19:16 ----------------------------------------------------- +19 | val ops1: List[() => Unit] = ops // error | ^^^^^^^^^^^^^^^^ | Separation failure: value ops1's type List[() => Unit] hides non-local parameter ops diff --git a/tests/neg-custom-args/captures/delayedRunops.scala b/tests/neg-custom-args/captures/delayedRunops.scala index a9ad226d4da1..4b89a14537c5 100644 --- a/tests/neg-custom-args/captures/delayedRunops.scala +++ b/tests/neg-custom-args/captures/delayedRunops.scala @@ -1,7 +1,5 @@ import language.experimental.captureChecking -import caps.{use, consume} - def runOps[C^](ops: List[() ->{C} Unit]): Unit = ops.foreach(op => op()) @@ -16,7 +14,7 @@ import caps.{use, consume} runOps(ops1) // unsound: impure operation pretended pure - def delayedRunOps2(@consume ops: List[() => Unit]): () ->{} Unit = + def delayedRunOps2(consume ops: List[() => Unit]): () ->{} Unit = () => val ops1: List[() => Unit] = ops // error runOps(ops1) // error diff --git a/tests/neg-custom-args/captures/i19330.check b/tests/neg-custom-args/captures/i19330.check index e142d6dbddb0..72947fb1bcf6 100644 --- a/tests/neg-custom-args/captures/i19330.check +++ b/tests/neg-custom-args/captures/i19330.check @@ -31,4 +31,4 @@ 16 | val t: () => Logger^ = () => l // error | ^^^^^^^^^^^^^ | Separation failure: value t's type () => Logger^ hides parameter l. - | The parameter needs to be annotated with @consume to allow this. + | The parameter needs to be annotated with consume to allow this. diff --git a/tests/neg-custom-args/captures/i21442.check b/tests/neg-custom-args/captures/i21442.check index aa0a8e53ff7f..0f64325fad98 100644 --- a/tests/neg-custom-args/captures/i21442.check +++ b/tests/neg-custom-args/captures/i21442.check @@ -12,4 +12,4 @@ 17 | val x1: Boxed[IO^] = x // error | ^^^^^^^^^^ | Separation failure: value x1's type Boxed[IO^] hides parameter x. - | The parameter needs to be annotated with @consume to allow this. + | The parameter needs to be annotated with consume to allow this. diff --git a/tests/neg-custom-args/captures/i22005.scala b/tests/neg-custom-args/captures/i22005.scala index fc7d4183f8c3..8bf17661fc82 100644 --- a/tests/neg-custom-args/captures/i22005.scala +++ b/tests/neg-custom-args/captures/i22005.scala @@ -7,4 +7,4 @@ class File(io: IO^) class Handler[C^]: def f(file: File^): File^{C} = file // error - def g(@consume file: File^{C}): File^ = file // ok + def g(consume file: File^{C}): File^ = file // ok diff --git a/tests/neg-custom-args/captures/i23431.check b/tests/neg-custom-args/captures/i23431.check index 624a3b63cb09..722fd6d6e313 100644 --- a/tests/neg-custom-args/captures/i23431.check +++ b/tests/neg-custom-args/captures/i23431.check @@ -48,4 +48,4 @@ 6 | var myIO: IO^ = io1 // error: separation | ^^^ | Separation failure: variable myIO's type IO^ hides parameter io1. - | The parameter needs to be annotated with @consume to allow this. + | The parameter needs to be annotated with consume to allow this. diff --git a/tests/neg-custom-args/captures/linear-buffer-2.check b/tests/neg-custom-args/captures/linear-buffer-2.check index be8f0623418e..6d9493bddb02 100644 --- a/tests/neg-custom-args/captures/linear-buffer-2.check +++ b/tests/neg-custom-args/captures/linear-buffer-2.check @@ -10,7 +10,7 @@ 20 | val buf3 = buf1.append(4) // error | ^^^^ | Separation failure: Illegal access to (buf1 : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 18 + | consume parameter or was used as a prefix to a consume method on line 18 | and therefore is no longer available. | | where: ^ refers to a fresh root capability classified as Mutable in the type of value buf1 @@ -18,7 +18,7 @@ 28 | val buf3 = buf1.append(4) // error | ^^^^ | Separation failure: Illegal access to (buf1 : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 25 + | consume parameter or was used as a prefix to a consume method on line 25 | and therefore is no longer available. | | where: ^ refers to a fresh root capability classified as Mutable in the type of value buf1 @@ -26,7 +26,7 @@ 38 | val buf3 = buf1.append(4) // error | ^^^^ | Separation failure: Illegal access to (buf1 : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 33 + | consume parameter or was used as a prefix to a consume method on line 33 | and therefore is no longer available. | | where: ^ refers to a fresh root capability classified as Mutable in the type of value buf1 @@ -34,6 +34,6 @@ 42 | buf.append(1) // error | ^^^ | Separation failure: (buf : Buffer[Int]^) appears in a loop, therefore it cannot - | be passed to a @consume parameter or be used as a prefix of a @consume method call. + | be passed to a consume parameter or be used as a prefix of a consume method call. | | where: ^ refers to a fresh root capability classified as Mutable in the type of parameter buf diff --git a/tests/neg-custom-args/captures/linear-buffer-2.scala b/tests/neg-custom-args/captures/linear-buffer-2.scala index 78561351838b..d2ed1544fadf 100644 --- a/tests/neg-custom-args/captures/linear-buffer-2.scala +++ b/tests/neg-custom-args/captures/linear-buffer-2.scala @@ -1,25 +1,25 @@ -import caps.{cap, consume, Mutable} +import caps.{cap, Mutable} import language.experimental.captureChecking class Buffer[T] extends Mutable: - @consume update def append(x: T): Buffer[T]^ = this // ok + consume def append(x: T): Buffer[T]^ = this // ok -def app[T](@consume buf: Buffer[T]^, elem: T): Buffer[T]^ = +def app[T](consume buf: Buffer[T]^, elem: T): Buffer[T]^ = buf.append(elem) -def Test(@consume buf: Buffer[Int]^) = +def Test(consume buf: Buffer[Int]^) = val buf1: Buffer[Int]^ = buf.append(1) val buf2 = buf1.append(2) // OK val buf3 = buf.append(3) // error -def Test2(@consume buf: Buffer[Int]^) = +def Test2(consume buf: Buffer[Int]^) = val buf1: Buffer[Int]^ = buf.append(1) val buf2 = if ??? then buf1.append(2) // OK else buf1.append(3) // OK val buf3 = buf1.append(4) // error -def Test3(@consume buf: Buffer[Int]^) = +def Test3(consume buf: Buffer[Int]^) = val buf1: Buffer[Int]^ = buf.append(1) val buf2 = (??? : Int) match case 1 => buf1.append(2) // OK @@ -27,7 +27,7 @@ def Test3(@consume buf: Buffer[Int]^) = case _ => buf1.append(3) val buf3 = buf1.append(4) // error -def Test4(@consume buf: Buffer[Int]^) = +def Test4(consume buf: Buffer[Int]^) = val buf1: Buffer[Int]^ = buf.append(1) val buf2 = (??? : Int) match case 1 => buf1.append(2) // OK @@ -37,6 +37,6 @@ def Test4(@consume buf: Buffer[Int]^) = case 5 => buf1.append(5) val buf3 = buf1.append(4) // error -def Test5(@consume buf: Buffer[Int]^) = +def Test5(consume buf: Buffer[Int]^) = while true do buf.append(1) // error diff --git a/tests/neg-custom-args/captures/linear-buffer.check b/tests/neg-custom-args/captures/linear-buffer.check index 4aaf1794aac1..78ddb54b5535 100644 --- a/tests/neg-custom-args/captures/linear-buffer.check +++ b/tests/neg-custom-args/captures/linear-buffer.check @@ -2,17 +2,17 @@ 5 | update def append(x: T): BadBuffer[T]^ = this // error | ^^^^^^^^^^^^^ | Separation failure: method append's result type BadBuffer[T]^ hides non-local this of class class BadBuffer. - | The access must be in a @consume method to allow this. + | The access must be in a consume method to allow this. -- Error: tests/neg-custom-args/captures/linear-buffer.scala:7:13 ------------------------------------------------------ 7 | def bar: BadBuffer[T]^ = this // error | ^^^^^^^^^^^^^ | Separation failure: method bar's result type BadBuffer[T]^ hides non-local this of class class BadBuffer. - | The access must be in a @consume method to allow this. + | The access must be in a consume method to allow this. -- Error: tests/neg-custom-args/captures/linear-buffer.scala:19:17 ----------------------------------------------------- 19 | val buf3 = app(buf, 3) // error | ^^^ | Separation failure: Illegal access to (buf : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 17 + | consume parameter or was used as a prefix to a consume method on line 17 | and therefore is no longer available. | | where: ^ refers to a fresh root capability classified as Mutable in the type of parameter buf @@ -20,7 +20,7 @@ 26 | val buf3 = app(buf1, 4) // error | ^^^^ | Separation failure: Illegal access to (buf1 : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 24 + | consume parameter or was used as a prefix to a consume method on line 24 | and therefore is no longer available. | | where: ^ refers to a fresh root capability classified as Mutable in the type of value buf1 @@ -28,7 +28,7 @@ 34 | val buf3 = app(buf1, 4) // error | ^^^^ | Separation failure: Illegal access to (buf1 : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 31 + | consume parameter or was used as a prefix to a consume method on line 31 | and therefore is no longer available. | | where: ^ refers to a fresh root capability classified as Mutable in the type of value buf1 @@ -36,7 +36,7 @@ 44 | val buf3 = app(buf1, 4) // error | ^^^^ | Separation failure: Illegal access to (buf1 : Buffer[Int]^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 39 + | consume parameter or was used as a prefix to a consume method on line 39 | and therefore is no longer available. | | where: ^ refers to a fresh root capability classified as Mutable in the type of value buf1 @@ -44,6 +44,6 @@ 48 | app(buf, 1) // error | ^^^ | Separation failure: (buf : Buffer[Int]^) appears in a loop, therefore it cannot - | be passed to a @consume parameter or be used as a prefix of a @consume method call. + | be passed to a consume parameter or be used as a prefix of a consume method call. | | where: ^ refers to a fresh root capability classified as Mutable in the type of parameter buf diff --git a/tests/neg-custom-args/captures/linear-buffer.scala b/tests/neg-custom-args/captures/linear-buffer.scala index 5c6d991579aa..27e2efeeed1f 100644 --- a/tests/neg-custom-args/captures/linear-buffer.scala +++ b/tests/neg-custom-args/captures/linear-buffer.scala @@ -1,4 +1,4 @@ -import caps.{cap, consume, Mutable} +import caps.{cap, Mutable} import language.experimental.captureChecking class BadBuffer[T] extends Mutable: @@ -8,24 +8,24 @@ class BadBuffer[T] extends Mutable: bar class Buffer[T] extends Mutable: - @consume update def append(x: T): Buffer[T]^ = this // ok + consume def append(x: T): Buffer[T]^ = this // ok -def app[T](@consume buf: Buffer[T]^, elem: T): Buffer[T]^ = +def app[T](consume buf: Buffer[T]^, elem: T): Buffer[T]^ = buf.append(elem) -def Test(@consume buf: Buffer[Int]^) = +def Test(consume buf: Buffer[Int]^) = val buf1: Buffer[Int]^ = app(buf, 1) val buf2 = app(buf1, 2) // OK val buf3 = app(buf, 3) // error -def Test2(@consume buf: Buffer[Int]^) = +def Test2(consume buf: Buffer[Int]^) = val buf1: Buffer[Int]^ = app(buf, 1) val buf2 = if ??? then app(buf1, 2) // OK else app(buf1, 3) // OK val buf3 = app(buf1, 4) // error -def Test3(@consume buf: Buffer[Int]^) = +def Test3(consume buf: Buffer[Int]^) = val buf1: Buffer[Int]^ = app(buf, 1) val buf2 = (??? : Int) match case 1 => app(buf1, 2) // OK @@ -33,7 +33,7 @@ def Test3(@consume buf: Buffer[Int]^) = case _ => app(buf1, 3) val buf3 = app(buf1, 4) // error -def Test4(@consume buf: Buffer[Int]^) = +def Test4(consume buf: Buffer[Int]^) = val buf1: Buffer[Int]^ = app(buf, 1) val buf2 = (??? : Int) match case 1 => app(buf1, 2) // OK @@ -43,6 +43,6 @@ def Test4(@consume buf: Buffer[Int]^) = case 5 => app(buf1, 5) val buf3 = app(buf1, 4) // error -def Test5(@consume buf: Buffer[Int]^) = +def Test5(consume buf: Buffer[Int]^) = while true do app(buf, 1) // error diff --git a/tests/neg-custom-args/captures/localReaches.check b/tests/neg-custom-args/captures/localReaches.check index 1df4886e61d5..5acb12457323 100644 --- a/tests/neg-custom-args/captures/localReaches.check +++ b/tests/neg-custom-args/captures/localReaches.check @@ -12,9 +12,9 @@ 14 | val xs: List[() => Unit] = op :: Nil // error | ^^^^^^^^^^^^^^^^ | Separation failure: value xs's type List[() => Unit] hides parameter op. - | The parameter needs to be annotated with @consume to allow this. + | The parameter needs to be annotated with consume to allow this. -- Error: tests/neg-custom-args/captures/localReaches.scala:22:10 ------------------------------------------------------ 22 | val xs: List[() => Unit] = ops // error | ^^^^^^^^^^^^^^^^ | Separation failure: value xs's type List[() => Unit] hides parameter ops. - | The parameter needs to be annotated with @consume to allow this. + | The parameter needs to be annotated with consume to allow this. diff --git a/tests/neg-custom-args/captures/localReaches.scala b/tests/neg-custom-args/captures/localReaches.scala index 9795a6dd099c..d88e3f4bb992 100644 --- a/tests/neg-custom-args/captures/localReaches.scala +++ b/tests/neg-custom-args/captures/localReaches.scala @@ -1,6 +1,6 @@ import language.experimental.captureChecking // no separation checking -import caps.consume + def localReach() = val xs: List[() => Unit] = ??? diff --git a/tests/neg-custom-args/captures/match.scala b/tests/neg-custom-args/captures/match.scala new file mode 100644 index 000000000000..cf0c0f1a3377 --- /dev/null +++ b/tests/neg-custom-args/captures/match.scala @@ -0,0 +1,24 @@ +import language.experimental.captureChecking + +trait A + +case class B(x: AnyRef^) extends A + +def test = + val x: AnyRef^ = new AnyRef + val a: A^{x} = B(x) + + val y1: A = a match + case b: B => b // error: (b: B) becomes B^{x} implicitly + + val y2: A^{x} = a match + case b: B => + val bb: B^{b} = b + val aa: A^{a} = bb + b // ok + + val x3: AnyRef = a match + case B(x2: AnyRef) => x2 // error: we lose some information about field x, but it still cannot be pure + + val x4: AnyRef = a match + case b: B => b.x // error diff --git a/tests/neg-custom-args/captures/non-local-consume.scala b/tests/neg-custom-args/captures/non-local-consume.scala index 0e46146eb7e8..81915a54246f 100644 --- a/tests/neg-custom-args/captures/non-local-consume.scala +++ b/tests/neg-custom-args/captures/non-local-consume.scala @@ -1,29 +1,29 @@ -import caps.{cap, consume, Mutable} +import caps.{cap, Mutable} import language.experimental.captureChecking class Buffer extends Mutable -def f1(@consume buf: Buffer^): Buffer^ = +def f1(consume buf: Buffer^): Buffer^ = val buf1: Buffer^ = buf // OK buf1 -def f2(@consume buf: Buffer^): Buffer^ = +def f2(consume buf: Buffer^): Buffer^ = def g(): Buffer^ = buf // error g() -def f3(@consume buf: Buffer^): Buffer^ = +def f3(consume buf: Buffer^): Buffer^ = val buf1 = buf def g(): Buffer^ = buf1 // error g() -def f4(@consume buf: Buffer^): Buffer^ = +def f4(consume buf: Buffer^): Buffer^ = val buf1: Buffer^ = buf def g(): Buffer^ = buf1 // error g() -def f5(@consume buf: Buffer^): Unit = +def f5(consume buf: Buffer^): Unit = val buf1: Buffer^ = buf def g(): Unit = cc(buf1) // error g() -def cc(@consume buf: Buffer^): Unit = () +def cc(consume buf: Buffer^): Unit = () diff --git a/tests/neg-custom-args/captures/partial-function.scala b/tests/neg-custom-args/captures/partial-function.scala new file mode 100644 index 000000000000..5d75ee193eb6 --- /dev/null +++ b/tests/neg-custom-args/captures/partial-function.scala @@ -0,0 +1,6 @@ +class A extends caps.SharedCapability: + def even(x: Int) = x % 2 == 0 + +def f(a: A): List[Int] -> List[Int] = + (xs: List[Int]) => xs.collect: // error + case x if a.even(x) => 1 diff --git a/tests/neg-custom-args/captures/reaches.check b/tests/neg-custom-args/captures/reaches.check index bcfbc33c8a9f..70896596593e 100644 --- a/tests/neg-custom-args/captures/reaches.check +++ b/tests/neg-custom-args/captures/reaches.check @@ -1,5 +1,5 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:22:13 -------------------------------------- -22 | usingFile: f => // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:20:13 -------------------------------------- +20 | usingFile: f => // error | ^ |Found: (f: File^'s1) ->'s2 Unit |Required: File^ => Unit @@ -9,11 +9,11 @@ |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 -23 | cur = (() => f.write()) :: Nil +21 | cur = (() => f.write()) :: Nil | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:32:13 -------------------------------------- -32 | usingFile: f => // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:30:13 -------------------------------------- +30 | usingFile: f => // error | ^ |Found: (f: File^'s3) ->'s4 Unit |Required: File^ => Unit @@ -23,19 +23,19 @@ |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 -33 | cur.set: -34 | (() => f.write()) :: Nil +31 | cur.set: +32 | (() => f.write()) :: Nil | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/reaches.scala:44:16 ----------------------------------------------------------- -44 | val cur = Ref[List[Proc]](xs) // error +-- Error: tests/neg-custom-args/captures/reaches.scala:42:16 ----------------------------------------------------------- +42 | val cur = Ref[List[Proc]](xs) // error | ^^^^^^^^^^ | Type variable T of constructor Ref cannot be instantiated to List[() => Unit] since | the part () => Unit of that type captures the root capability `cap`. | | where: => refers to the universal root capability --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:46:35 -------------------------------------- -46 | val next: () => Unit = cur.get.head // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:44:35 -------------------------------------- +44 | val next: () => Unit = cur.get.head // error | ^^^^^^^^^^^^ | Found: () => Unit | Required: () =>² Unit @@ -49,8 +49,8 @@ | cap² is a fresh root capability in the type of value next | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:48:20 -------------------------------------- -48 | cur.set(cur.get.tail: List[Proc]) // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:46:20 -------------------------------------- +46 | cur.set(cur.get.tail: List[Proc]) // error | ^^^^^^^^^^^^ | Found: List[() => Unit] | Required: List[() =>² Unit] @@ -64,15 +64,15 @@ | cap² is a fresh root capability created in method runAll3 | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/reaches.scala:54:51 ----------------------------------------------------------- -54 | val id: Id[Proc, Proc] = new Id[Proc, () -> Unit] // error +-- Error: tests/neg-custom-args/captures/reaches.scala:52:51 ----------------------------------------------------------- +52 | val id: Id[Proc, Proc] = new Id[Proc, () -> Unit] // error | ^ | Type variable A of constructor Id cannot be instantiated to () => Unit since | that type captures the root capability `cap`. | | where: => refers to the universal root capability --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:59:27 -------------------------------------- -59 | val id: File^ -> File^ = x => x // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:57:27 -------------------------------------- +57 | val id: File^ -> File^ = x => x // error | ^^^^^^ | Found: (x: File^) ->'s5 File^² | Required: File^ -> File^³ @@ -87,8 +87,8 @@ | cap² is a fresh root capability in the type of value id | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:69:38 -------------------------------------- -69 | val leaked = usingFile[File^{id*}]: f => // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:67:38 -------------------------------------- +67 | val leaked = usingFile[File^{id*}]: f => // error | ^ |Found: (f: File^'s6) ->'s7 File^{id*} |Required: File^ => File^{id*} @@ -98,12 +98,12 @@ |where: => refers to a fresh root capability created in value leaked when checking argument to parameter f of method usingFile | ^ refers to the universal root capability | cap is a fresh root capability created in anonymous function of type (f: File^'s6): File^{id*} of parameter parameter f² of method $anonfun -70 | val f1: File^{id*} = id(f) -71 | f1 +68 | val f1: File^{id*} = id(f) +69 | f1 | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:87:10 -------------------------------------- -87 | ps.map((x, y) => compose1(x, y)) // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:85:10 -------------------------------------- +85 | ps.map((x, y) => compose1(x, y)) // error | ^^^^^^^^^^^^^^^^^^^^^^^ |Found: (x$1: (A^ ->'s8 A^'s9, A^ ->'s10 A^'s11)^'s12) ->'s13 A^'s14 ->'s15 A^'s16 |Required: ((A ->{ps*} A, A ->{ps*} A)) => A^'s17 ->'s18 A^'s19 @@ -114,8 +114,8 @@ | ^ refers to the universal root capability | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:90:10 -------------------------------------- -90 | ps.map((x, y) => compose1(x, y)) // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reaches.scala:88:10 -------------------------------------- +88 | ps.map((x, y) => compose1(x, y)) // error | ^^^^^^^^^^^^^^^^^^^^^^^ |Found: (x$1: (A^ ->'s20 A^'s21, A^ ->'s22 A^'s23)^'s24) ->'s25 A^'s26 ->'s27 A^'s28 |Required: ((A ->{C} A, A ->{C} A)) => A^'s29 ->'s30 A^'s31 @@ -126,16 +126,16 @@ | ^ refers to the universal root capability | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/reaches.scala:39:31 ----------------------------------------------------------- -39 | val next: () => Unit = cur.head // error, use +-- Error: tests/neg-custom-args/captures/reaches.scala:37:31 ----------------------------------------------------------- +37 | val next: () => Unit = cur.head // error, use | ^^^^^^^^ | Local reach capability xs* leaks into capture scope of method runAll2. | You could try to abstract the capabilities referred to by xs* in a capset variable. --- Error: tests/neg-custom-args/captures/reaches.scala:62:36 ----------------------------------------------------------- -62 | val leaked = usingFile[File^{id*}]: f => // error: separation +-- 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 | | where: cap is a fresh root capability created in value id of parameter parameter x of method $anonfun -63 | val f1: File^{id*} = id(f) -64 | f1 +61 | val f1: File^{id*} = id(f) +62 | f1 diff --git a/tests/neg-custom-args/captures/reaches.scala b/tests/neg-custom-args/captures/reaches.scala index d59bc4a7994b..daf326f17f98 100644 --- a/tests/neg-custom-args/captures/reaches.scala +++ b/tests/neg-custom-args/captures/reaches.scala @@ -1,5 +1,3 @@ -import caps.use; -import caps.consume class File: def write(): Unit = ??? @@ -33,7 +31,7 @@ def runAll1[C^](xs: List[() ->{C} Unit]): Unit = cur.set: (() => f.write()) :: Nil -def runAll2(@consume xs: List[Proc]): Unit = +def runAll2(consume xs: List[Proc]): Unit = var cur: List[Proc] = xs while cur.nonEmpty do val next: () => Unit = cur.head // error, use diff --git a/tests/neg-custom-args/captures/reference-cc.check b/tests/neg-custom-args/captures/reference-cc.check new file mode 100644 index 000000000000..dea3e1a5b5e1 --- /dev/null +++ b/tests/neg-custom-args/captures/reference-cc.check @@ -0,0 +1,45 @@ +-- Error: tests/neg-custom-args/captures/reference-cc.scala:42:20 ------------------------------------------------------ +42 | def f = println(c) // error + | ^ + | reference (c : Cap) is not included in the allowed capture set {} + | of the enclosing class A +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reference-cc.scala:44:27 --------------------------------- +44 | val later = usingLogFile { file => () => file.write(0) } // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Found: (file: java.io.FileOutputStream^'s1) ->'s2 () ->{file} Unit + |Required: java.io.FileOutputStream^ => () ->'s3 Unit + | + |Note that capability file cannot be included in outer capture set 's3. + | + |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 + | + | longer explanation available when compiling with `-explain` +-- [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 + | + |Note that capability f cannot be included in outer capture set 's6. + | + |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` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/reference-cc.scala:58:8 ---------------------------------- +58 | try () => xs.map(f).sum // error TODO improve error message + | ^^^^^^^^^^^^^^^^^^^ + |Found: () => Double + |Required: () =>² Double + | + |Note that capability cap is not included in capture set {cap²} + |because cap in method try$1 is not visible from cap² in enclosing function. + | + |where: => refers to a fresh root capability classified as Control in the type of given instance canThrow$1 + | =>² refers 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 + | cap is a fresh root capability classified as Control in the type of given instance canThrow$1 + | cap² is 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 + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/reference-cc.scala b/tests/neg-custom-args/captures/reference-cc.scala new file mode 100644 index 000000000000..c94ad207bb04 --- /dev/null +++ b/tests/neg-custom-args/captures/reference-cc.scala @@ -0,0 +1,59 @@ +import language.experimental.captureChecking +import scala.compiletime.uninitialized +import java.io.* +import language.experimental.saferExceptions + + +def usingLogFile[T](op: FileOutputStream^ => T): T = + val logFile = FileOutputStream("log") + val result = op(logFile) + logFile.close() + result + +trait LzyList[+A]: + def isEmpty: Boolean + def head: A + def tail: LzyList[A]^{this} + def map[B](f: A => B): LzyList[B]^{f, this} = ??? +object LzyList: + def apply[A](xs: A*): LzyList[A] = ??? + +object LzyNil extends LzyList[Nothing]: + def isEmpty = true + def head = ??? + def tail = ??? + +final class LzyCons[+A](hd: A, tl: () => LzyList[A]^) extends LzyList[A]: + private var forced = false + private var cache: LzyList[A]^{this} = uninitialized + private def force = + if !forced then { cache = tl(); forced = true } + cache + + def isEmpty = false + def head = hd + def tail: LzyList[A]^{this} = force +end LzyCons + +class Cap extends caps.SharedCapability +def test(c: Cap) = + class A: + val x: A = this + def f = println(c) // error + + val later = usingLogFile { file => () => file.write(0) } // error + later() // crash + + val xs = usingLogFile: f => // error + LzyList(1, 2, 3).map { x => f.write(x); x * x } + +class LimitExceeded extends Exception + +def testException = + val limit = 10e+10 + def f(x: Double): Double throws LimitExceeded = + if x < limit then x * x else throw LimitExceeded() + + def escaped(xs: Double*): (() => Double) throws LimitExceeded = + try () => xs.map(f).sum // error TODO improve error message + catch case ex: LimitExceeded => () => -1 diff --git a/tests/neg-custom-args/captures/scoped-caps2.check b/tests/neg-custom-args/captures/scoped-caps2.check new file mode 100644 index 000000000000..1a885b4689d3 --- /dev/null +++ b/tests/neg-custom-args/captures/scoped-caps2.check @@ -0,0 +1,93 @@ +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps2.scala:5:23 ---------------------------------- +5 | val _: (x: C) => C = b // error + | ^ + | Found: (b : C => C) + | Required: (x: C^) =>² C^² + | + | Note that the existential capture root in C^ + | cannot subsume the capability cap.. + | + | Note that capability cap is not included in capture set {cap²}. + | + | where: => refers to a fresh root capability in the type of value b + | =>² refers to a fresh root capability in the type of value _$1 + | ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: C^): C^² + | cap is a fresh root capability classified as SharedCapability in the type of value b + | cap² is a fresh root capability in the type of value b + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps2.scala:6:18 ---------------------------------- +6 | val _: C => C = a // error + | ^ + | Found: (a : (x: C) => C) + | Required: C^ =>² C^² + | + | Note that capability cap is not included in capture set {cap²}. + | + | where: => refers to a fresh root capability in the type of value a + | =>² refers to a fresh root capability in the type of value _$2 + | ^ refers to the universal root capability + | ^² refers to a fresh root capability classified as SharedCapability in the type of value _$2 + | cap is a fresh root capability in the type of value a + | cap² is a fresh root capability in the type of value _$2 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps2.scala:8:18 ---------------------------------- +8 | val _: C => C = (x: C) => a(x) // error + | ^^^^^^^^^^^^^^ + | Found: (x: C^) ->{a} C^² + | Required: C^ => C^³ + | + | Note that capability cap is not included in capture set {cap²} + | because cap is not visible from cap² in value _$4. + | + | where: => refers to a fresh root capability in the type of value _$4 + | ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: C^): C^² + | ^³ refers to a fresh root capability classified as SharedCapability in the type of value _$4 + | cap is a root capability associated with the result type of (x: C^): C^² + | cap² is a fresh root capability classified as SharedCapability in the type of value _$4 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps2.scala:14:18 --------------------------------- +14 | val _: C -> C = a // error + | ^ + | Found: (a : (x: C) -> C) + | Required: C^ -> C^² + | + | Note that capability cap is not included in capture set {cap²} + | because cap is not visible from cap² in value _$6. + | + | where: ^ refers to the universal root capability + | ^² refers to a fresh root capability classified as SharedCapability in the type of value _$6 + | cap is a root capability associated with the result type of (x: C^): C^³ + | cap² is a fresh root capability classified as SharedCapability in the type of value _$6 + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps2.scala:15:23 --------------------------------- +15 | val _: (x: C) -> C = (x: C) => b(x) // error + | ^^^^^^^^^^^^^^ + | Found: (x: C^) ->{b} C^² + | Required: (x: C^) -> C^³ + | + | Note that capability b is not included in capture set {}. + | + | where: ^ refers to the universal root capability + | ^² refers to a root capability associated with the result type of (x: C^): C^² + | ^³ refers to a root capability associated with the result type of (x: C^): C^³ + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/scoped-caps2.scala:16:29 --------------------------------- +16 | val _: C -> C = (x: C) => a(x) // error + | ^^^^ + | Found: C^{x} + | Required: C^ + | + | Note that capability x is not included in capture set {cap} + | because (x : C) in enclosing function is not visible from cap in value _$8. + | + | where: ^ refers to a fresh root capability classified as SharedCapability in the type of value _$8 + | cap is a fresh root capability classified as SharedCapability in the type of value _$8 + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/scoped-caps2.scala b/tests/neg-custom-args/captures/scoped-caps2.scala new file mode 100644 index 000000000000..527debb57378 --- /dev/null +++ b/tests/neg-custom-args/captures/scoped-caps2.scala @@ -0,0 +1,17 @@ +class C extends caps.SharedCapability +def test1(c: C) = + val a: (x: C) => C = ??? + val b: C => C = ??? + val _: (x: C) => C = b // error + val _: C => C = a // error + val _: (x: C) => C = (x: C) => b(x) // OK + val _: C => C = (x: C) => a(x) // error + +def test2(c: C) = + val a: (x: C) -> C = ??? + val b: C -> C = ??? + val _: (x: C) -> C = b // OK + val _: C -> C = a // error + val _: (x: C) -> C = (x: C) => b(x) // error + val _: C -> C = (x: C) => a(x) // error + diff --git a/tests/neg-custom-args/captures/sep-consume.check b/tests/neg-custom-args/captures/sep-consume.check index e44d3235afd6..5967947783ae 100644 --- a/tests/neg-custom-args/captures/sep-consume.check +++ b/tests/neg-custom-args/captures/sep-consume.check @@ -2,7 +2,7 @@ 19 | x.put(42) // error | ^ | Separation failure: Illegal access to (x : Ref^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 18 + | consume parameter or was used as a prefix to a consume method on line 18 | and therefore is no longer available. | | where: ^ refers to a fresh root capability classified as Mutable in the type of parameter x @@ -10,11 +10,11 @@ 21 | par(rx, () => x.put(42)) // error | ^ | Separation failure: Illegal access to (x : Ref^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 18 + | consume parameter or was used as a prefix to a consume method on line 18 | and therefore is no longer available. | | where: ^ refers to a fresh root capability classified as Mutable in the type of parameter x -- Error: tests/neg-custom-args/captures/sep-consume.scala:26:16 ------------------------------------------------------- 26 | def foo = bad(f) // error | ^ - | Separation failure: argument to @consume parameter with type (f : () ->{x.rd} Unit) refers to non-local value f + | Separation failure: argument to consume parameter with type (f : () ->{x.rd} Unit) refers to non-local value f diff --git a/tests/neg-custom-args/captures/sep-consume.scala b/tests/neg-custom-args/captures/sep-consume.scala index 7b633b814e88..3caa2713dd95 100644 --- a/tests/neg-custom-args/captures/sep-consume.scala +++ b/tests/neg-custom-args/captures/sep-consume.scala @@ -11,9 +11,9 @@ case class Pair[+A, +B](fst: A, snd: B) // require f and g to be non-interfering def par(f: () => Unit, g: () => Unit): Unit = ??? -def bad(@consume op: () ->{cap.rd} Unit): () => Unit = op +def bad(consume op: () ->{cap.rd} Unit): () => Unit = op -def test2(@consume x: Ref^): Unit = +def test2(consume x: Ref^): Unit = val f: () ->{x.rd} Unit = () => x.get val rx: () => Unit = bad(f) // hides x.rd in the resulting `cap` x.put(42) // error @@ -21,22 +21,22 @@ def test2(@consume x: Ref^): Unit = par(rx, () => x.put(42)) // error par(rx, () => x.get) // ok rd/rd -def test3(@consume x: Ref^): Unit = +def test3(consume x: Ref^): Unit = val f: () ->{x.rd} Unit = () => x.get def foo = bad(f) // error foo() foo() /* -def test4(@consume @use p: Pair[Ref^, Ref^]): Unit = +def test4(consume @use p: Pair[Ref^, Ref^]): Unit = val x: Ref^{p.fst*} = p.fst val y: Ref^{p.snd*} = p.snd badp(Pair(x, y)) println(p.fst.get) -def badp(@consume p: Pair[Ref^, Ref^]): Unit = () +def badp(consume p: Pair[Ref^, Ref^]): Unit = () -def test5(@consume @use p: Pair[Ref^, Ref^]): Unit = +def test5(consume @use p: Pair[Ref^, Ref^]): Unit = badp(p) // ok println(p.fst.get) */ diff --git a/tests/neg-custom-args/captures/sep-lazyListState.check b/tests/neg-custom-args/captures/sep-lazyListState.check index 519d143983bf..5806f9082d58 100644 --- a/tests/neg-custom-args/captures/sep-lazyListState.check +++ b/tests/neg-custom-args/captures/sep-lazyListState.check @@ -2,4 +2,4 @@ 13 | def tail: LazyListIterable[A]^ = tl // error | ^^^^^^^^^^^^^^^^^^^^ | Separation failure: method tail's result type LazyListIterable[A]^ hides non-local this of class class Cons. - | The access must be in a @consume method to allow this. + | The access must be in a consume method to allow this. diff --git a/tests/neg-custom-args/captures/sep-use.scala b/tests/neg-custom-args/captures/sep-use.scala index e89adb0f060e..ec661b15fedc 100644 --- a/tests/neg-custom-args/captures/sep-use.scala +++ b/tests/neg-custom-args/captures/sep-use.scala @@ -1,25 +1,25 @@ -import caps.{cap, consume} +import caps.cap -def test1(@consume io: Object^): Unit = +def test1(consume io: Object^): Unit = val x: () => Unit = () => println(io) println(io) // error println(x) // ok -def test2(@consume io: Object^): Unit = +def test2(consume io: Object^): Unit = def x: () => Unit = () => println(io) // error println(io) // error println(x) // ok -def test3(@consume io: Object^): Unit = +def test3(consume io: Object^): Unit = def xx: (y: Int) => Unit = _ => println(io) // error println(io) // error println(xx(2)) // ok -def test4(@consume io: Object^): Unit = +def test4(consume io: Object^): Unit = def xxx(y: Int): Object^ = io // error println(io) // error diff --git a/tests/neg-custom-args/captures/sep-use2.check b/tests/neg-custom-args/captures/sep-use2.check index d6379639f0bc..8fec0e3bdd83 100644 --- a/tests/neg-custom-args/captures/sep-use2.check +++ b/tests/neg-custom-args/captures/sep-use2.check @@ -14,7 +14,7 @@ 12 | val x4: Object^ = // error | ^^^^^^^ | Separation failure: value x4's type Object^ hides parameter f. - | The parameter needs to be annotated with @consume to allow this. + | The parameter needs to be annotated with consume to allow this. -- Error: tests/neg-custom-args/captures/sep-use2.scala:16:10 ---------------------------------------------------------- 16 | def cc: Object^ = c // error | ^^^^^^^ @@ -61,10 +61,10 @@ |where: ^ refers to a fresh root capability in the type of parameter c | ^² refers to a fresh root capability created in value x4 when checking argument to parameter v1 of method apply -- Error: tests/neg-custom-args/captures/sep-use2.scala:23:10 ---------------------------------------------------------- -23 | val x4: Object^ = // error: ^ hides f*, needs @consume +23 | val x4: Object^ = // error: ^ hides f*, needs consume | ^^^^^^^ | Separation failure: value x4's type Object^ hides parameter f. - | The parameter needs to be annotated with @consume to allow this. + | The parameter needs to be annotated with consume to allow this. -- Error: tests/neg-custom-args/captures/sep-use2.scala:28:8 ----------------------------------------------------------- 28 | { f(c) } // error | ^ @@ -83,7 +83,7 @@ |where: ^ refers to a fresh root capability in the type of parameter c | ^² refers to a fresh root capability created in value x4 when checking argument to parameter v1 of method apply -- Error: tests/neg-custom-args/captures/sep-use2.scala:27:10 ---------------------------------------------------------- -27 | val x4: Object^ = // error: ^ hides f* which refers to c, so c needs @consume +27 | val x4: Object^ = // error: ^ hides f* which refers to c, so c needs consume | ^^^^^^^ | Separation failure: value x4's type Object^ hides parameter c. - | The parameter needs to be annotated with @consume to allow this. + | The parameter needs to be annotated with consume to allow this. diff --git a/tests/neg-custom-args/captures/sep-use2.scala b/tests/neg-custom-args/captures/sep-use2.scala index 088984fab0c6..9ce8a1ade72f 100644 --- a/tests/neg-custom-args/captures/sep-use2.scala +++ b/tests/neg-custom-args/captures/sep-use2.scala @@ -1,7 +1,7 @@ -import caps.consume -def test1(@consume c: Object^, f: (x: Object^) => Object^) = + +def test1(consume c: Object^, f: (x: Object^) => Object^) = def cc: Object^ = c // error val x1 = { f(cc) } // ok @@ -12,19 +12,19 @@ def test1(@consume c: Object^, f: (x: Object^) => Object^) = val x4: Object^ = // error { f(c) } // error -def test2(@consume c: Object^, f: (x: Object^) ->{c} Object^) = +def test2(consume c: Object^, f: (x: Object^) ->{c} Object^) = def cc: Object^ = c // error val x1 = { f(cc) } // error val x4: Object^ = // ^ hides just c, since the Object^ in the result of `f` is existential { f(c) } // error // error -def test3(@consume c: Object^, f: Object^ ->{c} Object^) = - val x4: Object^ = // error: ^ hides f*, needs @consume +def test3(consume c: Object^, f: Object^ ->{c} Object^) = + val x4: Object^ = // error: ^ hides f*, needs consume { f(c) } // error -def test4(c: Object^, @consume f: Object^ ->{c} Object^) = - val x4: Object^ = // error: ^ hides f* which refers to c, so c needs @consume +def test4(c: Object^, consume f: Object^ ->{c} Object^) = + val x4: Object^ = // error: ^ hides f* which refers to c, so c needs consume { f(c) } // error diff --git a/tests/neg-custom-args/captures/sepchecks2.check b/tests/neg-custom-args/captures/sepchecks2.check index e7b8dcbf038f..5529c2a6d1c8 100644 --- a/tests/neg-custom-args/captures/sepchecks2.check +++ b/tests/neg-custom-args/captures/sepchecks2.check @@ -55,9 +55,9 @@ 30 | val x: (Object^, Object^{c}) = (d, c) // error | ^^^^^^^^^^^^^^^^^^^^^ | Separation failure: value x's type (Object^, Object^{c}) hides parameter d. - | The parameter needs to be annotated with @consume to allow this. + | The parameter needs to be annotated with consume to allow this. -- Error: tests/neg-custom-args/captures/sepchecks2.scala:33:9 --------------------------------------------------------- 33 | val x: (Object^, Object^) = (c, d) // error | ^^^^^^^^^^^^^^^^^^ | Separation failure: value x's type (Object^, Object^) hides parameters c and d. - | The parameters need to be annotated with @consume to allow this. + | The parameters need to be annotated with consume to allow this. diff --git a/tests/neg-custom-args/captures/sepchecks2.scala b/tests/neg-custom-args/captures/sepchecks2.scala index 34ca3a56a243..d3310fc8b3ca 100644 --- a/tests/neg-custom-args/captures/sepchecks2.scala +++ b/tests/neg-custom-args/captures/sepchecks2.scala @@ -1,11 +1,11 @@ -import caps.consume + def foo(xs: List[() => Unit], y: Object^) = ??? def bar(x: (Object^, Object^)): Unit = ??? -def Test(@consume c: Object^) = +def Test(consume c: Object^) = val xs: List[() => Unit] = (() => println(c)) :: Nil println(c) // error @@ -14,10 +14,10 @@ def Test2(c: Object^, d: Object^): Unit = val x1: (Object^, Object^) = (c, c) // error val x2: (Object^, Object^{d}) = (d, d) // error -def Test3(@consume c: Object^, @consume d: Object^) = +def Test3(consume c: Object^, consume d: Object^) = val x: (Object^, Object^) = (c, d) // ok -def Test4(@consume c: Object^, @consume d: Object^) = +def Test4(consume c: Object^, consume d: Object^) = val x: (Object^, Object^{c}) = (d, c) // ok def Test5(c: Object^, d: Object^): Unit = diff --git a/tests/neg-custom-args/captures/sepchecks3.scala b/tests/neg-custom-args/captures/sepchecks3.scala index 8cc7d705b42b..abfd263b0ff8 100644 --- a/tests/neg-custom-args/captures/sepchecks3.scala +++ b/tests/neg-custom-args/captures/sepchecks3.scala @@ -1,5 +1,5 @@ -import caps.consume + def foo(xs: List[() => Unit], y: Object^) = ??? @@ -7,6 +7,6 @@ def bar(x: (Object^, Object^)): Unit = ??? def Test(c: Object^): Object^ = c // error -def Test2(@consume c: Object^): Object^ = c // ok +def Test2(consume c: Object^): Object^ = c // ok def Test3(c: Object^): List[Object^] = c :: Nil // error diff --git a/tests/neg-custom-args/captures/sepchecks4.check b/tests/neg-custom-args/captures/sepchecks4.check index 7b587363d1cd..64ebb04239d5 100644 --- a/tests/neg-custom-args/captures/sepchecks4.check +++ b/tests/neg-custom-args/captures/sepchecks4.check @@ -2,12 +2,12 @@ 8 | val x: () => Unit = () => println(io) // error | ^^^^^^^^^^ | Separation failure: value x's type () => Unit hides parameter io. - | The parameter needs to be annotated with @consume to allow this. + | The parameter needs to be annotated with consume to allow this. -- Error: tests/neg-custom-args/captures/sepchecks4.scala:7:25 --------------------------------------------------------- 7 |def bad(io: Object^): () => Unit = // error | ^^^^^^^^^^ | Separation failure: method bad's result type () => Unit hides parameter io. - | The parameter needs to be annotated with @consume to allow this. + | The parameter needs to be annotated with consume to allow this. -- Error: tests/neg-custom-args/captures/sepchecks4.scala:12:6 --------------------------------------------------------- 12 | par(() => println(io))(() => println(io)) // error // (1) | ^^^^^^^^^^^^^^^^^ diff --git a/tests/neg-custom-args/captures/sepchecks4.scala b/tests/neg-custom-args/captures/sepchecks4.scala index d44b31ca02dc..285c5405e215 100644 --- a/tests/neg-custom-args/captures/sepchecks4.scala +++ b/tests/neg-custom-args/captures/sepchecks4.scala @@ -12,5 +12,5 @@ def test(io: Object^): Unit = par(() => println(io))(() => println(io)) // error // (1) val f = bad(io) - par(f)(() => println(io)) // no error, but it is equivalent to (1) and should failimport caps.consume + par(f)(() => println(io)) // no error, but it is equivalent to (1) and should fail diff --git a/tests/neg-custom-args/captures/sepchecks5.check b/tests/neg-custom-args/captures/sepchecks5.check index 63ec3bc73431..9f76ef21f46d 100644 --- a/tests/neg-custom-args/captures/sepchecks5.check +++ b/tests/neg-custom-args/captures/sepchecks5.check @@ -1,18 +1,18 @@ -- Error: tests/neg-custom-args/captures/sepchecks5.scala:12:37 -------------------------------------------------------- 12 |def bad(io: Object^): () => Unit = f(io) // error | ^^ - | Separation failure: argument to @consume parameter with type (io : Object^) refers to parameter io. - | The parameter needs to be annotated with @consume to allow this. + | Separation failure: argument to consume parameter with type (io : Object^) refers to parameter io. + | The parameter needs to be annotated with consume to allow this. -- Error: tests/neg-custom-args/captures/sepchecks5.scala:19:13 -------------------------------------------------------- 19 | val f2 = g(io) // error | ^^ - | Separation failure: argument to @consume parameter with type (io : Object^) refers to parameter io. - | The parameter needs to be annotated with @consume to allow this. + | Separation failure: argument to consume parameter with type (io : Object^) refers to parameter io. + | The parameter needs to be annotated with consume to allow this. -- Error: tests/neg-custom-args/captures/sepchecks5.scala:20:24 -------------------------------------------------------- 20 | par(f2)(() => println(io)) // error | ^^ | Separation failure: Illegal access to (io : Object^), which was passed to a - | @consume parameter or was used as a prefix to a @consume method on line 19 + | consume parameter or was used as a prefix to a consume method on line 19 | and therefore is no longer available. | | where: ^ refers to a fresh root capability in the type of parameter io diff --git a/tests/neg-custom-args/captures/sepchecks5.scala b/tests/neg-custom-args/captures/sepchecks5.scala index 9a16051e64bd..82b7f17108f0 100644 --- a/tests/neg-custom-args/captures/sepchecks5.scala +++ b/tests/neg-custom-args/captures/sepchecks5.scala @@ -1,13 +1,13 @@ -import caps.{cap, consume} +import caps.cap import language.future import language.experimental.captureChecking def par(op1: () => Unit)(op2: () => Unit): Unit = () -def f(@consume io: Object^): () => Unit = +def f(consume io: Object^): () => Unit = () => println(io) -def g(@consume io: Object^): () => Unit = f(io) // ok +def g(consume io: Object^): () => Unit = f(io) // ok def bad(io: Object^): () => Unit = f(io) // error diff --git a/tests/neg-custom-args/captures/unsound-reach-3.scala b/tests/neg-custom-args/captures/unsound-reach-3.scala index 46ed469a308e..56b39097b1ca 100644 --- a/tests/neg-custom-args/captures/unsound-reach-3.scala +++ b/tests/neg-custom-args/captures/unsound-reach-3.scala @@ -1,6 +1,6 @@ import language.experimental.captureChecking -import caps.consume + trait File: def close(): Unit @@ -8,9 +8,9 @@ trait File: def withFile[R](path: String)(op: File^ => R): R = ??? trait Foo[+X]: - def use(@consume x: File^): X + def use(consume x: File^): X class Bar extends Foo[File^]: // error - def use(@consume x: File^): File^ = x // error override + def use(consume x: File^): File^ = x // error override def bad(): Unit = val backdoor: Foo[File^] = new Bar // error (follow-on, since the parent Foo[File^] of bar is illegal). diff --git a/tests/neg-custom-args/captures/unsound-reach-4.check b/tests/neg-custom-args/captures/unsound-reach-4.check index 56e1a0cc1548..573e9a31d068 100644 --- a/tests/neg-custom-args/captures/unsound-reach-4.check +++ b/tests/neg-custom-args/captures/unsound-reach-4.check @@ -20,7 +20,7 @@ | | longer explanation available when compiling with `-explain` -- [E164] Declaration Error: tests/neg-custom-args/captures/unsound-reach-4.scala:17:6 --------------------------------- -17 | def use(@consume x: F): File^ = x // error @consume override +17 | def use(consume x: F): File^ = x // error consume override | ^ | error overriding method use in trait Foo of type (x: File^): File^; | method use of type (@consume x: File^): File^² has incompatible type diff --git a/tests/neg-custom-args/captures/unsound-reach-4.scala b/tests/neg-custom-args/captures/unsound-reach-4.scala index 715dfcc90ef8..c6ad721213c2 100644 --- a/tests/neg-custom-args/captures/unsound-reach-4.scala +++ b/tests/neg-custom-args/captures/unsound-reach-4.scala @@ -2,7 +2,7 @@ import language.experimental.captureChecking; -import caps.consume + trait File: def close(): Unit @@ -14,7 +14,7 @@ type F = File^ trait Foo[+X]: def use(x: F): X class Bar extends Foo[File^]: // error - def use(@consume x: F): File^ = x // error @consume override + def use(consume x: F): File^ = x // error consume override def bad(): Unit = val backdoor: Foo[File^] = new Bar // error (follow-on, since the parent Foo[File^] of bar is illegal). diff --git a/tests/neg-custom-args/captures/unsound-reach-6.check b/tests/neg-custom-args/captures/unsound-reach-6.check index c6f45c15e62b..b9e9b287dace 100644 --- a/tests/neg-custom-args/captures/unsound-reach-6.check +++ b/tests/neg-custom-args/captures/unsound-reach-6.check @@ -22,11 +22,11 @@ | Local reach capability xs* leaks into capture scope of method f. | You could try to abstract the capabilities referred to by xs* in a capset variable. -- Error: tests/neg-custom-args/captures/unsound-reach-6.scala:11:14 --------------------------------------------------- -11 | val z = f(ys) // error @consume failure +11 | val z = f(ys) // error consume failure | ^^ | Local reach capability ys* leaks into capture scope of method test. | You could try to abstract the capabilities referred to by ys* in a capset variable. -- Error: tests/neg-custom-args/captures/unsound-reach-6.scala:19:14 --------------------------------------------------- -19 | val z = f(ys) // error @consume failure +19 | val z = f(ys) // error consume failure | ^^ - |Separation failure: argument to @consume parameter with type List[() ->{io} Unit] refers to non-local parameter io + |Separation failure: argument to consume parameter with type List[() ->{io} Unit] refers to non-local parameter io diff --git a/tests/neg-custom-args/captures/unsound-reach-6.scala b/tests/neg-custom-args/captures/unsound-reach-6.scala index 4643950d78d0..0d737828d466 100644 --- a/tests/neg-custom-args/captures/unsound-reach-6.scala +++ b/tests/neg-custom-args/captures/unsound-reach-6.scala @@ -1,14 +1,14 @@ -import caps.consume + class IO -def f(@consume xs: List[() => Unit]): () => Unit = () => +def f(consume xs: List[() => Unit]): () => Unit = () => println(xs.head) // error def test(io: IO^)(ys: List[() ->{io} Unit]) = val x = () => - val z = f(ys) // error @consume failure + val z = f(ys) // error consume failure z() val _: () -> Unit = x // error () @@ -16,7 +16,7 @@ def test(io: IO^)(ys: List[() ->{io} Unit]) = def test(io: IO^) = def ys: List[() ->{io} Unit] = ??? val x = () => - val z = f(ys) // error @consume failure + val z = f(ys) // error consume failure z() val _: () -> Unit = x // error () diff --git a/tests/neg-macros/i19842-a.check b/tests/neg-macros/i19842-a.check index 4d92739e00ff..33588b7278c2 100644 --- a/tests/neg-macros/i19842-a.check +++ b/tests/neg-macros/i19842-a.check @@ -7,7 +7,7 @@ |Parents in symbol: [class Object, trait Serializer] |Parents in tree: [trait Serializer] | - | at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8) + | at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:10) | at dotty.tools.dotc.transform.TreeChecker$.checkParents(TreeChecker.scala:210) | at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:286) | at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:285) diff --git a/tests/neg-macros/i19842-b.check b/tests/neg-macros/i19842-b.check index 5a2fe6f39d48..29500e10fb25 100644 --- a/tests/neg-macros/i19842-b.check +++ b/tests/neg-macros/i19842-b.check @@ -7,7 +7,7 @@ |Parents in symbol: [class Object, trait Serializer] |Parents in tree: [class Object, trait Serializer, trait Foo] | - | at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8) + | at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:10) | at dotty.tools.dotc.transform.TreeChecker$.checkParents(TreeChecker.scala:210) | at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:286) | at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:285) diff --git a/tests/neg-macros/i23008.check b/tests/neg-macros/i23008.check index 5193fb0ad7c4..c44f7cccb52a 100644 --- a/tests/neg-macros/i23008.check +++ b/tests/neg-macros/i23008.check @@ -7,9 +7,9 @@ | at scala.Predef$.require(Predef.scala:337) | at scala.quoted.runtime.impl.QuotesImpl$reflect$StringConstant$.apply(QuotesImpl.scala:2540) | at scala.quoted.runtime.impl.QuotesImpl$reflect$StringConstant$.apply(QuotesImpl.scala:2539) + | at scala.quoted.ToExpr$StringToExpr.apply(ToExpr.scala:82) | at scala.quoted.ToExpr$StringToExpr.apply(ToExpr.scala:80) - | at scala.quoted.ToExpr$StringToExpr.apply(ToExpr.scala:78) - | at scala.quoted.Expr$.apply(Expr.scala:70) + | at scala.quoted.Expr$.apply(Expr.scala:72) | at Macros$.buildStringCode(Macro_1.scala:9) | |--------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/localReaches.scala b/tests/neg/localReaches.scala index d0935e2d491f..5bf1b9a26303 100644 --- a/tests/neg/localReaches.scala +++ b/tests/neg/localReaches.scala @@ -1,6 +1,6 @@ import language.experimental.captureChecking // no separation checking -import caps.consume + def localReach() = val xs: List[() => Unit] = ??? diff --git a/tests/new/test.scala b/tests/new/test.scala index 2904592cc6b2..23ac0de47b6f 100644 --- a/tests/new/test.scala +++ b/tests/new/test.scala @@ -1 +1,5 @@ -def test[T] = println(T) +class Cap extends caps.ExclusiveCapability + +def test(consume x: Cap) = ??? + +def impl(using consume x: Cap) = ??? diff --git a/tests/pos-custom-args/captures/capt-depfun.scala b/tests/pos-custom-args/captures/capt-depfun.scala index fb6866459aba..359c49e9110f 100644 --- a/tests/pos-custom-args/captures/capt-depfun.scala +++ b/tests/pos-custom-args/captures/capt-depfun.scala @@ -1,5 +1,5 @@ import annotation.retains -import caps.consume + class C type Cap = C @retains[caps.cap.type] @@ -11,7 +11,7 @@ class STR(s: String) val aa: ((x: Cap) -> STR @retains[x.type]) = (x: Cap) => STR("") -def f(@consume y: Cap, z: Cap): STR @retains[caps.cap.type] = +def f(consume y: Cap, z: Cap): STR @retains[caps.cap.type] = val a: ((x: Cap) -> STR @retains[x.type]) = (x: Cap) => STR("") val b = a(y) val c: STR @retains[y.type] = b diff --git a/tests/pos-custom-args/captures/capt-depfun2.scala b/tests/pos-custom-args/captures/capt-depfun2.scala index 101ea6bb94e1..4451deb27c76 100644 --- a/tests/pos-custom-args/captures/capt-depfun2.scala +++ b/tests/pos-custom-args/captures/capt-depfun2.scala @@ -1,10 +1,10 @@ import annotation.retains -import caps.consume + class C type Cap = C @retains[caps.cap.type] class STR -def f(@consume y: Cap, @consume z: Cap) = +def f(consume y: Cap, consume z: Cap) = def g(): C @retains[y.type | z.type] = ??? val ac: ((x: Cap) -> Array[STR @retains[x.type]]) = ??? val dc: Array[? >: STR <: STR^]^{y, z} = ac(g()) // needs to be inferred diff --git a/tests/pos-custom-args/captures/cc-this.scala b/tests/pos-custom-args/captures/cc-this.scala index 6aa21a7a6c70..719a48130c6c 100644 --- a/tests/pos-custom-args/captures/cc-this.scala +++ b/tests/pos-custom-args/captures/cc-this.scala @@ -1,12 +1,12 @@ -import caps.consume + import caps.unsafe.unsafeAssumeSeparate class Cap extends caps.SharedCapability def eff(using Cap): Unit = () -def test(using @consume cc: Cap) = +def test(using consume cc: Cap) = class C(val x: () => Int): val y: C^ = this diff --git a/tests/pos-custom-args/captures/lazyref.scala b/tests/pos-custom-args/captures/lazyref.scala index 6b5d62c743b6..0723aef95271 100644 --- a/tests/pos-custom-args/captures/lazyref.scala +++ b/tests/pos-custom-args/captures/lazyref.scala @@ -1,5 +1,5 @@ -import caps.consume + class Cap extends caps.SharedCapability @@ -14,7 +14,7 @@ def map[A, B](ref: LazyRef[A]^, f: A => B): LazyRef[B]^{f, ref} = def mapc[A, B]: (ref: LazyRef[A]^, f: A => B) => LazyRef[B]^{f, ref} = (ref1, f1) => map[A, B](ref1, f1) -def test(@consume cap1: Cap, @consume cap2: Cap) = +def test(consume cap1: Cap, consume cap2: Cap) = def f(x: Int) = if cap1 == cap1 then x else 0 def g(x: Int) = if cap2 == cap2 then x else 0 val ref1 = LazyRef(() => f(0)) diff --git a/tests/pos-custom-args/captures/partial-function.scala b/tests/pos-custom-args/captures/partial-function.scala new file mode 100644 index 000000000000..e8e99148fcda --- /dev/null +++ b/tests/pos-custom-args/captures/partial-function.scala @@ -0,0 +1,7 @@ +class A extends caps.SharedCapability: + def even(x: Int) = x % 2 == 0 + +def f(a: A): List[Int] ->{a} List[Int] = + (xs: List[Int]) => xs.collect: + case x if a.even(x) => 1 + diff --git a/tests/pos-custom-args/captures/reaches.scala b/tests/pos-custom-args/captures/reaches.scala index eae4c3e8a73c..348346ad1c3f 100644 --- a/tests/pos-custom-args/captures/reaches.scala +++ b/tests/pos-custom-args/captures/reaches.scala @@ -1,6 +1,3 @@ - -import caps.{use, consume} - class C def f(xs: List[C^]) = val y = xs @@ -37,7 +34,7 @@ def cons(x: Proc, xs: List[Proc]): List[() ->{x, xs*} Unit] = val y = x :: xs y -def addOneProc(@consume xs: List[Proc]): List[Proc] = +def addOneProc(consume xs: List[Proc]): List[Proc] = val x: Proc = () => println("hello") val result: List[() ->{x, xs*} Unit] = x :: xs result // OK, we can widen () ->{x, xs*} Unit to cap here. @@ -45,7 +42,7 @@ def addOneProc(@consume xs: List[Proc]): List[Proc] = def compose1[A, B, C](f: A => B, g: B => C): A ->{f, g} C = z => g(f(z)) -def compose2[A, B, C](@consume f: A => B, @consume g: B => C): A => C = +def compose2[A, B, C](consume f: A => B, consume g: B => C): A => C = z => g(f(z)) //def mapCompose[A](ps: List[(A => A, A => A)]): List[A ->{ps*} A] = diff --git a/tests/pos-custom-args/captures/reference-cc.scala b/tests/pos-custom-args/captures/reference-cc.scala new file mode 100644 index 000000000000..ea0dbf6860aa --- /dev/null +++ b/tests/pos-custom-args/captures/reference-cc.scala @@ -0,0 +1,8 @@ +import language.experimental.captureChecking +import java.io.* + +def usingLogFile[T](op: FileOutputStream^ => T): T = + val logFile = FileOutputStream("log") + val result = op(logFile) + logFile.close() + result \ No newline at end of file diff --git a/tests/pos-custom-args/captures/sep-pairs.scala b/tests/pos-custom-args/captures/sep-pairs.scala index 17cb8eb9aa6c..ad22df412e0d 100644 --- a/tests/pos-custom-args/captures/sep-pairs.scala +++ b/tests/pos-custom-args/captures/sep-pairs.scala @@ -1,5 +1,5 @@ import caps.Mutable -import caps.{cap, consume, use} +import caps.cap class Ref extends Mutable: var x = 0 @@ -14,19 +14,19 @@ def mkPair: Pair[Ref^, Ref^] = val p_exact: Pair[Ref^{r1}, Ref^{r2}] = Pair(r1, r2) p_exact -def copyPair[C^, D^](@consume p: Pair[Ref^{C}, Ref^{D}]): Pair[Ref^{C}, Ref^{D}] = +def copyPair[C^, D^](consume p: Pair[Ref^{C}, Ref^{D}]): Pair[Ref^{C}, Ref^{D}] = val x: Ref^{C} = p.fst val y: Ref^{D} = p.snd Pair[Ref^{C}, Ref^{D}](x, y) /* TODO: The following variants don't work -def copyPair1[C^, D^](@consume p: Pair[Ref^{C}, Ref^{D}]): Pair[Ref^{C}, Ref^{D}] = +def copyPair1[C^, D^](consume p: Pair[Ref^{C}, Ref^{D}]): Pair[Ref^{C}, Ref^{D}] = val x: Ref^{C} = p.fst val y: Ref^{D} = p.snd Pair(x, y) -def copyPair2[C^, D^](@consume p: Pair[Ref^{C}, Ref^{D}]): Pair[Ref^, Ref^] = +def copyPair2[C^, D^](consume p: Pair[Ref^{C}, Ref^{D}]): Pair[Ref^, Ref^] = val x: Ref^{C} = p.fst val y: Ref^{D} = p.snd Pair(x, y) diff --git a/tests/pos-custom-args/captures/skolems2.scala b/tests/pos-custom-args/captures/skolems2.scala index 53fbe935e362..754f80b9a36f 100644 --- a/tests/pos-custom-args/captures/skolems2.scala +++ b/tests/pos-custom-args/captures/skolems2.scala @@ -1,8 +1,8 @@ -import caps.consume + import caps.unsafe.unsafeAssumeSeparate -def Test(@consume c: Object^, @consume f: Object^ => Object^) = +def Test(consume c: Object^, consume f: Object^ => Object^) = def cc: Object^ = unsafeAssumeSeparate(c) val x1 = { f(cc) } @@ -13,7 +13,7 @@ def Test(@consume c: Object^, @consume f: Object^ => Object^) = val x4: Object^ = { unsafeAssumeSeparate(f)(cc) } -def Test2(@consume c: Object^, @consume f: Object^ => Object^) = +def Test2(consume c: Object^, consume f: Object^ => Object^) = def cc(): Object^ = unsafeAssumeSeparate(c) val x1 = { f(cc()) } diff --git a/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala b/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala index ffb68c8d0d60..ea0bdc240e0c 100644 --- a/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala +++ b/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala @@ -457,13 +457,12 @@ object CollectionStrawMan5 { def apply[A](underlying: Iterable[A]^, pp: A => Boolean, isFlipped: Boolean): Filter[A]^{underlying, pp} = underlying match case filter: Filter[A] => - new Filter(filter.underlying, a => filter.p(a) && pp(a)) - .asInstanceOf[Filter[A]^{underlying, pp}] - //unsafeAssumeSeparate: + unsafeAssumeSeparate: + new Filter(filter.underlying, a => filter.p(a) && pp(a)) + .asInstanceOf[Filter[A]^{underlying, pp}] // See filter-iterable.scala for a test where a variant of Filter // works without the unsafeAssumeSeparate. But it requires significant // changes compared to the version here. - //new Filter(filter.underlying, a => filter.p(a) && pp(a)) case _ => new Filter(underlying, pp) case class Partition[A](val underlying: Iterable[A]^, p: A => Boolean) { diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index 263e14ae154b..98f2c3a3c870 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -38,7 +38,6 @@ val experimentalDefinitionInLibrary = Set( "scala.caps.ExclusiveCapability", "scala.caps.Mutable", "scala.caps.Read", - "scala.caps.consume", "scala.caps.internal", "scala.caps.internal$", "scala.caps.cap", diff --git a/tests/run/assert-stack.check b/tests/run/assert-stack.check index c2c97450fd57..aec36d2a4f6e 100644 --- a/tests/run/assert-stack.check +++ b/tests/run/assert-stack.check @@ -1,6 +1,6 @@ -scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8) +scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:10) Test$.main(assert-stack.scala:7) -scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:11) +scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:13) Test$.main(assert-stack.scala:12)