Skip to content

Docs/enable snippet compiler on enums reference #19498

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
28 changes: 14 additions & 14 deletions docs/_docs/reference/enums/adts.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ is treated as a normal enum value.
The `extends` clauses that were omitted in the example above can also
be given explicitly:

```scala
```scala sc-name:option
enum Option[+T]:
case Some(x: T) extends Option[T]
case None extends Option[Nothing]
Expand All @@ -39,21 +39,21 @@ As for normal enum values, the cases of an `enum` are all defined in
the `enum`s companion object. So it's `Option.Some` and `Option.None`
unless the definitions are "pulled out" with an import:

```scala
scala> Option.Some("hello")
val res1: t2.Option[String] = Some(hello)
```scala sc-compile-with:option
val option = Option.Some("hello")
// some: Option[String] = Some(hello)

scala> Option.None
val res2: t2.Option[Nothing] = None
val none = Option.None
// none: Option[Nothing] = None
```

Note that the type of the expressions above is always `Option`. Generally, the type of a enum case constructor application will be widened to the underlying enum type, unless a more specific type is expected. This is a subtle difference with respect to normal case classes. The classes making up the cases do exist, and can be unveiled, either by constructing them directly with a `new`, or by explicitly providing an expected type.

```scala
scala> new Option.Some(2)
val res3: Option.Some[Int] = Some(2)
scala> val x: Option.Some[Int] = Option.Some(3)
val res4: Option.Some[Int] = Some(3)
```scala sc-compile-with:option
val some0 = new Option.Some(2)
// some: Option.Some[Int] = Some(2)
val some1: Option.Some[Int] = Option.Some(3)
// opt: Option.Some[Int] = Some(3)
```

As all other enums, ADTs can define methods. For instance, here is `Option` again, with an
Expand Down Expand Up @@ -100,15 +100,15 @@ below:
The following `View` enum has a contravariant type parameter `T` and a single case `Refl`, representing a function
mapping a type `T` to itself:

```scala
```scala sc:nocompile
enum View[-T]:
case Refl(f: T => T)
```

The definition of `Refl` is incorrect, as it uses contravariant type `T` in the covariant result position of a
function type, leading to the following error:

```scala
```scala sc:nocompile
-- Error: View.scala:2:12 --------
2 | case Refl(f: T => T)
| ^^^^^^^^^
Expand All @@ -118,7 +118,7 @@ function type, leading to the following error:

Because `Refl` does not declare explicit parameters, it looks to the compiler like the following:

```scala
```scala sc:nocompile
enum View[-T]:
case Refl[/*synthetic*/-T1](f: T1 => T1) extends View[T1]
```
Expand Down
44 changes: 22 additions & 22 deletions docs/_docs/reference/enums/desugarEnums.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ are missing them. Rules (7) to (9) define how such cases with `extends` clauses
map into `case class`es or `val`s.

1. An `enum` definition
```scala
```scala sc:nocompile
enum E ... { <defs> <cases> }
```
expands to a `sealed abstract` class that extends the `scala.reflect.Enum` trait and
an associated companion object that contains the defined cases, expanded according
to rules (2 - 8). The enum class starts with a compiler-generated import that imports
the names `<caseIds>` of all cases so that they can be used without prefix in the class.
```scala
```scala sc:nocompile
sealed abstract class E ... extends <parents> with scala.reflect.Enum {
import E.{ <caseIds> }
<defs>
Expand All @@ -48,80 +48,80 @@ map into `case class`es or `val`s.
```

2. A simple case consisting of a comma-separated list of enum names
```scala
```scala sc:nocompile
case C_1, ..., C_n
```
expands to
```scala
```scala sc:nocompile
case C_1; ...; case C_n
```
Any modifiers or annotations on the original case extend to all expanded
cases.

3. A simple case
```scala
```scala sc:nocompile
case C
```
of an enum `E` that does not take type parameters expands to
```scala
```scala sc:nocompile
val C = $new(n, "C")
```
Here, `$new` is a private method that creates an instance of `E` (see
below).

