Skip to content

Scaladoc Support for Capture & Separation Checking #23471

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions local/project/dummy/arrows.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package dummy

import language.experimental.captureChecking
import caps.*

trait Nested:
val c: AnyRef^
val next: Nested

trait Arrows:
val a: AnyRef^
val b: AnyRef^
val c: AnyRef^

val purev: Int -> Int
val purev2: Int ->{} Int
val impurev: Int => Int
val impurev2: Int ->{a,b,c} Int
val impurev3: Int ->{a,b,c} Int => Int
val impureCap: Int ->{cap} Int
val impureCap2: Int ->{cap, a, b, c} Int
val contextPureV: Int ?-> Int
val contextPureV2: Int ?->{} Int
val contextImpureV: Int ?=> Int
val contextImpureV2: Int ?->{a,b,c} Int
val contextImpureV3: Int ?->{a,b,c} Int ?=> Int
val contextImpureCap: Int ?->{cap} Int
val contextImpureCap2: Int ?->{cap, a, b, c} Int

def pure(f: Int -> Int): Int
def pure2(f: Int ->{} Int): Int
def impure(f: Int => Int): Int
def impure2(f: Int ->{a,b,c} Int): Int
def impure3(f: Int ->{a,b,c} Int => Int): Int

def uses(@use a: AnyRef^): Any
def uses2(@use x: AnyRef^{a}, @use y: AnyRef^{b}): Any

def consumes(@consume a: AnyRef^): Any
def consumes2(@consume x: AnyRef^{a}, @consume y: AnyRef^{b}): Any

def usesAndConsumes(@use a: AnyRef^, @consume b: AnyRef^): Any
def usesAndConsumes2(@use @consume x: AnyRef^{a}): Any
def consumesAndUses(@consume @use x: AnyRef^{a}): Any
def consumesAndUses2(@consume @use x: List[AnyRef^]): Array[AnyRef^{x*}]

def reachThis: AnyRef^{this*}

def byNamePure(f: -> Int): Int
def byNameImpure(f: ->{a,b,c} Int): Int
def byNameImpure2(f: => Int): Int

def pathDependent(n: Nested^)(g: AnyRef^{n.c} => Any): Any
def pathDependent2(n: Nested^)(g: AnyRef^{n.next.c} => Any): Any
def pathDependent3(n: Nested^)(g: AnyRef^{n.c} => AnyRef^{n.next.c} ->{n.c} Any): Any
def pathDependent4(n: Nested^)(g: AnyRef^{n.c} => AnyRef^{n.next.c} ->{n.c} Any): AnyRef^{n.next.next.c}
def pathDependent5(n: Nested^)(g: AnyRef^{n.c} => AnyRef^{n.next.c} ->{n.c} Any): AnyRef^{n.next.next.c*, n.c, cap}

def contextPure(f: AnyRef^{a} ?-> Int): Int
def contextImpure(f: AnyRef^{a} ?=> Int): Int
def contextImpure2(f: AnyRef^{a} ?->{b,c} Int): Int
def contextImpure3(f: AnyRef^{a} ?->{b,c} Int => AnyRef^{a} ?=> Int): Int

val noParams: () -> () -> Int
val noParams2: () ->{} () ->{} Int
val noParamsImpure: () => () => Int => Unit

val uncurried: (x: AnyRef^, y: AnyRef^) -> AnyRef^{x,y} => Int ->{x,y} Int
val uncurried2: (x: AnyRef^, y: AnyRef^) -> AnyRef => Int ->{x,y} Int
val uncurried3: (x: AnyRef^, y: AnyRef^) => AnyRef
val uncurried4: (x: AnyRef^, y: AnyRef^) ->{a,b} AnyRef^ => Int ->{x,y} Int

val contextUncurried: (x: AnyRef^{a}, y: AnyRef^{b}) ?-> AnyRef^{x,y} ?-> Int ?->{x,y} Int
val contextUncurried2: (x: AnyRef^{a}, y: AnyRef^{b}) ?-> AnyRef ?-> Int ?->{x,y} Int
val contextUncurried3: (x: AnyRef^{a}, y: AnyRef^{b}) ?=> AnyRef
val contextUncurried4: (x: AnyRef^{a}, y: AnyRef^{b}) ?->{a,b} AnyRef^ ?=> Int ?->{x,y} Int

def polyPure[A](f: A -> Int): Int
def polyPure2[A](f: A ->{} Int): Int
def polyImpure[A](f: A => Int): Int
def polyImpure2[A](f: A ->{a,b,c} Int): Int
def polyImpure3[A](f: A ->{a,b,c} Int => Int): Int

def polyContextPure[A](f: A ?-> Int): Int
def polyContextPure2[A](f: A ?->{} Int): Int
def polyContextImpure[A](f: A ?=> Int): Int
def polyContextImpure2[A](f: A ?->{a,b,c} Int): Int
def polyContextImpure3[A](f: A ?->{a,b,c} Int => Int): Int

