Cats-like typeclasses

This commit is contained in:
Eugene Yokota 2021-12-06 02:06:26 -05:00
parent 1d027ec655
commit 00eba85d98
62 changed files with 639 additions and 441 deletions

View File

@ -1,5 +1,6 @@
version = 2.3.2
edition = 2019-10
version = 3.2.1
runner.dialect = scala3
maxColumn = 100
project.git = true
project.excludeFilters = [ "\\Wsbt-test\\W", "\\Winput_sources\\W", "\\Wcontraband-scala\\W" ]
@ -7,7 +8,7 @@ lineEndings = preserve
# https://docs.scala-lang.org/style/scaladoc.html recommends the JavaDoc style.
# scala/scala is written that way too https://github.com/scala/scala/blob/v2.12.2/src/library/scala/Predef.scala
docstrings = JavaDoc
docstrings.style = Asterisk
# This also seems more idiomatic to include whitespace in import x.{ yyy }
spaces.inImportCurlyBraces = true
@ -18,7 +19,7 @@ align.openParenCallSite = false
align.openParenDefnSite = false
# For better code clarity
danglingParentheses = true
danglingParentheses.preset = true
trailingCommas = preserve

View File

@ -180,8 +180,8 @@ def mimaSettingsSince(versions: Seq[String]): Seq[Def.Setting[_]] = Def settings
val scriptedSbtReduxMimaSettings = Def.settings(mimaPreviousArtifacts := Set())
lazy val sbtRoot: Project = (project in file("."))
// .enablePlugins(ScriptedPlugin)
.aggregate(nonRoots: _*)
// .aggregate(nonRoots: _*)
.aggregate(collectionProj)
.settings(
minimalSettings,
onLoadMessage := {
@ -256,49 +256,20 @@ lazy val bundledLauncherProj =
/* ** subproject declarations ** */
val collectionProj = (project in file("internal") / "util-collection")
val collectionProj = (project in file("util-collection"))
.dependsOn(utilPosition)
.settings(
name := "Collections",
testedBaseSettings,
utilCommonSettings,
Util.keywordsSettings,
name := "Collections",
libraryDependencies ++= Seq(sjsonNewScalaJson.value),
libraryDependencies ++= (CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, major)) if major <= 12 => Seq()
case _ => Seq("org.scala-lang.modules" %% "scala-parallel-collections" % "0.2.0")
case _ => Seq(scalaPar)
}),
mimaSettings,
mimaBinaryIssueFilters ++= Seq(
// Added private[sbt] method to capture State attributes.
exclude[ReversedMissingMethodProblem]("sbt.internal.util.AttributeMap.setCond"),
// Dropped in favour of kind-projector's inline type lambda syntax
exclude[MissingClassProblem]("sbt.internal.util.TypeFunctions$P1of2"),
// Dropped in favour of kind-projector's polymorphic lambda literals
exclude[MissingClassProblem]("sbt.internal.util.Param"),
exclude[MissingClassProblem]("sbt.internal.util.Param$"),
// Dropped in favour of plain scala.Function, and its compose method
exclude[MissingClassProblem]("sbt.internal.util.Fn1"),
exclude[DirectMissingMethodProblem]("sbt.internal.util.TypeFunctions.toFn1"),
exclude[DirectMissingMethodProblem]("sbt.internal.util.Types.toFn1"),
// Instead of defining foldr in KList & overriding in KCons,
// it's now abstract in KList and defined in both KCons & KNil.
exclude[FinalMethodProblem]("sbt.internal.util.KNil.foldr"),
exclude[DirectAbstractMethodProblem]("sbt.internal.util.KList.foldr"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.Init*.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.Settings0.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.EvaluateSettings#INode.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.TypeFunctions.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.EvaluateSettings.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.Settings.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.EvaluateSettings#MixedNode.*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.EvaluateSettings#BindNode.this"),
exclude[IncompatibleSignatureProblem](
"sbt.internal.util.EvaluateSettings#BindNode.dependsOn"
),
exclude[IncompatibleSignatureProblem]("sbt.internal.util.Types.some")
),
)
.dependsOn(utilPosition)
// Command line-related utilities.
val completeProj = (project in file("internal") / "util-complete")

View File