4. If `E` is an enum with type parameters
```scala
```scala sc:nocompile
V1 T1 >: L1 <: U1 , ... , Vn Tn >: Ln <: Un (n > 0)
```
where each of the variances `Vi` is either `'+'` or `'-'`, then a simple case
```scala
```scala sc:nocompile
case C
```
expands to
```scala
```scala sc:nocompile
case C extends E[B1, ..., Bn]
```
where `Bi` is `Li` if `Vi = '+'` and `Ui` if `Vi = '-'`. This result is then further
rewritten with rule (8). Simple cases of enums with non-variant type
parameters are not permitted (however value cases with explicit `extends` clause are)

5. A class case without an extends clause
```scala
```scala sc:nocompile
case C <type-params> <value-params>
```
of an enum `E` that does not take type parameters expands to
```scala
```scala sc:nocompile
case C <type-params> <value-params> extends E
```
This result is then further rewritten with rule (9).

6. If `E` is an enum with type parameters `Ts`, a class case with neither type parameters nor an extends clause
```scala
```scala sc:nocompile
case C <value-params>
```
expands to
```scala
```scala sc:nocompile
case C[Ts] <value-params> extends E[Ts]
```
This result is then further rewritten with rule (9). For class cases that have type parameters themselves, an extends clause needs to be given explicitly.

7. If `E` is an enum with type parameters `Ts`, a class case without type parameters but with an extends clause
```scala
```scala sc:nocompile
case C <value-params> extends <parents>
```
expands to
```scala
```scala sc:nocompile
case C[Ts] <value-params> extends <parents>
```
provided at least one of the parameters `Ts` is mentioned in a parameter type in
`<value-params>` or in a type argument in `<parents>`.

8. A value case
```scala
```scala sc:nocompile
case C extends <parents>
```
expands to a value definition in `E`'s companion object:
```scala
```scala sc:nocompile
val C = new <parents> { <body>; def ordinal = n }
```
where `n` is the ordinal number of the case in the companion object,
Expand All @@ -132,15 +132,15 @@ map into `case class`es or `val`s.
in a type argument of `<parents>`.

9. A class case
```scala
```scala sc:nocompile
case C <params> extends <parents>
```
expands analogous to a final case class in `E`'s companion object:
```scala
```scala sc:nocompile
final case class C <params> extends <parents>
```
The enum case defines an `ordinal` method of the form
```scala
```scala sc:nocompile
def ordinal = n
```
where `n` is the ordinal number of the case in the companion object,
Expand All @@ -151,7 +151,7 @@ map into `case class`es or `val`s.
a type parameter of the case, i.e. the parameter name is defined in `<params>`.

The compiler-generated `apply` and `copy` methods of an enum case
```scala
```scala sc:nocompile
case C(ps) extends P1, ..., Pn
```
are treated specially. A call `C(ts)` of the apply method is ascribed the underlying type
Expand All @@ -175,7 +175,7 @@ If `E` contains at least one simple case, its companion object will define in ad
ordinal number and name. This method can be thought as being defined as
follows.

```scala
```scala sc:nocompile
private def $new(_$ordinal: Int, $name: String) =
new E with runtime.EnumValue:
def ordinal = _$ordinal
Expand Down
47 changes: 23 additions & 24 deletions docs/_docs/reference/enums/enums.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ companion object.

Enums can be parameterized.

```scala
```scala sc-name:color
enum Color(val rgb: Int):
case Red extends Color(0xFF0000)
case Green extends Color(0x00FF00)
Expand All @@ -34,11 +34,10 @@ explicit extends clause.
The values of an enum correspond to unique integers. The integer
associated with an enum value is returned by its `ordinal` method:

```scala
scala> val red = Color.Red
val red: Color = Red
scala> red.ordinal
val res0: Int = 0
```scala sc-compile-with:color
val red = Color.Red
val ord = red.ordinal
assert(ord == 0)
```

The companion object of an enum also defines three utility methods.
Expand All @@ -47,20 +46,20 @@ by its name. The `values` method returns all enum values
defined in an enumeration in an `Array`. The `fromOrdinal`
method obtains an enum value from its ordinal (`Int`) value.

```scala
scala> Color.valueOf("Blue")
val res0: Color = Blue
scala> Color.values
val res1: Array[Color] = Array(Red, Green, Blue)
scala> Color.fromOrdinal(0)
val res2: Color = Red
```scala sc-compile-with:color
val blue = Color.valueOf("Blue")
// blue: Color = Blue
val values = Color.values
// values: Array[Color] = Array(Red, Green, Blue)
val red = Color.fromOrdinal(0)
// red: Color = Red
```

## User-defined members of enums

It is possible to add your own definitions to an enum. Example:

```scala
```scala sc-name:planet
enum Planet(mass: Double, radius: Double):
private final val G = 6.67300E-11
def surfaceGravity = G * mass / (radius * radius)
Expand All @@ -80,7 +79,7 @@ end Planet
## User-defined companion object of enums
It is also possible to define an explicit companion object for an enum:

```scala
```scala sc-compile-with:planet
object Planet:
def main(args: Array[String]) =
val earthWeight = args(0).toDouble
Expand All @@ -100,7 +99,7 @@ enum class.
Similarly, enum case declarations may not directly reference members of the enum's companion object,
even if they are imported (directly, or by renaming). For example:

```scala
```scala sc:nocompile
import Planet.*
enum Planet(mass: Double, radius: Double):
private final val (mercuryMass, mercuryRadius) = (3.303e+23, 2.4397e6)
Expand Down Expand Up @@ -146,7 +145,7 @@ We now want to deprecate the `Pluto` case. First we add the `scala.deprecated` a

Outside the lexical scopes of `enum Planet` or `object Planet`, references to `Planet.Pluto` will produce a deprecation warning, but within those scopes we can still reference it to implement introspection over the deprecated cases:

```scala
```scala sc:nocompile
trait Deprecations[T <: reflect.Enum] {
extension (t: T) def isDeprecatedCase: Boolean
}
Expand All @@ -166,7 +165,7 @@ We could imagine that a library may use [type class derivation](../contextual/de
If you want to use the Scala-defined enums as [Java enums](https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html), you can do so by extending
the class `java.lang.Enum`, which is imported by default, as follows:

```scala
```scala sc-name:jcolor
enum Color extends Enum[Color] { case Red, Green, Blue }
```