val polyPureV: [A] => A -> Int
val polyPureV2: [A] => Int => A ->{a,b,c} Int
val polyImpureV: [A] -> A => Int
val polyImpureV2: [A] -> A => Int
27 changes: 27 additions & 0 deletions local/project/dummy/capturevars.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package dummy

import language.experimental.captureChecking
import caps.*

trait Test:
val a: AnyRef^
val b: AnyRef^
type Ordinary
type Ordinary2 >: Int <: String
type T[-C^ >: {a,b}]
type U[+C^]
type Foo = [C^ >: {a,b} <: {a,b,cap}] =>> AnyRef^{C}
type C^
type D^ >: {C} <: {a,b}
type E^ <: C
type F^ <: {D,E}
type G^ = C
type H^ = {C}
def foo[C^ >: {a,b}](x: T[C]): Unit
def bar(x: T[{a,b}]): Unit
def baz(x: T[{a,b,caps.cap}]): Unit
def foo2[C^](x: U[C]): Unit
def bar2(x: U[{a,b,cap}]): Unit
def baz2(x: U[{caps.cap}]): Unit
def test[E^, F^ >: {caps.cap} <: {}, G <: [C^ >: {a,b} <: {a,b}] =>> AnyRef^{C}](x: T[{E,a,b}], y: U[F]): Unit
val poly: [C^ >: {a,b}] => (f: () ->{C} Unit) -> Int ->{C} Unit
34 changes: 34 additions & 0 deletions local/project/dummy/colltest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dummy

import language.experimental.captureChecking
// Showing a problem with recursive references
object CollectionStrawMan5 {

/** Base trait for generic collections */
trait Iterable[+A] extends IterableLike[A] {
def iterator: Iterator[A]^{this}
def coll: Iterable[A]^{this} = this
}

trait IterableLike[+A]:
def coll: Iterable[A]^{this}
def partition(p: A => Boolean): Unit =
val pn = Partition(coll, p)
()

/** Concrete collection type: View */
trait View[+A] extends Iterable[A] with IterableLike[A]

case class Partition[A](val underlying: Iterable[A]^, p: A => Boolean) {

class Partitioned(expected: Boolean) extends View[A]:
this: Partitioned^{Partition.this} =>
def iterator: Iterator[A]^{this} =
underlying.iterator.filter((x: A) => p(x) == expected)

val left: Partitioned^{Partition.this} = Partitioned(true)
val right: Partitioned^{Partition.this} = Partitioned(false)
}


}
7 changes: 7 additions & 0 deletions local/project/dummy/nocc.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dummy

trait NoCaptureChecking:
def byName(f: => Int): Int
def impure(f: Int => Int): Int
def context(f: Int ?=> Int): Int
def dependent(f: (x: Int) => x.type): Int
27 changes: 27 additions & 0 deletions local/project/dummy/sep-pairs.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package dummy
import language.experimental.captureChecking
import caps.Mutable
import caps.{cap, consume, use}

class Ref extends Mutable:
var x = 0
def get: Int = x
update def put(y: Int): Unit = x = y

case class Pair[+A, +B](fst: A, snd: B)

def mkPair: Pair[Ref^, Ref^] =
val r1 = Ref()
val r2 = Ref()
val p_exact: Pair[Ref^{r1}, Ref^{r2}] = Pair(r1, r2)
p_exact

def copyPair(@consume @use p: Pair[Ref^, Ref^]): Pair[Ref^, Ref^] =
val x: Ref^{p.fst*} = p.fst
val y: Ref^{p.snd*} = p.snd
Pair(x, y)

trait TestRd:
@consume def copyPair(@use p: Pair[Ref^, Ref^]): Pair[Ref^{p.fst*}, Ref^{p.snd*}]
def rdPair(@consume p: Pair[Ref^, Ref^]): Int ->{p.fst*.rd} Int
val rdPairV: (p: Pair[Ref^, Ref^]) => Int ->{p.fst*, p.snd*.rd} Int
43 changes: 43 additions & 0 deletions mystuff.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import sbt._
import sbt.io.IO

import sbt.dsl.LinterLevel.Ignore

lazy val compileSrcTree = taskKey[Unit]("Example project")

compileSrcTree := {
val log = streams.value.log
val baseDir = baseDirectory.value
val srcTreeDir = baseDir / "local" / "project"
val outDir = baseDir / "local" / "out"

IO.delete(outDir)
IO.createDirectory(outDir) // mkdir -p

val sources: Seq[String] =
(srcTreeDir ** "*.scala").get.map(_.getPath) // find all .scala

if (sources.isEmpty)
streams.value.log.warn(s"No .scala files found under $srcTreeDir")
else {
val cmd = ("scalac" +: "-d" +: outDir.getPath +: sources).mkString(" ")
Command.process(cmd, state.value)
}
}

lazy val ensureApiDir = taskKey[Unit]("Create <repo>/local/api if it’s missing")

ensureApiDir := {
val dir = (ThisBuild / baseDirectory).value / "local" / "api"
IO.createDirectory(dir)
}

