Skip to content

Commit

Permalink
Add a section about tracked members to the modularity doc and update the
Browse files Browse the repository at this point in the history
syntax reference
  • Loading branch information
KacperFKorban committed Nov 18, 2024
1 parent b15153e commit 8208d46
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 13 deletions.
4 changes: 1 addition & 3 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3489,7 +3489,7 @@ object Parsers {
* UsingClsTermParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’
* ClsParams ::= ClsParam {‘,’ ClsParam}
* ClsParam ::= {Annotation}
* [{Modifier | ‘tracked’} (‘val’ | ‘var’)] Param
* [{Modifier} (‘val’ | ‘var’)] Param
* TypelessClause ::= DefTermParamClause
* | UsingParamClause
*
Expand Down Expand Up @@ -3527,8 +3527,6 @@ object Parsers {
if isErasedKw then
mods = addModifier(mods)
if paramOwner.isClass then
if isIdent(nme.tracked) && in.featureEnabled(Feature.modularity) && !in.lookahead.isColon then
mods = addModifier(mods)
mods = addFlag(modifiers(start = mods), ParamAccessor)
mods =
if in.token == VAL then
Expand Down
5 changes: 3 additions & 2 deletions docs/_docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ type val var while with yield
### Soft keywords

```
as derives end erased extension infix inline opaque open throws transparent using | * + -
as derives end erased extension infix inline opaque open throws tracked transparent using | * + -
```

See the [separate section on soft keywords](../reference/soft-modifier.md) for additional
Expand Down Expand Up @@ -381,7 +381,7 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’
| [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’
ClsParams ::= ClsParam {‘,’ ClsParam}
ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var
[{Modifier | ‘tracked’} (‘val’ | ‘var’)] Param
[{Modifier} (‘val’ | ‘var’)] Param
DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent
DefParamClause ::= DefTypeParamClause
Expand Down Expand Up @@ -418,6 +418,7 @@ LocalModifier ::= ‘abstract’
| ‘transparent’
| ‘infix’
| ‘erased’
| ‘tracked’
AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier]
AccessQualifier ::= ‘[’ id ‘]’
Expand Down
40 changes: 32 additions & 8 deletions docs/_docs/reference/experimental/modularity.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,6 @@ This works as it should now. Without the addition of `tracked` to the
parameter of `SetFunctor` typechecking would immediately lose track of
the element type `T` after an `add`, and would therefore fail.

**Syntax Change**

```
ClsParam ::= {Annotation} [{Modifier | ‘tracked’} (‘val’ | ‘var’)] Param
```

The (soft) `tracked` modifier is only allowed for `val` parameters of classes.

**Discussion**

Since `tracked` is so useful, why not assume it by default? First, `tracked` makes sense only for `val` parameters. If a class parameter is not also a field declared using `val` then there's nothing to refine in the constructor result type. One could think of at least making all `val` parameters tracked by default, but that would be a backwards incompatible change. For instance, the following code would break:
Expand All @@ -134,6 +126,38 @@ only if the class refers to a type member of `x`. But it turns out that this
scheme is unimplementable since it would quickly lead to cyclic references
when typechecking recursive class graphs. So an explicit `tracked` looks like the best available option.

## Tracked members

The `tracked` modifier can also be used for `val` members of classes and traits
to force the type of the member (or it's overriding member) to be as exact as
possible. More precisely, it will will assign the `tracked` member the infered
type of the rhs. For instance, consider the following definition:

```scala
trait F:
tracked val a: Int
tracked val b: Int

class N extends F:
val a = 22 // a.type =:= 22
val b: Int = 22 // b.type =:= Int
tracked val c = 22 // c.type =:= 22
```

Here, the `tracked` modifier ensures that the type of `a` in `N` is `22` and not
`Int`. But the type of `b` is `N` is `Int` since it's explicitly declared as
`Int`. `tracked` members can also be immediately initialized, as in the case of
`c`.

## Tracked syntax change

```
LocalModifier ::= ‘tracked’
```

The (soft) `tracked` modifier is allowed as a local modifier.


## Allow Class Parents to be Refined Types

Since `tracked` parameters create refinements in constructor types,
Expand Down
12 changes: 12 additions & 0 deletions tests/neg/abstract-tracked-1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import scala.language.experimental.modularity
import scala.language.future

trait F:
tracked val a: Int

class G:
val a: Int = 1

def Test =
val g = new G
summon[g.a.type <:< 1] // error
5 changes: 5 additions & 0 deletions tests/pos/abstract-tracked.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class L
trait M:
val f: Int

class N extends F:
val a = 10

object Test:
val f = new F:
val a = 1
Expand All @@ -39,6 +42,7 @@ object Test:
}
val m = new M:
tracked val f = 9
val n = new N

summon[f.a.type <:< 1]
summon[g.b.type <:< 2]
Expand All @@ -48,3 +52,4 @@ object Test:
summon[k.d.type <:< 7]
// summon[l.e.type <:< 8] // unrelated issue -- error: e is not a member of L
summon[m.f.type <:< 9]
summon[n.a.type <:< 10]

0 comments on commit 8208d46

Please sign in to comment.