Expand All @@ -175,9 +174,9 @@ There is no need to provide constructor arguments (as defined in the Java API do

After defining `Color` like that, you can use it like you would a Java enum:

```scala
scala> Color.Red.compareTo(Color.Green)
val res15: Int = -1
```scala sc-compile-with:jcolor
val cmp = Color.Red.compareTo(Color.Green)
assert(cmp == -1)
```

For a more in-depth example of using Scala 3 enums from Java, see [this test](https://github.com/lampepfl/dotty/tree/main/tests/run/enum-java). In the test, the enums are defined in the `MainScala.scala` file and used from a Java source, `Test.java`.
Expand All @@ -187,7 +186,7 @@ For a more in-depth example of using Scala 3 enums from Java, see [this test](ht
Enums are represented as `sealed` classes that extend the `scala.reflect.Enum` trait.
This trait defines a single public method, `ordinal`:

```scala
```scala sc:nocompile
package scala.reflect

/** A base trait of all Scala enum definitions */
Expand All @@ -200,7 +199,7 @@ transparent trait Enum extends Any, Product, Serializable:
Enum values with `extends` clauses get expanded to anonymous class instances.
For instance, the `Venus` value above would be defined like this:

```scala
```scala sc:nocompile
val Venus: Planet = new Planet(4.869E24, 6051800.0):
def ordinal: Int = 1
override def productPrefix: String = "Venus"
Expand All @@ -212,7 +211,7 @@ that can be instantiated using a private method that takes a tag and a name as a
For instance, the first
definition of value `Color.Red` above would expand to:

```scala
```scala sc:nocompile
val Red: Color = $new(0, "Red")
```

Expand Down
24 changes: 11 additions & 13 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2323,6 +2323,13 @@ object ScaladocConfigs {
.withTargets(tastyRoots)
}

def snippetCompilerTargets(dottyLibSrc:String) = List(
s"$dottyLibSrc/scala=compile",
s"$dottyLibSrc/scala/quoted=compile",
s"$dottyLibSrc/scala/compiletime=compile",
s"$dottyLibSrc/scala/util=compile",
s"$dottyLibSrc/scala/util/control=compile",
)
lazy val Scala3 = Def.task {
val dottyJars: Seq[java.io.File] = Seq(
(`scala2-library-bootstrapped`/Compile/products).value,
Expand Down Expand Up @@ -2358,11 +2365,9 @@ object ScaladocConfigs {
)))
.add(VersionsDictionaryUrl("https://scala-lang.org/api/versions.json"))
.add(DocumentSyntheticTypes(true))
.add(SnippetCompiler(List(
s"$dottyLibRoot/src/scala=compile",
s"$dottyLibRoot/src/scala/compiletime=compile",
s"$dottyLibRoot/src/scala/util=compile",
s"$dottyLibRoot/src/scala/util/control=compile"
.add(SnippetCompiler(
snippetCompilerTargets(s"$dottyLibRoot/src") ++ List(
"docs/_docs/reference/enums=compile"
)))
.add(SiteRoot("docs"))
.add(ApiSubdirectory(true))
Expand All @@ -2375,14 +2380,7 @@ object ScaladocConfigs {
Scala3.value
.add(defaultSourceLinks(version + "-bin-SNAPSHOT-nonbootstrapped", version).value)
.add(ProjectVersion(version))
.add(SnippetCompiler(
List(
s"$dottyLibrarySrc/scala/quoted=compile",
s"$dottyLibrarySrc/scala/compiletime=compile",
s"$dottyLibrarySrc/scala/util=compile",
s"$dottyLibrarySrc/scala/util/control=compile"
)
))
.add(SnippetCompiler(snippetCompilerTargets(dottyLibrarySrc)))
.add(CommentSyntax(List(
s"$dottyLibrarySrc=markdown",
s"$scalaLibrarySrc=wiki",
Expand Down