addCommandAlias(
"myrefresh",
";compileSrcTree; ensureApiDir ; scaladoc/runMain dotty.tools.scaladoc.Main -siteroot /dev/null -project Foo -project-version 0.0.1 -d local/api local/out"
)

addCommandAlias(
"myscaladoc",
"; ensureApiDir ; scaladoc/runMain dotty.tools.scaladoc.Main -siteroot /dev/null -project Foo -project-version 0.0.1 -d local/api local/out"
)
10 changes: 5 additions & 5 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2584,7 +2584,7 @@ object ScaladocConfigs {

def defaultSourceLinks(version: String = dottyNonBootstrappedVersion, refVersion: String = dottyVersion) = Def.task {
def stdLibVersion = stdlibVersion(NonBootstrapped)
def srcManaged(v: String, s: String) = s"out/bootstrap/scala2-library-bootstrapped/scala-$v/src_managed/main/$s-library-src"
def srcManaged(v: String, s: String) = s"out/bootstrap/scala2-library-cc/scala-$v/src_managed/main/$s-library-src"
SourceLinks(
List(
scalaSrcLink(stdLibVersion, srcManaged(version, "scala") + "="),
Expand Down Expand Up @@ -2673,7 +2673,7 @@ object ScaladocConfigs {

lazy val Scala3 = Def.task {
val dottyJars: Seq[java.io.File] = Seq(
(`scala2-library-bootstrapped`/Compile/products).value,
(`scala2-library-cc`/Compile/products).value,
(`scala3-library-bootstrapped`/Compile/products).value,
(`scala3-interfaces`/Compile/products).value,
(`tasty-core-bootstrapped`/Compile/products).value,
Expand All @@ -2682,7 +2682,7 @@ object ScaladocConfigs {
val roots = dottyJars.map(_.getAbsolutePath)

val managedSources =
(`scala2-library-bootstrapped`/Compile/sourceManaged).value / "scala-library-src"
(`scala2-library-cc`/Compile/sourceManaged).value / "scala-library-src"
val projectRoot = (ThisBuild/baseDirectory).value.toPath
val stdLibRoot = projectRoot.relativize(managedSources.toPath.normalize())
val docRootFile = stdLibRoot.resolve("rootdoc.txt")
Expand Down Expand Up @@ -2718,7 +2718,7 @@ object ScaladocConfigs {
}

def stableScala3(version: String) = Def.task {
val scalaLibrarySrc = s"out/bootstrap/scala2-library-bootstrapped/scala-$version-bin-SNAPSHOT-nonbootstrapped/src_managed"
val scalaLibrarySrc = s"out/bootstrap/scala2-library-cc/scala-$version-bin-SNAPSHOT-nonbootstrapped/src_managed"
val dottyLibrarySrc = "library/src"
Scala3.value
.add(defaultSourceLinks(version + "-bin-SNAPSHOT-nonbootstrapped", version).value)
Expand All @@ -2739,7 +2739,7 @@ object ScaladocConfigs {
.add(DocRootContent(s"$scalaLibrarySrc/rootdoc.txt"))
.withTargets(
Seq(
s"out/bootstrap/scala2-library-bootstrapped/scala-$version-bin-SNAPSHOT-nonbootstrapped/classes",
s"out/bootstrap/scala2-library-cc/scala-$version-bin-SNAPSHOT-nonbootstrapped/classes",
s"out/bootstrap/scala3-library-bootstrapped/scala-$version-bin-SNAPSHOT-nonbootstrapped/classes",
s"tmp/interfaces/target/classes",
s"out/bootstrap/tasty-core-bootstrapped/scala-$version-bin-SNAPSHOT-nonbootstrapped/classes"
Expand Down
6 changes: 4 additions & 2 deletions scaladoc/src/dotty/tools/scaladoc/api.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum Modifier(val name: String, val prefix: Boolean):
case Transparent extends Modifier("transparent", true)
case Infix extends Modifier("infix", true)
case AbsOverride extends Modifier("abstract override", true)
case Update extends Modifier("update", 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)
Expand All @@ -69,7 +70,7 @@ enum Kind(val name: String):
case Var extends Kind("var")
case Val extends Kind("val")
case Exported(base: Kind) extends Kind("export")
case Type(concreate: Boolean, opaque: Boolean, typeParams: Seq[TypeParameter])
case Type(concreate: Boolean, opaque: Boolean, typeParams: Seq[TypeParameter], isCaptureVar: Boolean = false)
extends Kind("type") // should we handle opaque as modifier?
case Given(kind: Def | Class | Val.type, as: Option[Signature], conversion: Option[ImplicitConversion])
extends Kind("given") with ImplicitConversionProvider
Expand Down Expand Up @@ -120,7 +121,8 @@ case class TypeParameter(
variance: "" | "+" | "-",
name: String,
dri: DRI,
signature: Signature
signature: Signature,
isCaptureVar: Boolean = false // under capture checking
)

case class Link(name: String, dri: DRI)
Expand Down
Loading