@ -18,16 +18,17 @@ object ContextUtil {
/**
* Constructs an object with utility methods for operating in the provided macro context `c`.
* Callers should explicitly specify the type parameter as `c.type` in order to preserve the path dependent types.
* Callers should explicitly specify the type parameter as `c.type` in order to preserve the path
* dependent types.
*/
def apply[C <: blackbox.Context with Singleton](c: C): ContextUtil[C] = new ContextUtil(c: C)
/**
* Helper for implementing a no-argument macro that is introduced via an implicit.
* This method removes the implicit conversion and evaluates the function `f` on the target of the conversion.
* Helper for implementing a no-argument macro that is introduced via an implicit. This method
* removes the implicit conversion and evaluates the function `f` on the target of the conversion.
*
* Given `myImplicitConversion(someValue).extensionMethod`, where `extensionMethod` is a macro that uses this
* method, the result of this method is `f(<Tree of someValue>)`.
* Given `myImplicitConversion(someValue).extensionMethod`, where `extensionMethod` is a macro
* that uses this method, the result of this method is `f(<Tree of someValue>)`.
*/
def selectMacroImpl[T: c.WeakTypeTag](
c: blackbox.Context
@ -46,10 +47,9 @@ object ContextUtil {
}
/**
* Utility methods for macros. Several methods assume that the context's universe is a full compiler
* (`scala.tools.nsc.Global`).
* This is not thread safe due to the underlying Context and related data structures not being thread safe.
* Use `ContextUtil[c.type](c)` to construct.
* Utility methods for macros. Several methods assume that the context's universe is a full compiler
* (`scala.tools.nsc.Global`). This is not thread safe due to the underlying Context and related
* data structures not being thread safe. Use `ContextUtil[c.type](c)` to construct.
*/
final class ContextUtil[C <: blackbox.Context](val ctx: C) {
import ctx.universe.{ Apply => ApplyTree, _ }
@ -64,20 +64,20 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
lazy val alist: Symbol = alistType.typeSymbol.companion
lazy val alistTC: Type = alistType.typeConstructor
/** Modifiers for a local val.*/
/** Modifiers for a local val. */
lazy val localModifiers = Modifiers(NoFlags)
def getPos(sym: Symbol) = if (sym eq null) NoPosition else sym.pos
/**
* Constructs a unique term name with the given prefix within this Context.
* (The current implementation uses Context.freshName, which increments
* Constructs a unique term name with the given prefix within this Context. (The current
* implementation uses Context.freshName, which increments
*/
def freshTermName(prefix: String) = TermName(ctx.freshName("$" + prefix))
/**
* Constructs a new, synthetic, local ValDef Type `tpe`, a unique name,
* Position `pos`, an empty implementation (no rhs), and owned by `owner`.
* Constructs a new, synthetic, local ValDef Type `tpe`, a unique name, Position `pos`, an empty
* implementation (no rhs), and owned by `owner`.
*/
def freshValDef(tpe: Type, pos: Position, owner: Symbol): ValDef = {
val SYNTHETIC = (1 << 21).toLong.asInstanceOf[FlagSet]
@ -91,8 +91,8 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
lazy val parameterModifiers = Modifiers(Flag.PARAM)
/**
* Collects all definitions in the tree for use in checkReferences.
* This excludes definitions in wrapped expressions because checkReferences won't allow nested dereferencing anyway.
* Collects all definitions in the tree for use in checkReferences. This excludes definitions in
* wrapped expressions because checkReferences won't allow nested dereferencing anyway.
*/
def collectDefs(
tree: Tree,
@ -119,8 +119,8 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
}
/**
* A reference is illegal if it is to an M instance defined within the scope of the macro call.
* As an approximation, disallow referenced to any local definitions `defs`.
* A reference is illegal if it is to an M instance defined within the scope of the macro call. As
* an approximation, disallow referenced to any local definitions `defs`.
*/
def illegalReference(defs: collection.Set[Symbol], sym: Symbol, mType: Type): Boolean =
sym != null && sym != NoSymbol && defs.contains(sym) && {
@ -131,8 +131,8 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
}
/**
* A reference is illegal if it is to an M instance defined within the scope of the macro call.
* As an approximation, disallow referenced to any local definitions `defs`.
* A reference is illegal if it is to an M instance defined within the scope of the macro call. As
* an approximation, disallow referenced to any local definitions `defs`.
*/
def illegalReference(defs: collection.Set[Symbol], sym: Symbol): Boolean =
illegalReference(defs, sym, weakTypeOf[Any])
@ -141,7 +141,7 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
/**
* A function that checks the provided tree for illegal references to M instances defined in the
* expression passed to the macro and for illegal dereferencing of M instances.
* expression passed to the macro and for illegal dereferencing of M instances.
*/
def checkReferences(
defs: collection.Set[Symbol],
@ -160,12 +160,15 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
@deprecated("Use that variant that specifies the M instance types to exclude", since = "1.3.0")
/**
* A function that checks the provided tree for illegal references to M instances defined in the
* expression passed to the macro and for illegal dereferencing of M instances.
* expression passed to the macro and for illegal dereferencing of M instances.
*/
def checkReferences(defs: collection.Set[Symbol], isWrapper: PropertyChecker): Tree => Unit =
checkReferences(defs, isWrapper, weakTypeOf[Any])
/** Constructs a ValDef with a parameter modifier, a unique name, with the provided Type and with an empty rhs. */
/**
* Constructs a ValDef with a parameter modifier, a unique name, with the provided Type and with
* an empty rhs.
*/
def freshMethodParameter(tpe: Type): ValDef =
ValDef(parameterModifiers, freshTermName("p"), TypeTree(tpe), EmptyTree)
@ -173,7 +176,7 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
def localValDef(tpt: Tree, rhs: Tree): ValDef =
ValDef(localModifiers, freshTermName("q"), tpt, rhs)
/** Constructs a tuple value of the right TupleN type from the provided inputs.*/
/** Constructs a tuple value of the right TupleN type from the provided inputs. */
def mkTuple(args: List[Tree]): Tree =
global.gen.mkTuple(args.asInstanceOf[List[global.Tree]]).asInstanceOf[ctx.universe.Tree]
@ -202,7 +205,10 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
/** A Type that references the given type variable. */
def refVar(variable: TypeSymbol): Type = variable.toTypeConstructor
/** Constructs a new, synthetic type variable that is a type constructor. For example, in type Y[L[x]], L is such a type variable. */
/**
* Constructs a new, synthetic type variable that is a type constructor. For example, in type
* Y[L[x]], L is such a type variable.
*/
def newTCVariable(owner: Symbol): TypeSymbol = {
val tc = newTypeVariable(owner)
val arg = newTypeVariable(tc, "x");
@ -226,11 +232,17 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
tpe.asInstanceOf[Type]
}
/** Create a Tree that references the `val` represented by `vd`, copying attributes from `replaced`. */
/**
* Create a Tree that references the `val` represented by `vd`, copying attributes from
* `replaced`.
*/
def refVal(replaced: Tree, vd: ValDef): Tree =
treeCopy.Ident(replaced, vd.name).setSymbol(vd.symbol)
/** Creates a Function tree using `functionSym` as the Symbol and changing `initialOwner` to `functionSym` in `body`.*/
/**
* Creates a Function tree using `functionSym` as the Symbol and changing `initialOwner` to
* `functionSym` in `body`.
*/
def createFunction(params: List[ValDef], body: Tree, functionSym: Symbol): Tree = {
changeOwner(body, initialOwner, functionSym)
val f = Function(params, body)
@ -262,7 +274,7 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
def singleton[T <: AnyRef with Singleton](i: T)(implicit it: ctx.TypeTag[i.type]): Symbol =
it.tpe match {
case SingleType(_, sym) if !sym.isFreeTerm && sym.isStatic => sym
case x => sys.error("Instance must be static (was " + x + ").")
case x => sys.error("Instance must be static (was " + x + ").")
}
def select(t: Tree, name: String): Tree = Select(t, TermName(name))
@ -275,12 +287,12 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
}
/**
* Returns a Type representing the type constructor tcp.<name>. For example, given
* `object Demo { type M[x] = List[x] }`, the call `extractTC(Demo, "M")` will return a type representing
* the type constructor `[x] List[x]`.
* Returns a Type representing the type constructor tcp.<name>. For example, given `object Demo {
* type M[x] = List[x] }`, the call `extractTC(Demo, "M")` will return a type representing the
* type constructor `[x] List[x]`.
*/
def extractTC(tcp: AnyRef with Singleton, name: String)(
implicit it: ctx.TypeTag[tcp.type]
def extractTC(tcp: AnyRef with Singleton, name: String)(implicit
it: ctx.TypeTag[tcp.type]
): ctx.Type = {
val itTpe = it.tpe.asInstanceOf[global.Type]
val m = itTpe.nonPrivateMember(global.newTypeName(name))
@ -290,10 +302,10 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
}
/**
* Substitutes wrappers in tree `t` with the result of `subWrapper`.
* A wrapper is a Tree of the form `f[T](v)` for which isWrapper(<Tree of f>, <Underlying Type>, <qual>.target) returns true.
* Typically, `f` is a `Select` or `Ident`.
* The wrapper is replaced with the result of `subWrapper(<Type of T>, <Tree of v>, <wrapper Tree>)`
* Substitutes wrappers in tree `t` with the result of `subWrapper`. A wrapper is a Tree of the
* form `f[T](v)` for which isWrapper(<Tree of f>, <Underlying Type>, <qual>.target) returns true.
* Typically, `f` is a `Select` or `Ident`. The wrapper is replaced with the result of
* `subWrapper(<Type of T>, <Tree of v>, <wrapper Tree>)`
*/
def transformWrappers(
t: Tree,
@ -307,7 +319,11 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) =>
subWrapper(nme.decodedName.toString, targ.tpe, qual, tree) match {
case Converted.Success(t, finalTx) =>
changeOwner(qual, currentOwner, initialOwner) // Fixes https://github.com/sbt/sbt/issues/1150
changeOwner(
qual,
currentOwner,
initialOwner
) // Fixes https://github.com/sbt/sbt/issues/1150
finalTx(t)
case Converted.Failure(p, m) => ctx.abort(p, m)
case _: Converted.NotApplicable[_] => super.transform(tree)

View File

@ -14,8 +14,8 @@ import sbt.internal.util.Types.Id
/**
* The separate hierarchy from Applicative/Monad is for two reasons.
*
* 1. The type constructor is represented as an abstract type because a TypeTag cannot represent a type constructor directly.
* 2. The applicative interface is uncurried.
* 1. The type constructor is represented as an abstract type because a TypeTag cannot represent a
* type constructor directly. 2. The applicative interface is uncurried.
*/
trait Instance {
type M[x]
@ -51,40 +51,41 @@ object Instance {
def idTransform[C <: blackbox.Context with Singleton]: Transform[C, Id] = in => in
/**
* Implementation of a macro that provides a direct syntax for applicative functors and monads.
* It is intended to be used in conjunction with another macro that conditions the inputs.
* Implementation of a macro that provides a direct syntax for applicative functors and monads. It
* is intended to be used in conjunction with another macro that conditions the inputs.
*
* This method processes the Tree `t` to find inputs of the form `wrap[T]( input )`
* This form is typically constructed by another macro that pretends to be able to get a value of type `T`
* from a value convertible to `M[T]`. This `wrap(input)` form has two main purposes.
* First, it identifies the inputs that should be transformed.
* Second, it allows the input trees to be wrapped for later conversion into the appropriate `M[T]` type by `convert`.
* This wrapping is necessary because applying the first macro must preserve the original type,
* but it is useful to delay conversion until the outer, second macro is called. The `wrap` method accomplishes this by
* allowing the original `Tree` and `Type` to be hidden behind the raw `T` type. This method will remove the call to `wrap`
* so that it is not actually called at runtime.
* This method processes the Tree `t` to find inputs of the form `wrap[T]( input )` This form is
* typically constructed by another macro that pretends to be able to get a value of type `T` from
* a value convertible to `M[T]`. This `wrap(input)` form has two main purposes. First, it
* identifies the inputs that should be transformed. Second, it allows the input trees to be
* wrapped for later conversion into the appropriate `M[T]` type by `convert`. This wrapping is
* necessary because applying the first macro must preserve the original type, but it is useful to
* delay conversion until the outer, second macro is called. The `wrap` method accomplishes this
* by allowing the original `Tree` and `Type` to be hidden behind the raw `T` type. This method
* will remove the call to `wrap` so that it is not actually called at runtime.
*
* Each `input` in each expression of the form `wrap[T]( input )` is transformed by `convert`.
* This transformation converts the input Tree to a Tree of type `M[T]`.
* The original wrapped expression `wrap(input)` is replaced by a reference to a new local `val x: T`, where `x` is a fresh name.
* These converted inputs are passed to `builder` as well as the list of these synthetic `ValDef`s.
* The `TupleBuilder` instance constructs a tuple (Tree) from the inputs and defines the right hand side of the vals
* that unpacks the tuple containing the results of the inputs.
* This transformation converts the input Tree to a Tree of type `M[T]`. The original wrapped
* expression `wrap(input)` is replaced by a reference to a new local `val x: T`, where `x` is a
* fresh name. These converted inputs are passed to `builder` as well as the list of these
* synthetic `ValDef`s. The `TupleBuilder` instance constructs a tuple (Tree) from the inputs and
* defines the right hand side of the vals that unpacks the tuple containing the results of the
* inputs.
*
* The constructed tuple of inputs and the code that unpacks the results of the inputs are then passed to the `i`,
* which is an implementation of `Instance` that is statically accessible.
* An Instance defines a applicative functor associated with a specific type constructor and, if it implements MonadInstance as well, a monad.
* Typically, it will be either a top-level module or a stable member of a top-level module (such as a val or a nested module).
* The `with Singleton` part of the type verifies some cases at macro compilation time,
* while the full check for static accessibility is done at macro expansion time.
* Note: Ideally, the types would verify that `i: MonadInstance` when `t.isRight`.
* With the various dependent types involved, this is not worth it.
* The constructed tuple of inputs and the code that unpacks the results of the inputs are then
* passed to the `i`, which is an implementation of `Instance` that is statically accessible. An
* Instance defines a applicative functor associated with a specific type constructor and, if it
* implements MonadInstance as well, a monad. Typically, it will be either a top-level module or a
* stable member of a top-level module (such as a val or a nested module). The `with Singleton`
* part of the type verifies some cases at macro compilation time, while the full check for static
* accessibility is done at macro expansion time. Note: Ideally, the types would verify that `i:
* MonadInstance` when `t.isRight`. With the various dependent types involved, this is not worth
* it.
*
* The `t` argument is the argument of the macro that will be transformed as described above.
* If the macro that calls this method is for a multi-input map (app followed by map),
* `t` should be the argument wrapped in Left.
* If this is for multi-input flatMap (app followed by flatMap),
* this should be the argument wrapped in Right.
* The `t` argument is the argument of the macro that will be transformed as described above. If
* the macro that calls this method is for a multi-input map (app followed by map), `t` should be
* the argument wrapped in Left. If this is for multi-input flatMap (app followed by flatMap),
* this should be the argument wrapped in Right.
*/
def contImpl[T, N[_]](
c: blackbox.Context,
@ -95,8 +96,8 @@ object Instance {
)(
t: Either[c.Expr[T], c.Expr[i.M[T]]],
inner: Transform[c.type, N]
)(
implicit tt: c.WeakTypeTag[T],
)(implicit
tt: c.WeakTypeTag[T],
nt: c.WeakTypeTag[N[T]],
it: c.TypeTag[i.type]
): c.Expr[i.M[N[T]]] = {

View File

@ -11,7 +11,7 @@ package appmacro
import scala.reflect._
import macros._
/** A `TupleBuilder` that uses a KList as the tuple representation.*/
/** A `TupleBuilder` that uses a KList as the tuple representation. */
object KListBuilder extends TupleBuilder {
def make(
c: blackbox.Context
@ -29,7 +29,7 @@ object KListBuilder extends TupleBuilder {
val mTC: Type = mt.asInstanceOf[c.universe.Type]
val kconsTC: Type = kconsTpe.typeConstructor
/** This is the L in the type function [L[x]] ... */
/** This is the L in the type function [L[x]] ... */
val tcVariable: TypeSymbol = newTCVariable(util.initialOwner)
/** Instantiates KCons[h, t <: KList[L], L], where L is the type constructor variable */
@ -70,8 +70,8 @@ object KListBuilder extends TupleBuilder {
val klist = makeKList(inputs.reverse, knil, knilType)
/**
* The input types combined in a KList type. The main concern is tracking the heterogeneous types.
* The type constructor is tcVariable, so that it can be applied to [X] X or M later.
* The input types combined in a KList type. The main concern is tracking the heterogeneous
* types. The type constructor is tcVariable, so that it can be applied to [X] X or M later.
* When applied to `M`, this type gives the type of the `input` KList.
*/
val klistType: Type = inputs.foldRight(knilType)((in, klist) => kconsType(in.tpe, klist))

View File

@ -12,8 +12,9 @@ import scala.reflect._
import macros._
/**
* A builder that uses `TupleN` as the representation for small numbers of inputs (up to `TupleNBuilder.MaxInputs`)
* and `KList` for larger numbers of inputs. This builder cannot handle fewer than 2 inputs.
* A builder that uses `TupleN` as the representation for small numbers of inputs (up to
* `TupleNBuilder.MaxInputs`) and `KList` for larger numbers of inputs. This builder cannot handle
* fewer than 2 inputs.
*/
object MixedBuilder extends TupleBuilder {
def make(

View File

@ -12,22 +12,25 @@ import scala.reflect._
import macros._
/**
* A `TupleBuilder` abstracts the work of constructing a tuple data structure such as a `TupleN` or `KList`
* and extracting values from it. The `Instance` macro implementation will (roughly) traverse the tree of its argument
* and ultimately obtain a list of expressions with type `M[T]` for different types `T`.
* The macro constructs an `Input` value for each of these expressions that contains the `Type` for `T`,
* the `Tree` for the expression, and a `ValDef` that will hold the value for the input.
* A `TupleBuilder` abstracts the work of constructing a tuple data structure such as a `TupleN` or
* `KList` and extracting values from it. The `Instance` macro implementation will (roughly)
* traverse the tree of its argument and ultimately obtain a list of expressions with type `M[T]`
* for different types `T`. The macro constructs an `Input` value for each of these expressions that
* contains the `Type` for `T`, the `Tree` for the expression, and a `ValDef` that will hold the
* value for the input.
*
* `TupleBuilder.apply` is provided with the list of `Input`s and is expected to provide three values in the returned BuilderResult.
* First, it returns the constructed tuple data structure Tree in `input`.
* Next, it provides the type constructor `representationC` that, when applied to M, gives the type of tuple data structure.
* For example, a builder that constructs a `Tuple3` for inputs `M[Int]`, `M[Boolean]`, and `M[String]`
* would provide a Type representing `[L[x]] (L[Int], L[Boolean], L[String])`. The `input` method
* would return a value whose type is that type constructor applied to M, or `(M[Int], M[Boolean], M[String])`.
* `TupleBuilder.apply` is provided with the list of `Input`s and is expected to provide three
* values in the returned BuilderResult. First, it returns the constructed tuple data structure Tree
* in `input`. Next, it provides the type constructor `representationC` that, when applied to M,
* gives the type of tuple data structure. For example, a builder that constructs a `Tuple3` for
* inputs `M[Int]`, `M[Boolean]`, and `M[String]` would provide a Type representing `[L[x]] (L[Int],
* L[Boolean], L[String])`. The `input` method would return a value whose type is that type
* constructor applied to M, or `(M[Int], M[Boolean], M[String])`.
*
* Finally, the `extract` method provides a list of vals that extract information from the applied input.
* The type of the applied input is the type constructor applied to `Id` (`[X] X`).
* The returned list of ValDefs should be the ValDefs from `inputs`, but with non-empty right-hand sides.
* Finally, the `extract` method provides a list of vals that extract information from the applied
* input. The type of the applied input is the type constructor applied to `Id` (`[X] X`). The
* returned list of ValDefs should be the ValDefs from `inputs`, but with non-empty right-hand
* sides.
*/
trait TupleBuilder {
@ -45,20 +48,23 @@ trait BuilderResult[C <: blackbox.Context with Singleton] {
import ctx.universe._
/**
* Represents the higher-order type constructor `[L[x]] ...` where `...` is the
* type of the data structure containing the added expressions,
* except that it is abstracted over the type constructor applied to each heterogeneous part of the type .
* Represents the higher-order type constructor `[L[x]] ...` where `...` is the type of the data
* structure containing the added expressions, except that it is abstracted over the type
* constructor applied to each heterogeneous part of the type .
*/
def representationC: PolyType
/** The instance of AList for the input. For a `representationC` of `[L[x]]`, this `Tree` should have a `Type` of `AList[L]`*/
/**
* The instance of AList for the input. For a `representationC` of `[L[x]]`, this `Tree` should
* have a `Type` of `AList[L]`
*/
def alistInstance: Tree
/** Returns the completed value containing all expressions added to the builder. */
def input: Tree
/* The list of definitions that extract values from a value of type `$representationC[Id]`.
* The returned value should be identical to the `ValDef`s provided to the `TupleBuilder.make` method but with
* non-empty right hand sides. Each `ValDef` may refer to `param` and previous `ValDef`s in the list.*/
* The returned value should be identical to the `ValDef`s provided to the `TupleBuilder.make` method but with
* non-empty right hand sides. Each `ValDef` may refer to `param` and previous `ValDef`s in the list.*/
def extract(param: ValDef): List[ValDef]
}

View File

@ -13,8 +13,8 @@ import scala.reflect._
import macros._
/**
* A builder that uses a TupleN as the tuple representation.
* It is limited to tuples of size 2 to `MaxInputs`.
* A builder that uses a TupleN as the tuple representation. It is limited to tuples of size 2 to
* `MaxInputs`.
*/
object TupleNBuilder extends TupleBuilder {
@ -35,8 +35,8 @@ object TupleNBuilder extends TupleBuilder {
val ctx: c.type = c
val representationC: PolyType = {
val tcVariable: Symbol = newTCVariable(util.initialOwner)
val tupleTypeArgs = inputs.map(
in => internal.typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type]
val tupleTypeArgs = inputs.map(in =>
internal.typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type]
)
val tuple = global.definitions.tupleType(tupleTypeArgs)
internal.polyType(tcVariable :: Nil, tuple.asInstanceOf[Type])

View File

@ -1,42 +0,0 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.util
object Classes {
trait Applicative[M[_]] {
def apply[S, T](f: M[S => T], v: M[S]): M[T]
def pure[S](s: => S): M[S]
def map[S, T](f: S => T, v: M[S]): M[T]
}
trait Selective[M[_]] extends Applicative[M] {
def select[A, B](fab: M[Either[A, B]])(fn: M[A => B]): M[B]
}
trait Monad[M[_]] extends Applicative[M] {
def flatten[T](m: M[M[T]]): M[T]
}
implicit val optionMonad: Monad[Option] = new Monad[Option] {
def apply[S, T](f: Option[S => T], v: Option[S]) = (f, v) match {
case (Some(fv), Some(vv)) => Some(fv(vv))
case _ => None
}
def pure[S](s: => S) = Some(s)
def map[S, T](f: S => T, v: Option[S]) = v map f
def flatten[T](m: Option[Option[T]]): Option[T] = m.flatten
}
implicit val listMonad: Monad[List] = new Monad[List] {
def apply[S, T](f: List[S => T], v: List[S]) = for (fv <- f; vv <- v) yield fv(vv)
def pure[S](s: => S) = s :: Nil
def map[S, T](f: S => T, v: List[S]) = v map f
def flatten[T](m: List[List[T]]): List[T] = m.flatten
}
}

View File

@ -36,8 +36,7 @@ object ErrorHandling {
if (e.getClass == classOf[RuntimeException]) {
val msg = e.getMessage
if (msg == null || msg.isEmpty) e.toString else msg
} else
e.toString
} else e.toString
}
sealed class TranslatedException private[sbt] (msg: String, cause: Throwable)

View File

@ -7,7 +7,7 @@
package sbt.internal.util
/** Defines a function to call as sbt exits.*/
/** Defines a function to call as sbt exits. */
trait ExitHook {
/** Subclasses should implement this method, which is called when this hook is executed. */
@ -21,7 +21,10 @@ object ExitHook {
object ExitHooks {
/** Calls each registered exit hook, trapping any exceptions so that each hook is given a chance to run. */
/**
* Calls each registered exit hook, trapping any exceptions so that each hook is given a chance to
* run.
*/
def runExitHooks(exitHooks: Seq[ExitHook]): Seq[Throwable] =
exitHooks.flatMap(hook => ErrorHandling.wideConvert(hook.runBeforeExiting()).left.toOption)

View File

@ -10,19 +10,20 @@ package sbt.internal.util
final class MessageOnlyException(override val toString: String) extends RuntimeException(toString)
/**
* A dummy exception for the top-level exception handler to know that an exception
* has been handled, but is being passed further up to indicate general failure.
* A dummy exception for the top-level exception handler to know that an exception has been handled,
* but is being passed further up to indicate general failure.
*/
final class AlreadyHandledException(val underlying: Throwable) extends RuntimeException
/**
* A marker trait for a top-level exception handler to know that this exception
* doesn't make sense to display.
* A marker trait for a top-level exception handler to know that this exception doesn't make sense
* to display.
*/
trait UnprintableException extends Throwable
/**
* A marker trait that refines UnprintableException to indicate to a top-level exception handler
* that the code throwing this exception has already provided feedback to the user about the error condition.
* that the code throwing this exception has already provided feedback to the user about the error
* condition.
*/
trait FeedbackProvidedException extends UnprintableException

View File

@ -11,9 +11,8 @@ import java.util.concurrent.ConcurrentHashMap
import scala.sys.process.Process
/**
* Manages forked processes created by sbt. Any process registered
* with RunningProcesses can be killed with the killAll method. In
* particular, this can be used in a signal handler to kill these
* Manages forked processes created by sbt. Any process registered with RunningProcesses can be
* killed with the killAll method. In particular, this can be used in a signal handler to kill these
* processes when the user inputs ctrl+c.
*/
private[sbt] object RunningProcesses {

View File

@ -50,7 +50,7 @@ sealed abstract class Literal extends Formula {
/** The underlying (positive) atom. */
def atom: Atom
/** Negates this literal.*/
/** Negates this literal. */
def unary_! : Literal
}
@ -62,24 +62,24 @@ final case class Atom(label: String) extends Literal {
}
/**
* A negated atom, in the sense of negation as failure, not logical negation.
* That is, it is true if `atom` is not known/defined.
* A negated atom, in the sense of negation as failure, not logical negation. That is, it is true if
* `atom` is not known/defined.
*/
final case class Negated(atom: Atom) extends Literal {
def unary_! : Atom = atom
}
/**
* A formula consists of variables, negation, and conjunction (and).
* (Disjunction is not currently included- it is modeled at the level of a sequence of clauses.
* This is less convenient when defining clauses, but is not less powerful.)
* A formula consists of variables, negation, and conjunction (and). (Disjunction is not currently
* included- it is modeled at the level of a sequence of clauses. This is less convenient when
* defining clauses, but is not less powerful.)
*/
sealed abstract class Formula {
/** Constructs a clause that proves `atoms` when this formula is true. */
def proves(atom: Atom, atoms: Atom*): Clause = Clause(this, (atom +: atoms).toSet)
/** Constructs a formula that is true iff this formula and `f` are both true.*/
/** Constructs a formula that is true iff this formula and `f` are both true. */
def &&(f: Formula): Formula = (this, f) match {
case (True, x) => x
case (x, True) => x
@ -111,9 +111,9 @@ object Logic {
/**
* Computes the variables in the unique stable model for the program represented by `clauses` and
* `initialFacts`. `clause` may not have any negative feedback (that is, negation is acyclic)
* and `initialFacts` cannot be in the head of any clauses in `clause`.
* These restrictions ensure that the logic program has a unique minimal model.
* `initialFacts`. `clause` may not have any negative feedback (that is, negation is acyclic) and
* `initialFacts` cannot be in the head of any clauses in `clause`. These restrictions ensure that
* the logic program has a unique minimal model.
*/
def reduce(clauses: Clauses, initialFacts: Set[Literal]): Either[LogicException, Matched] = {
val (posSeq, negSeq) = separate(initialFacts.toSeq)
@ -130,10 +130,9 @@ object Logic {
}
/**
* Verifies `initialFacts` are not in the head of any `clauses`.
* This avoids the situation where an atom is proved but no clauses prove it.
* This isn't necessarily a problem, but the main sbt use cases expects
* a proven atom to have at least one clause satisfied.
* Verifies `initialFacts` are not in the head of any `clauses`. This avoids the situation where
* an atom is proved but no clauses prove it. This isn't necessarily a problem, but the main sbt
* use cases expects a proven atom to have at least one clause satisfied.
*/
private[this] def checkOverlap(
clauses: Clauses,
@ -179,12 +178,11 @@ object Logic {
}
private[this] def dependencyMap(clauses: Clauses): Map[Atom, Set[Literal]] =
clauses.clauses.foldLeft(Map.empty[Atom, Set[Literal]]) {
case (m, Clause(formula, heads)) =>
val deps = literals(formula)
heads.foldLeft(m) { (n, head) =>
n.updated(head, n.getOrElse(head, Set.empty) ++ deps)
}
clauses.clauses.foldLeft(Map.empty[Atom, Set[Literal]]) { case (m, Clause(formula, heads)) =>
val deps = literals(formula)
heads.foldLeft(m) { (n, head) =>
n.updated(head, n.getOrElse(head, Set.empty) ++ deps)
}
}
sealed abstract class LogicException(override val toString: String)
@ -229,8 +227,8 @@ object Logic {
}
/**
* Finds clauses that have no body and thus prove their head.
* Returns `(<proven atoms>, <remaining unproven clauses>)`.
* Finds clauses that have no body and thus prove their head. Returns `(<proven atoms>, <remaining
* unproven clauses>)`.
*/
private[this] def findProven(c: Clauses): (Set[Atom], List[Clause]) = {
val (proven, unproven) = c.clauses.partition(_.body == True)
@ -253,8 +251,7 @@ object Logic {
val processedFacts = state add keepPositive(factsToProcess)
val newlyProven = proven -- processedFacts.provenSet
val newState = processedFacts add newlyProven
if (unprovenClauses.isEmpty)
newState // no remaining clauses, done.
if (unprovenClauses.isEmpty) newState // no remaining clauses, done.
else {
val unproven = Clauses(unprovenClauses)
val nextFacts: Set[Literal] =
@ -265,8 +262,8 @@ object Logic {
}
/**
* Finds negated atoms under the negation as failure rule and returns them.
* This should be called only after there are no more known atoms to be substituted.
* Finds negated atoms under the negation as failure rule and returns them. This should be called
* only after there are no more known atoms to be substituted.
*/
private[this] def inferFailure(clauses: Clauses): Set[Literal] = {
/* At this point, there is at least one clause and one of the following is the case as the
@ -281,8 +278,7 @@ object Logic {
*/
val allAtoms = atoms(clauses)
val newFacts: Set[Literal] = negated(allAtoms.triviallyFalse)
if (newFacts.nonEmpty)
newFacts
if (newFacts.nonEmpty) newFacts
else {
val possiblyTrue = hasNegatedDependency(clauses.clauses, Relation.empty, Relation.empty)
val newlyFalse: Set[Literal] = negated(allAtoms.inHead -- possiblyTrue)
@ -296,10 +292,9 @@ object Logic {
private[this] def negated(atoms: Set[Atom]): Set[Literal] = atoms.map(a => (Negated(a): Literal))
/**
* Computes the set of atoms in `clauses` that directly or transitively take a negated atom as input.
* For example, for the following clauses, this method would return `List(a, d)` :
* a :- b, not c
* d :- a
* Computes the set of atoms in `clauses` that directly or transitively take a negated atom as
* input. For example, for the following clauses, this method would return `List(a, d)` : a :- b,
* not c d :- a
*/
@tailrec
def hasNegatedDependency(
@ -315,9 +310,8 @@ object Logic {
case Clause(formula, head) +: tail =>
// collect direct positive and negative literals and track them in separate graphs
val (pos, neg) = directDeps(formula)
val (newPos, newNeg) = head.foldLeft((posDeps, negDeps)) {
case ((pdeps, ndeps), d) =>
(pdeps.+(d, pos), ndeps.+(d, neg))
val (newPos, newNeg) = head.foldLeft((posDeps, negDeps)) { case ((pdeps, ndeps), d) =>
(pdeps.+(d, pos), ndeps.+(d, neg))
}
hasNegatedDependency(tail, newPos, newNeg)
}
@ -346,7 +340,9 @@ object Logic {
case True => Set()
}
/** Represents the set of atoms in the heads of clauses and in the bodies (formulas) of clauses. */
/**
* Represents the set of atoms in the heads of clauses and in the bodies (formulas) of clauses.
*/
final case class Atoms(inHead: Set[Atom], inFormula: Set[Atom]) {
/** Concatenates this with `as`. */
@ -359,16 +355,16 @@ object Logic {
/**
* Applies known facts to `clause`s, deriving a new, possibly empty list of clauses.
* 1. If a fact is in the body of a clause, the derived clause has that fact removed from the body.
* 2. If the negation of a fact is in a body of a clause, that clause fails and is removed.
* 3. If a fact or its negation is in the head of a clause, the derived clause has that fact (or its negation) removed from the head.
* 4. If a head is empty, the clause proves nothing and is removed.
* 1. If a fact is in the body of a clause, the derived clause has that fact removed from the
* body. 2. If the negation of a fact is in a body of a clause, that clause fails and is
* removed. 3. If a fact or its negation is in the head of a clause, the derived clause has
* that fact (or its negation) removed from the head. 4. If a head is empty, the clause
* proves nothing and is removed.
*
* NOTE: empty bodies do not cause a clause to succeed yet.
* All known facts must be applied before this can be done in order to avoid inconsistencies.
* Precondition: no contradictions in `facts`
* Postcondition: no atom in `facts` is present in the result
* Postcondition: No clauses have an empty head
* NOTE: empty bodies do not cause a clause to succeed yet. All known facts must be applied before
* this can be done in order to avoid inconsistencies. Precondition: no contradictions in `facts`
* Postcondition: no atom in `facts` is present in the result Postcondition: No clauses have an
* empty head
*/
def applyAll(cs: Clauses, facts: Set[Literal]): Option[Clauses] = {
val newClauses =

View File

@ -15,17 +15,21 @@ object Relation {
def empty[A, B]: Relation[A, B] = make(Map.empty, Map.empty)
/**
* Constructs a [[Relation]] from underlying `forward` and `reverse` representations, without checking that they are consistent.
* This is a low-level constructor and the alternatives [[empty]] and [[reconstruct]] should be preferred.
* Constructs a [[Relation]] from underlying `forward` and `reverse` representations, without
* checking that they are consistent. This is a low-level constructor and the alternatives
* [[empty]] and [[reconstruct]] should be preferred.
*/
def make[A, B](forward: Map[A, Set[B]], reverse: Map[B, Set[A]]): Relation[A, B] =
new MRelation(forward, reverse)
/** Constructs a relation such that for every entry `_1 -> _2s` in `forward` and every `_2` in `_2s`, `(_1, _2)` is in the relation. */
/**
* Constructs a relation such that for every entry `_1 -> _2s` in `forward` and every `_2` in
* `_2s`, `(_1, _2)` is in the relation.
*/
def reconstruct[A, B](forward: Map[A, Set[B]]): Relation[A, B] = {
val reversePairs = for ((a, bs) <- forward.view; b <- bs.view) yield (b, a)
val reverse = reversePairs.foldLeft(Map.empty[B, Set[A]]) {
case (m, (b, a)) => add(m, b, a :: Nil)
val reverse = reversePairs.foldLeft(Map.empty[B, Set[A]]) { case (m, (b, a)) =>
add(m, b, a :: Nil)
}
make(forward filter { case (a, bs) => bs.nonEmpty }, reverse)
}
@ -53,19 +57,17 @@ object Relation {
/** when both parameters taken by relation are the same type, switch calls a function on them. */
private[sbt] def switch[X, Y](relation: Relation[X, X], f: X => Y): Relation[Y, Y] = {
val forward = relation.forwardMap.map {
case (first, second) =>
f(first) -> second.map(f)
val forward = relation.forwardMap.map { case (first, second) =>
f(first) -> second.map(f)
}
val reverse = relation.reverseMap.map {
case (first, second) =>
f(first) -> second.map(f)
val reverse = relation.reverseMap.map { case (first, second) =>
f(first) -> second.map(f)
}
make(forward, reverse)
}
}
/** Binary relation between A and B. It is a set of pairs (_1, _2) for _1 in A, _2 in B. */
/** Binary relation between A and B. It is a set of pairs (_1, _2) for _1 in A, _2 in B. */
trait Relation[A, B] {
/** Returns the set of all `_2`s such that `(_1, _2)` is in this relation. */
@ -113,37 +115,41 @@ trait Relation[A, B] {
/** Returns the number of pairs in this relation */
def size: Int
/** Returns true iff `(a,b)` is in this relation*/
/** Returns true iff `(a,b)` is in this relation */
def contains(a: A, b: B): Boolean
/** Returns a relation with only pairs `(a,b)` for which `f(a,b)` is true.*/
/** Returns a relation with only pairs `(a,b)` for which `f(a,b)` is true. */
def filter(f: (A, B) => Boolean): Relation[A, B]
/**
* Returns a pair of relations: the first contains only pairs `(a,b)` for which `f(a,b)` is true and
* the other only pairs `(a,b)` for which `f(a,b)` is false.
* Returns a pair of relations: the first contains only pairs `(a,b)` for which `f(a,b)` is true
* and the other only pairs `(a,b)` for which `f(a,b)` is false.
*/
def partition(f: (A, B) => Boolean): (Relation[A, B], Relation[A, B])
/** Partitions this relation into a map of relations according to some discriminator function. */
def groupBy[K](discriminator: ((A, B)) => K): Map[K, Relation[A, B]]
/** Returns all pairs in this relation.*/
/** Returns all pairs in this relation. */
def all: Traversable[(A, B)]
/**
* Represents this relation as a `Map` from a `_1` to the set of `_2`s such that `(_1, _2)` is in this relation.
* Represents this relation as a `Map` from a `_1` to the set of `_2`s such that `(_1, _2)` is in
* this relation.
*
* Specifically, there is one entry for each `_1` such that `(_1, _2)` is in this relation for some `_2`.
* The value associated with a given `_1` is the set of all `_2`s such that `(_1, _2)` is in this relation.
* Specifically, there is one entry for each `_1` such that `(_1, _2)` is in this relation for
* some `_2`. The value associated with a given `_1` is the set of all `_2`s such that `(_1, _2)`
* is in this relation.
*/
def forwardMap: Map[A, Set[B]]
/**
* Represents this relation as a `Map` from a `_2` to the set of `_1`s such that `(_1, _2)` is in this relation.
* Represents this relation as a `Map` from a `_2` to the set of `_1`s such that `(_1, _2)` is in
* this relation.
*
* Specifically, there is one entry for each `_2` such that `(_1, _2)` is in this relation for some `_1`.
* The value associated with a given `_2` is the set of all `_1`s such that `(_1, _2)` is in this relation.
* Specifically, there is one entry for each `_2` such that `(_1, _2)` is in this relation for
* some `_1`. The value associated with a given `_2` is the set of all `_1`s such that `(_1, _2)`
* is in this relation.
*/
def reverseMap: Map[B, Set[A]]
}

View File

@ -21,12 +21,11 @@ object RelationTest extends Properties("Relation") {
r._1s == _1s && r.forwardMap.keySet == _1s &&
r._2s == _2s && r.reverseMap.keySet == _2s &&
pairs.forall {
case (a, b) =>
(r.forward(a) contains b) &&
(r.reverse(b) contains a) &&
(r.forwardMap(a) contains b) &&
(r.reverseMap(b) contains a)
pairs.forall { case (a, b) =>
(r.forward(a) contains b) &&
(r.reverse(b) contains a) &&
(r.forwardMap(a) contains b) &&
(r.reverseMap(b) contains a)
}
}
@ -46,12 +45,11 @@ object RelationTest extends Properties("Relation") {
("Forward map does not contain removed" |: !r.forwardMap.contains(rem)) &&
("Removed is not a value in reverse map" |: !r.reverseMap.values.toSet.contains(rem))
} &&
all(removeFine) {
case (a, b) =>
("Forward does not contain removed" |: (!r.forward(a).contains(b))) &&
("Reverse does not contain removed" |: (!r.reverse(b).contains(a))) &&
("Forward map does not contain removed" |: (notIn(r.forwardMap, a, b))) &&
("Reverse map does not contain removed" |: (notIn(r.reverseMap, b, a)))
all(removeFine) { case (a, b) =>
("Forward does not contain removed" |: (!r.forward(a).contains(b))) &&
("Reverse does not contain removed" |: (!r.reverse(b).contains(a))) &&
("Forward map does not contain removed" |: (notIn(r.forwardMap, a, b))) &&
("Reverse map does not contain removed" |: (notIn(r.reverseMap, b, a)))
}
}
@ -59,8 +57,8 @@ object RelationTest extends Properties("Relation") {
val splitInto = math.abs(randomInt) % 10 + 1 // Split into 1-10 groups.
val rel = Relation.empty[Int, Double] ++ entries
val grouped = rel groupBy (_._1 % splitInto)
all(grouped.toSeq) {
case (k, rel_k) => rel_k._1s forall { _ % splitInto == k }
all(grouped.toSeq) { case (k, rel_k) =>
rel_k._1s forall { _ % splitInto == k }
}
}

View File

@ -6,8 +6,9 @@ object Dependencies {
// WARNING: Please Scala update versions in PluginCross.scala too
val scala212 = "2.12.17"
val scala213 = "2.13.8"
val scala3 = "3.1.0"
val checkPluginCross = settingKey[Unit]("Make sure scalaVersion match up")
val baseScalaVersion = scala212
val baseScalaVersion = scala3
def nightlyVersion: Option[String] =
sys.env.get("BUILD_VERSION") orElse sys.props.get("sbt.build.version")
@ -79,8 +80,11 @@ object Dependencies {
val lmCoursierShaded = "io.get-coursier" %% "lm-coursier-shaded" % "2.0.13"
def sjsonNew(n: String) =
Def.setting("com.eed3si9n" %% n % "0.9.1") // contrabandSjsonNewVersion.value
lazy val sjsonNewVersion = "0.9.1"
def sjsonNew(n: String) = Def.setting(
if (scalaBinaryVersion.value == "3") "com.eed3si9n" % (n + "_2.13") % sjsonNewVersion
else "com.eed3si9n" %% n % "0.9.1"
) // contrabandSjsonNewVersion.value
val sjsonNewScalaJson = sjsonNew("sjson-new-scalajson")
val sjsonNewMurmurhash = sjsonNew("sjson-new-murmurhash")
@ -121,7 +125,7 @@ object Dependencies {
"org.scala-lang" % "scala-reflect" % scalaVersion.value
}
)
val scalaPar = "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.0"
val scalaPar = "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.4"
// specify all of log4j modules to prevent misalignment
def log4jModule = (n: String) => "org.apache.logging.log4j" % n % "2.17.1"

View File

@ -24,14 +24,15 @@ object HouseRulesPlugin extends AutoPlugin {
.value
.toList,
scalacOptions ++= "-Ykind-projector".ifScala3.value.toList,
scalacOptions ++= "-Ysemanticdb".ifScala3.value.toList,
scalacOptions ++= "-Yinline-warnings".ifScala211OrMinus.value.toList,
scalacOptions ++= "-Yno-adapted-args".ifScala212OrMinus.value.toList,
scalacOptions += "-Ywarn-dead-code",
scalacOptions += "-Ywarn-numeric-widen",
scalacOptions += "-Ywarn-value-discard",
scalacOptions ++= "-Ywarn-unused-import".ifScala(v => 11 <= v && v <= 12).value.toList
) ++ Seq(Compile, Test).flatMap(
c => (c / console / scalacOptions) --= Seq("-Ywarn-unused-import", "-Xlint")
) ++ Seq(Compile, Test).flatMap(c =>
(c / console / scalacOptions) --= Seq("-Ywarn-unused-import", "-Xlint")
)
private def scalaPartV = Def setting (CrossVersion partialVersion scalaVersion.value)

View File

@ -10,14 +10,13 @@ package sbt
trait CompletionService[A, R] {
/**
* Submits a work node A with work that returns R.
* In Execute this is used for tasks returning sbt.Completed.
* Submits a work node A with work that returns R. In Execute this is used for tasks returning
* sbt.Completed.
*/
def submit(node: A, work: () => R): Unit
/**
* Retrieves and removes the result from the next completed task,
* waiting if none are yet present.
* Retrieves and removes the result from the next completed task, waiting if none are yet present.
* In Execute this is used for tasks returning sbt.Completed.
*/
def take(): R
@ -57,20 +56,21 @@ object CompletionService {
() => future.get
}
private[sbt] def submitFuture[A](work: () => A, completion: JCompletionService[A]): JFuture[A] = {
val future = try completion.submit {
new Callable[A] {
def call =
try {
work()
} catch {
case _: InterruptedException =>
throw Incomplete(None, message = Some("cancelled"))
}
val future =
try completion.submit {
new Callable[A] {
def call =
try {
work()
} catch {
case _: InterruptedException =>
throw Incomplete(None, message = Some("cancelled"))
}
}
} catch {
case _: RejectedExecutionException =>
throw Incomplete(None, message = Some("cancelled"))
}
} catch {
case _: RejectedExecutionException =>
throw Incomplete(None, message = Some("cancelled"))
}
future
}
def manage[A, T](

View File

@ -17,31 +17,30 @@ import scala.collection.mutable
/**
* Describes restrictions on concurrent execution for a set of tasks.
*
* @tparam A the type of a task
* @tparam A
* the type of a task
*/
trait ConcurrentRestrictions[A] {
/** Internal state type used to describe a set of tasks. */
type G
/** Representation of zero tasks.*/
/** Representation of zero tasks. */
def empty: G
/** Updates the description `g` to include a new task `a`.*/
/** Updates the description `g` to include a new task `a`. */
def add(g: G, a: A): G
/** Updates the description `g` to remove a previously added task `a`.*/
/** Updates the description `g` to remove a previously added task `a`. */
def remove(g: G, a: A): G
/**
* Returns true if the tasks described by `g` are allowed to execute concurrently.
* The methods in this class must obey the following laws:
* Returns true if the tasks described by `g` are allowed to execute concurrently. The methods in
* this class must obey the following laws:
*
* 1. forall g: G, a: A; valid(g) => valid(remove(g,a))
* 2. forall a: A; valid(add(empty, a))
* 3. forall g: G, a: A; valid(g) <=> valid(remove(add(g, a), a))
* 4. (implied by 1,2,3) valid(empty)
* 5. forall g: G, a: A, b: A; !valid(add(g,a)) => !valid(add(add(g,b), a))
* 1. forall g: G, a: A; valid(g) => valid(remove(g,a)) 2. forall a: A; valid(add(empty, a)) 3.
* forall g: G, a: A; valid(g) <=> valid(remove(add(g, a), a)) 4. (implied by 1,2,3)
* valid(empty) 5. forall g: G, a: A, b: A; !valid(add(g,a)) => !valid(add(add(g,b), a))
*/
def valid(g: G): Boolean
}
@ -69,7 +68,8 @@ object ConcurrentRestrictions {
/**
* A ConcurrentRestrictions instance that places no restrictions on concurrently executing tasks.
* @param zero the constant placeholder used for t
* @param zero
* the constant placeholder used for t
*/
def unrestricted[A]: ConcurrentRestrictions[A] =
new ConcurrentRestrictions[A] {
@ -91,13 +91,13 @@ object ConcurrentRestrictions {
}
}
/** A key object used for associating information with a task.*/
/** A key object used for associating information with a task. */
final case class Tag(name: String)
val tagsKey =
AttributeKey[TagMap]("tags", "Attributes restricting concurrent execution of tasks.")
/** A standard tag describing the number of tasks that do not otherwise have any tags.*/
/** A standard tag describing the number of tasks that do not otherwise have any tags. */
val Untagged = Tag("untagged")
/** A standard tag describing the total number of tasks. */
@ -108,9 +108,12 @@ object ConcurrentRestrictions {
/**
* Implements concurrency restrictions on tasks based on Tags.
* @tparam A type of a task
* @param get extracts tags from a task
* @param validF defines whether a set of tasks are allowed to execute concurrently based on their merged tags
* @tparam A
* type of a task
* @param get
* extracts tags from a task
* @param validF
* defines whether a set of tasks are allowed to execute concurrently based on their merged tags
*/
def tagged[A](get: A => TagMap, validF: TagMap => Boolean): ConcurrentRestrictions[A] =
new ConcurrentRestrictions[A] {
@ -142,10 +145,14 @@ object ConcurrentRestrictions {
private[this] val poolID = new AtomicInteger(1)
/**
* Constructs a CompletionService suitable for backing task execution based on the provided restrictions on concurrent task execution.
* @return a pair, with _1 being the CompletionService and _2 a function to shutdown the service.
* @tparam A the task type
* @tparam R the type of data that will be computed by the CompletionService.
* Constructs a CompletionService suitable for backing task execution based on the provided
* restrictions on concurrent task execution.
* @return
* a pair, with _1 being the CompletionService and _2 a function to shutdown the service.
* @tparam A
* the task type
* @tparam R
* the type of data that will be computed by the CompletionService.
*/
def completionService[A, R](
tags: ConcurrentRestrictions[A],
@ -167,10 +174,13 @@ object ConcurrentRestrictions {
): (CompletionService[A, R], () => Unit) = {
val pool = Executors.newCachedThreadPool()
val service = completionService[A, R](pool, tags, warn, isSentinel)
(service, () => {
pool.shutdownNow()
()
})
(
service,
() => {
pool.shutdownNow()
()
}
)
}
def cancellableCompletionService[A, R](
@ -180,11 +190,14 @@ object ConcurrentRestrictions {
): (CompletionService[A, R], Boolean => Unit) = {
val pool = Executors.newCachedThreadPool()
val service = completionService[A, R](pool, tags, warn, isSentinel)
(service, force => {
if (force) service.close()
pool.shutdownNow()
()
})
(
service,
force => {
if (force) service.close()
pool.shutdownNow()
()
}
)
}
def completionService[A, R](
@ -196,8 +209,9 @@ object ConcurrentRestrictions {
}
/**
* Constructs a CompletionService suitable for backing task execution based on the provided restrictions on concurrent task execution
* and using the provided Executor to manage execution on threads.
* Constructs a CompletionService suitable for backing task execution based on the provided
* restrictions on concurrent task execution and using the provided Executor to manage execution
* on threads.
*/
def completionService[A, R](
backing: Executor,
@ -220,13 +234,16 @@ object ConcurrentRestrictions {
/** Backing service used to manage execution on threads once all constraints are satisfied. */
private[this] val jservice = new ExecutorCompletionService[R](backing)
/** The description of the currently running tasks, used by `tags` to manage restrictions.*/
/** The description of the currently running tasks, used by `tags` to manage restrictions. */
private[this] var tagState = tags.empty
/** The number of running tasks. */
private[this] var running = 0
/** Tasks that cannot be run yet because they cannot execute concurrently with the currently running tasks.*/
/**
* Tasks that cannot be run yet because they cannot execute concurrently with the currently
* running tasks.
*/
private[this] val pending = new LinkedList[Enqueue]
private[this] val sentinels: mutable.ListBuffer[JFuture[_]] = mutable.ListBuffer.empty

View File

@ -65,12 +65,11 @@ private[sbt] final class Execute[F[_] <: AnyRef](
private[this] val viewCache = pMap[F, Node[F, *]]
private[this] val results = pMap[F, Result]
private[this] val getResult: F ~> Result = λ[F ~> Result](
a =>
view.inline(a) match {
case Some(v) => Value(v())
case None => results(a)
}
private[this] val getResult: F ~> Result = λ[F ~> Result](a =>
view.inline(a) match {
case Some(v) => Value(v())
case None => results(a)
}
)
private[this] type State = State.Value
private[this] object State extends Enumeration {
@ -149,8 +148,7 @@ private[sbt] final class Execute[F[_] <: AnyRef](
}
post {
if (done(target))
assert(done(node))
if (done(target)) assert(done(node))
else {
assert(calling(node))
assert(callers(target) contains node)
@ -203,9 +201,9 @@ private[sbt] final class Execute[F[_] <: AnyRef](
}
/**
* Ensures the given node has been added to the system.
* Once added, a node is pending until its inputs and dependencies have completed.
* Its computation is then evaluated and made available for nodes that have it as an input.
* Ensures the given node has been added to the system. Once added, a node is pending until its
* inputs and dependencies have completed. Its computation is then evaluated and made available
* for nodes that have it as an input.
*/
def addChecked[A](node: F[A])(implicit strategy: Strategy): Unit = {
if (!added(node)) addNew(node)
@ -214,9 +212,9 @@ private[sbt] final class Execute[F[_] <: AnyRef](
}
/**
* Adds a node that has not yet been registered with the system.
* If all of the node's dependencies have finished, the node's computation is scheduled to run.
* The node's dependencies will be added (transitively) if they are not already registered.
* Adds a node that has not yet been registered with the system. If all of the node's dependencies
* have finished, the node's computation is scheduled to run. The node's dependencies will be
* added (transitively) if they are not already registered.
*/
def addNew[A](node: F[A])(implicit strategy: Strategy): Unit = {
pre { newPre(node) }
@ -231,8 +229,7 @@ private[sbt] final class Execute[F[_] <: AnyRef](
/* active is mutable, so take a snapshot */
)
if (active.isEmpty)
ready(node)
if (active.isEmpty) ready(node)
else {
forward(node) = active
for (a <- active) {
@ -249,7 +246,10 @@ private[sbt] final class Execute[F[_] <: AnyRef](
}
}
/** Called when a pending 'node' becomes runnable. All of its dependencies must be done. This schedules the node's computation with 'strategy'.*/
/**
* Called when a pending 'node' becomes runnable. All of its dependencies must be done. This
* schedules the node's computation with 'strategy'.
*/
def ready[A](node: F[A])(implicit strategy: Strategy): Unit = {
pre {
assert(pending(node))
@ -283,8 +283,8 @@ private[sbt] final class Execute[F[_] <: AnyRef](
}
/**
* Evaluates the computation 'f' for 'node'.
* This returns a Completed instance, which contains the post-processing to perform after the result is retrieved from the Strategy.
* Evaluates the computation 'f' for 'node'. This returns a Completed instance, which contains the
* post-processing to perform after the result is retrieved from the Strategy.
*/
def work[A](node: F[A], f: => Either[F[A], A])(implicit strategy: Strategy): Completed = {
progress.beforeWork(node)

View File

@ -10,48 +10,46 @@ package sbt
import sbt.internal.util.RMap
/**
* Processes progress events during task execution.
* All methods are called from the same thread except `started` and `finished`,
* which is called from the executing task's thread.
* All methods should return quickly to avoid task execution overhead.
* Processes progress events during task execution. All methods are called from the same thread
* except `started` and `finished`, which is called from the executing task's thread. All methods
* should return quickly to avoid task execution overhead.
*/
trait ExecuteProgress[F[_]] {
def initial(): Unit
/**
* Notifies that a `task` has been registered in the system for execution.
* The dependencies of `task` are `allDeps` and the subset of those dependencies that
* have not completed are `pendingDeps`.
* Notifies that a `task` has been registered in the system for execution. The dependencies of
* `task` are `allDeps` and the subset of those dependencies that have not completed are
* `pendingDeps`.
*/
def afterRegistered(task: F[_], allDeps: Iterable[F[_]], pendingDeps: Iterable[F[_]]): Unit
/**
* Notifies that all of the dependencies of `task` have completed and `task` is therefore
* ready to run. The task has not been scheduled on a thread yet.
* Notifies that all of the dependencies of `task` have completed and `task` is therefore ready to
* run. The task has not been scheduled on a thread yet.
*/
def afterReady(task: F[_]): Unit
/**
* Notifies that the work for `task` is starting after this call returns.
* This is called from the thread the task executes on, unlike most other methods in this callback.
* It is called immediately before the task's work starts with minimal intervening executor overhead.
* Notifies that the work for `task` is starting after this call returns. This is called from the
* thread the task executes on, unlike most other methods in this callback. It is called
* immediately before the task's work starts with minimal intervening executor overhead.
*/
def beforeWork(task: F[_]): Unit
/**
* Notifies that the work for `task` work has finished. The task may have computed the next task to
* run, in which case `result` contains that next task wrapped in Left. If the task produced a value
* or terminated abnormally, `result` provides that outcome wrapped in Right. The ultimate result of
* a task is provided to the `completed` method.
* This is called from the thread the task executes on, unlike most other methods in this callback.
* It is immediately called after the task's work is complete with minimal intervening executor overhead.
* Notifies that the work for `task` work has finished. The task may have computed the next task
* to run, in which case `result` contains that next task wrapped in Left. If the task produced a
* value or terminated abnormally, `result` provides that outcome wrapped in Right. The ultimate
* result of a task is provided to the `completed` method. This is called from the thread the task
* executes on, unlike most other methods in this callback. It is immediately called after the
* task's work is complete with minimal intervening executor overhead.
*/
def afterWork[A](task: F[A], result: Either[F[A], Result[A]]): Unit
/**
* Notifies that `task` has completed.
* The task's work is done with a final `result`.
* Any tasks called by `task` have completed.
* Notifies that `task` has completed. The task's work is done with a final `result`. Any tasks
* called by `task` have completed.
*/
def afterCompleted[A](task: F[A], result: Result[A]): Unit
@ -62,7 +60,9 @@ trait ExecuteProgress[F[_]] {
def stop(): Unit
}
/** This module is experimental and subject to binary and source incompatible changes at any time. */
/**
* This module is experimental and subject to binary and source incompatible changes at any time.
*/
object ExecuteProgress {
def empty[F[_]]: ExecuteProgress[F] = new ExecuteProgress[F] {
override def initial(): Unit = ()

View File

@ -15,11 +15,17 @@ import Incomplete.{ Error, Value => IValue }
/**
* Describes why a task did not complete.
*
* @param node the task that did not complete that is described by this Incomplete instance
* @param tpe whether the task was incomplete because of an error or because it was skipped. Only Error is actually used and Skipped may be removed in the future.
* @param message an optional error message describing this incompletion
* @param causes a list of incompletions that prevented `node` from completing
* @param directCause the exception that caused `node` to not complete
* @param node
* the task that did not complete that is described by this Incomplete instance
* @param tpe
* whether the task was incomplete because of an error or because it was skipped. Only Error is
* actually used and Skipped may be removed in the future.
* @param message
* an optional error message describing this incompletion
* @param causes
* a list of incompletions that prevented `node` from completing
* @param directCause
* the exception that caused `node` to not complete
*/
final case class Incomplete(
node: Option[AnyRef],

View File

@ -12,8 +12,10 @@ import sbt.internal.util.AList
/**
* Represents a task node in a format understood by the task evaluation engine Execute.
*
* @tparam A the task type constructor
* @tparam T the type computed by this node
* @tparam A
* the task type constructor
* @tparam T
* the type computed by this node
*/
trait Node[A[_], T] {
type K[L[x]]

View File

@ -11,17 +11,17 @@ import sbt.internal.util.~>
// used instead of Either[Incomplete, T] for type inference
/** Result of completely evaluating a task.*/
/** Result of completely evaluating a task. */
sealed trait Result[+T] {
def toEither: Either[Incomplete, T]
}
/** Indicates the task did not complete normally and so it does not have a value.*/
/** Indicates the task did not complete normally and so it does not have a value. */
final case class Inc(cause: Incomplete) extends Result[Nothing] {
def toEither: Either[Incomplete, Nothing] = Left(cause)
}
/** Indicates the task completed normally and produced the given `value`.*/
/** Indicates the task completed normally and produced the given `value`. */
final case class Value[+T](value: T) extends Result[T] {
def toEither: Either[Incomplete, T] = Right(value)
}

View File

@ -16,6 +16,7 @@ object EvaluationState extends Enumeration {
val New, Blocked, Ready, Calling, Evaluated = Value
}
/*
abstract class EvaluateSettings[ScopeType] {
protected val init: Init[ScopeType]
import init._
@ -228,3 +229,4 @@ abstract class EvaluateSettings[ScopeType] {
protected def evaluate0(): Unit = setValue(f(alist.transform(in, getValue)))
}
}
*/

View File

@ -16,8 +16,8 @@ import sbt.util.OptJsonWriter
// a single AttributeKey instance cannot conform to AttributeKey[T] for different Ts
/**
* A key in an [[AttributeMap]] that constrains its associated value to be of type `T`.
* The key is uniquely defined by its `label` and type `T`, represented at runtime by `manifest`.
* A key in an [[AttributeMap]] that constrains its associated value to be of type `T`. The key is
* uniquely defined by its `label` and type `T`, represented at runtime by `manifest`.
*/
sealed trait AttributeKey[T] {
@ -32,18 +32,19 @@ sealed trait AttributeKey[T] {
/**
* In environments that support delegation, looking up this key when it has no associated value
* will delegate to the values associated with these keys.
* The delegation proceeds in order the keys are returned here.
* will delegate to the values associated with these keys. The delegation proceeds in order the
* keys are returned here.
*/
def extend: Seq[AttributeKey[_]]
/**
* Specifies whether this key is a local, anonymous key (`true`) or not (`false`).
* This is typically only used for programmatic, intermediate keys that should not be referenced outside of a specific scope.
* Specifies whether this key is a local, anonymous key (`true`) or not (`false`). This is
* typically only used for programmatic, intermediate keys that should not be referenced outside
* of a specific scope.
*/
def isLocal: Boolean
/** Identifies the relative importance of a key among other keys.*/
/** Identifies the relative importance of a key among other keys. */
def rank: Int
def optJsonWriter: OptJsonWriter[T]
@ -133,60 +134,73 @@ object AttributeKey {
}
/**
* An immutable map where a key is the tuple `(String,T)` for a fixed type `T` and can only be associated with values of type `T`.
* It is therefore possible for this map to contain mappings for keys with the same label but different types.
* Excluding this possibility is the responsibility of the client if desired.
* An immutable map where a key is the tuple `(String,T)` for a fixed type `T` and can only be
* associated with values of type `T`. It is therefore possible for this map to contain mappings for
* keys with the same label but different types. Excluding this possibility is the responsibility of
* the client if desired.
*/
trait AttributeMap {
/**
* Gets the value of type `T` associated with the key `k`.
* If a key with the same label but different type is defined, this method will fail.
* Gets the value of type `T` associated with the key `k`. If a key with the same label but
* different type is defined, this method will fail.
*/
def apply[T](k: AttributeKey[T]): T
/**
* Gets the value of type `T` associated with the key `k` or `None` if no value is associated.
* If a key with the same label but a different type is defined, this method will return `None`.
* Gets the value of type `T` associated with the key `k` or `None` if no value is associated. If
* a key with the same label but a different type is defined, this method will return `None`.
*/
def get[T](k: AttributeKey[T]): Option[T]
/**
* Returns this map without the mapping for `k`.
* This method will not remove a mapping for a key with the same label but a different type.
* Returns this map without the mapping for `k`. This method will not remove a mapping for a key
* with the same label but a different type.
*/
def remove[T](k: AttributeKey[T]): AttributeMap
/**
* Returns true if this map contains a mapping for `k`.
* If a key with the same label but a different type is defined in this map, this method will return `false`.
* Returns true if this map contains a mapping for `k`. If a key with the same label but a
* different type is defined in this map, this method will return `false`.
*/
def contains[T](k: AttributeKey[T]): Boolean
/**
* Adds the mapping `k -> value` to this map, replacing any existing mapping for `k`.
* Any mappings for keys with the same label but different types are unaffected.
* Adds the mapping `k -> value` to this map, replacing any existing mapping for `k`. Any mappings
* for keys with the same label but different types are unaffected.
*/
def put[T](k: AttributeKey[T], value: T): AttributeMap
/** All keys with defined mappings. There may be multiple keys with the same `label`, but different types. */
/**
* All keys with defined mappings. There may be multiple keys with the same `label`, but different
* types.
*/
def keys: Iterable[AttributeKey[_]]
/** Adds the mappings in `o` to this map, with mappings in `o` taking precedence over existing mappings.*/
/**
* Adds the mappings in `o` to this map, with mappings in `o` taking precedence over existing
* mappings.
*/
def ++(o: Iterable[AttributeEntry[_]]): AttributeMap
/** Combines the mappings in `o` with the mappings in this map, with mappings in `o` taking precedence over existing mappings.*/
/**
* Combines the mappings in `o` with the mappings in this map, with mappings in `o` taking
* precedence over existing mappings.
*/
def ++(o: AttributeMap): AttributeMap
/** All mappings in this map. The [[AttributeEntry]] type preserves the typesafety of mappings, although the specific types are unknown.*/
/**
* All mappings in this map. The [[AttributeEntry]] type preserves the typesafety of mappings,
* although the specific types are unknown.
*/
def entries: Iterable[AttributeEntry[_]]
/** `true` if there are no mappings in this map, `false` if there are. */
def isEmpty: Boolean
/**
* Adds the mapping `k -> opt.get` if opt is Some.
* Otherwise, it returns this map without the mapping for `k`.
* Adds the mapping `k -> opt.get` if opt is Some. Otherwise, it returns this map without the
* mapping for `k`.
*/
private[sbt] def setCond[T](k: AttributeKey[T], opt: Option[T]): AttributeMap
}
@ -199,11 +213,11 @@ object AttributeMap {
/** Constructs an [[AttributeMap]] containing the given `entries`. */
def apply(entries: Iterable[AttributeEntry[_]]): AttributeMap = empty ++ entries
/** Constructs an [[AttributeMap]] containing the given `entries`.*/
/** Constructs an [[AttributeMap]] containing the given `entries`. */
def apply(entries: AttributeEntry[_]*): AttributeMap = empty ++ entries
/** Presents an `AttributeMap` as a natural transformation. */
implicit def toNatTrans(map: AttributeMap): AttributeKey ~> Id = λ[AttributeKey ~> Id](map(_))
// implicit def toNatTrans(map: AttributeMap): AttributeKey ~> Id = λ[AttributeKey ~> Id](map(_))
}
private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any])
@ -230,8 +244,8 @@ private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any])
}
def entries: Iterable[AttributeEntry[_]] =
backing.collect {
case (k: AttributeKey[kt], v) => AttributeEntry(k, v.asInstanceOf[kt])
backing.collect { case (k: AttributeKey[kt], v) =>
AttributeEntry(k, v.asInstanceOf[kt])
}
private[sbt] def setCond[T](k: AttributeKey[T], opt: Option[T]): AttributeMap =
@ -269,7 +283,7 @@ object Attributed {
/** Extracts the underlying data from the sequence `in`. */
def data[T](in: Seq[Attributed[T]]): Seq[T] = in.map(_.data)
/** Associates empty metadata maps with each entry of `in`.*/
/** Associates empty metadata maps with each entry of `in`. */
def blankSeq[T](in: Seq[T]): Seq[Attributed[T]] = in map blank
/** Associates an empty metadata map with `data`. */

View File

@ -83,8 +83,8 @@ object Dag {
private[sbt] trait DirectedSignedGraph[Node] {
/**
* Directed edge type that tracks the sign and target (head) vertex.
* The sign can be obtained via [[isNegative]] and the target vertex via [[head]].
* Directed edge type that tracks the sign and target (head) vertex. The sign can be obtained
* via [[isNegative]] and the target vertex via [[head]].
*/
type Arrow
@ -103,10 +103,10 @@ object Dag {
}
/**
* Traverses a directed graph defined by `graph` looking for a cycle that includes a "negative" edge.
* The directed edges are weighted by the caller as "positive" or "negative".
* If a cycle containing a "negative" edge is detected, its member edges are returned in order.
* Otherwise, the empty list is returned.
* Traverses a directed graph defined by `graph` looking for a cycle that includes a "negative"
* edge. The directed edges are weighted by the caller as "positive" or "negative". If a cycle
* containing a "negative" edge is detected, its member edges are returned in order. Otherwise,
* the empty list is returned.
*/
private[sbt] def findNegativeCycle[Node](graph: DirectedSignedGraph[Node]): List[graph.Arrow] = {
import graph._
@ -132,8 +132,7 @@ object Dag {
between
else
visit(tail, stack)
} else
visit(tail, stack)
} else visit(tail, stack)
}
visit(graph.nodes, Nil)

View File

@ -9,7 +9,7 @@ package sbt.internal.util
import scala.collection.JavaConverters._
/** A mutable set interface that uses object identity to test for set membership.*/
/** A mutable set interface that uses object identity to test for set membership. */
trait IDSet[T] {
def apply(t: T): Boolean
def contains(t: T): Boolean

View File

@ -32,8 +32,8 @@ object Signals {
}
/**
* Register a signal handler that can be removed later.
* NOTE: Does not stack with other signal handlers!!!!
* Register a signal handler that can be removed later. NOTE: Does not stack with other signal
* handlers!!!!
*/
def register(handler: () => Unit, signal: String = INT): Registration =
// TODO - Maybe we can just ignore things if not is-supported.
@ -90,7 +90,8 @@ private final class Signals0 {
val oldHandler = Signal.handle(intSignal, newHandler)
try Right(action())
catch { case e: LinkageError => Left(e) } finally {
catch { case e: LinkageError => Left(e) }
finally {
Signal.handle(intSignal, oldHandler); ()
}
}

View File

@ -7,6 +7,7 @@
package sbt.internal.util
/*
trait TypeFunctions {
import TypeFunctions._
type Id[X] = X
@ -54,7 +55,9 @@ object TypeFunctions extends TypeFunctions {
}
}
}
*/
/*
trait ~>[-A[_], +B[_]] { outer =>
def apply[T](a: A[T]): B[T]
// directly on ~> because of type inference limitations
@ -68,3 +71,4 @@ object ~> {
val Id: Id ~> Id = idK[Id]
implicit def tcIdEquals: Id ~> Id = Id
}
*/

View File

@ -9,8 +9,8 @@ package sbt.internal.util
object Types extends Types
trait Types extends TypeFunctions {
val :^: = KCons
type :+:[H, T <: HList] = HCons[H, T]
val :+: = HCons
trait Types /* extends TypeFunctions */ {
// val :^: = KCons
// type :+:[H, T <: HList] = HCons[H, T]
// val :+: = HCons
}

View File

@ -15,8 +15,8 @@ import scala.language.experimental.macros
object Util {
def makeList[T](size: Int, value: T): List[T] = List.fill(size)(value)
def separateE[A, B](ps: Seq[Either[A, B]]): (Seq[A], Seq[B]) =
separate(ps)(Types.idFun)
// def separateE[A, B](ps: Seq[Either[A, B]]): (Seq[A], Seq[B]) =
// separate(ps)(Types.idFun)
def separate[T, A, B](ps: Seq[T])(f: T => Either[A, B]): (Seq[A], Seq[B]) = {
val (a, b) = ps.foldLeft((Nil: Seq[A], Nil: Seq[B]))((xs, y) => prependEither(xs, f(y)))
@ -45,7 +45,7 @@ object Util {
def quoteIfKeyword(s: String): String = if (ScalaKeywords.values(s)) s"`${s}`" else s
def ignoreResult[T](f: => T): Unit = macro Macro.ignore
// def ignoreResult[T](f: => T): Unit = macro Macro.ignore
lazy val isMac: Boolean =
System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("mac")
@ -73,7 +73,7 @@ object Util {
implicit class AnyOps[A](private val value: A) extends AnyVal {
def some: Option[A] = (Some(value): Option[A])
}
class Macro(val c: blackbox.Context) {
def ignore(f: c.Tree): c.Expr[Unit] = c.universe.reify({ c.Expr[Any](f).splice; () })
}
// class Macro(val c: blackbox.Context) {
// def ignore(f: c.Tree): c.Expr[Unit] = c.universe.reify({ c.Expr[Any](f).splice; () })
// }
}

View File

@ -0,0 +1,19 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
trait Applicative[F[_]] extends Apply[F]:
def pure[A](x: A): F[A]
override def map[A, B](fa: F[A])(f: A => B): F[B] =
ap(pure(f))(fa)
end Applicative
object Applicative:
given Applicative[Option] = OptionInstances.optionMonad
end Applicative

View File

@ -0,0 +1,19 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
trait Apply[F[_]] extends Functor[F]:
def ap[A, B](ff: F[A => B])(fa: F[A]): F[B]
def product[A, B](fa: F[A], fb: F[B]): F[(A, B)] =
ap(map(fa)(a => (b: B) => (a, b)))(fb)
end Apply
object Apply:
given Apply[Option] = OptionInstances.optionMonad
end Apply

View File

@ -0,0 +1,22 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
import scala.annotation.implicitNotFound
@implicitNotFound("Could not find an instance of FlatMap for ${F}")
trait FlatMap[F[_]] extends Apply[F]:
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
def flatten[A](ffa: F[F[A]]): F[A] =
flatMap(ffa)(fa => fa)
end FlatMap
object FlatMap:
given FlatMap[Option] = OptionInstances.optionMonad
end FlatMap

View File

@ -0,0 +1,16 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
trait Functor[F[_]]:
def map[A, B](fa: F[A])(f: A => B): F[B]
end Functor
object Functor:
given Functor[Option] = OptionInstances.optionMonad
end Functor

View File

@ -0,0 +1,23 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
private[sbt] object ListInstances:
lazy val listMonad: Monad[List] =
new Monad[List]:
def pure[A](x: A): List[A] = List(x)
def ap[A, B](ff: List[A => B])(fa: List[A]): List[B] =
for
f <- ff
a <- fa
yield f(a)
def flatMap[A, B](fa: List[A])(f: A => List[B]): List[B] = fa.flatMap(f)
override def map[A, B](fa: List[A])(f: A => B): List[B] = fa.map(f)
override def flatten[A](ffa: List[List[A]]): List[A] = ffa.flatten
end ListInstances

View File

@ -0,0 +1,19 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
import scala.annotation.implicitNotFound
@implicitNotFound("Could not find an instance of Monad for ${F}")
trait Monad[F[_]] extends FlatMap[F] with Applicative[F]:
//
end Monad
object Monad:
given Monad[Option] = OptionInstances.optionMonad
end Monad

View File

@ -0,0 +1,21 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
private[sbt] object OptionInstances:
lazy val optionMonad: Monad[Option] =
new Monad[Option]:
def pure[A](x: A): Option[A] = Some(x)
def ap[A, B](ff: Option[A => B])(fa: Option[A]): Option[B] =
if ff.isDefined && fa.isDefined then Some(ff.get(fa.get))
else None
def flatMap[A, B](fa: Option[A])(f: A => Option[B]): Option[B] = fa.flatMap(f)
override def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa.map(f)
override def flatten[A](ffa: Option[Option[A]]): Option[A] = ffa.flatten
end OptionInstances

View File

@ -0,0 +1,12 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.util
trait Selective[F[_]] extends Applicative[F]:
def select[A, B](fab: F[Either[A, B]])(fn: F[A => B]): F[B]
end Selective

View File

@ -7,11 +7,12 @@
package sbt.util
trait Show[A] {
trait Show[A]:
def show(a: A): String
}
object Show {
end Show
object Show:
def apply[A](f: A => String): Show[A] = a => f(a)
def fromToString[A]: Show[A] = _.toString
}
end Show

View File

@ -0,0 +1,40 @@
import hedgehog.*
import hedgehog.runner.*
import _root_.sbt.util.Functor
object FunctorTest extends Properties:
val F = summon[Functor[Option]]
override def tests: List[Test] =
List(
example("None", testNone),
property("identity", identityProperty),
property("composition", compositionProperty),
property("map", mapProperty),
)
def testNone: Result =
Result.assert(F.map(None: Option[Int])(_ + 1) == None)
def identityProperty: Property =
for x <- Gen.int(Range.linear(-100, 100)).forAll
yield F.map(Some(x))(identity) ==== Some(x)
def mapProperty: Property =
for
x <- Gen.int(Range.linear(-100, 100)).forAll
f <- genFun.forAll
yield F.map(Some(x))(f) ==== Some(f(x))
def compositionProperty: Property =
for
x <- Gen.int(Range.linear(-100, 100)).forAll
f <- genFun.forAll
g <- genFun.forAll
yield F.map(Some(x))(f compose g) ==== F.map(F.map(Some(x))(g))(f)
def genFun: Gen[Int => Int] =
for x <- Gen.int(Range.linear(-100, 100))
yield (_: Int) + x
end FunctorTest