Skip to content

Commit 5ed44d1

Browse files
committed
Add a section about tracked members to the modularity doc and update the
syntax reference
1 parent b15153e commit 5ed44d1

File tree

5 files changed

+52
-13
lines changed

5 files changed

+52
-13
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3489,7 +3489,7 @@ object Parsers {
34893489
* UsingClsTermParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’
34903490
* ClsParams ::= ClsParam {‘,’ ClsParam}
34913491
* ClsParam ::= {Annotation}
3492-
* [{Modifier | ‘tracked’} (‘val’ | ‘var’)] Param
3492+
* [{Modifier} (‘val’ | ‘var’)] Param
34933493
* TypelessClause ::= DefTermParamClause
34943494
* | UsingParamClause
34953495
*
@@ -3527,8 +3527,6 @@ object Parsers {
35273527
if isErasedKw then
35283528
mods = addModifier(mods)
35293529
if paramOwner.isClass then
3530-
if isIdent(nme.tracked) && in.featureEnabled(Feature.modularity) && !in.lookahead.isColon then
3531-
mods = addModifier(mods)
35323530
mods = addFlag(modifiers(start = mods), ParamAccessor)
35333531
mods =
35343532
if in.token == VAL then

docs/_docs/internals/syntax.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ type val var while with yield
141141
### Soft keywords
142142

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

147147
See the [separate section on soft keywords](../reference/soft-modifier.md) for additional
@@ -381,7 +381,7 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’
381381
| [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’
382382
ClsParams ::= ClsParam {‘,’ ClsParam}
383383
ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var
384-
[{Modifier | ‘tracked’} (‘val’ | ‘var’)] Param
384+
[{Modifier} (‘val’ | ‘var’)] Param
385385
386386
DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent
387387
DefParamClause ::= DefTypeParamClause
@@ -418,6 +418,7 @@ LocalModifier ::= ‘abstract’
418418
| ‘transparent’
419419
| ‘infix’
420420
| ‘erased’
421+
| ‘tracked’
421422
422423
AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier]
423424
AccessQualifier ::= ‘[’ id ‘]’

docs/_docs/reference/experimental/modularity.md

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,6 @@ This works as it should now. Without the addition of `tracked` to the
108108
parameter of `SetFunctor` typechecking would immediately lose track of
109109
the element type `T` after an `add`, and would therefore fail.
110110

111-
**Syntax Change**
112-
113-
```
114-
ClsParam ::= {Annotation} [{Modifier | ‘tracked’} (‘val’ | ‘var’)] Param
115-
```
116-
117-
The (soft) `tracked` modifier is only allowed for `val` parameters of classes.
118-
119111
**Discussion**
120112

121113
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:
@@ -134,6 +126,37 @@ only if the class refers to a type member of `x`. But it turns out that this
134126
scheme is unimplementable since it would quickly lead to cyclic references
135127
when typechecking recursive class graphs. So an explicit `tracked` looks like the best available option.
136128

129+
## Tracked members
130+
131+
The `tracked` modifier can also be used for `val` members of classes and traits
132+
to force the type of the member (or it's overriding member) to be as exact as
133+
possible. For instance, consider the following definition:
134+
135+
```scala
136+
trait F:
137+
tracked val a: Int
138+
tracked val b: Int
139+
140+
class N extends F:
141+
val a = 22 // a.type =:= 22
142+
val b: Int = 22 // b.type =:= Int
143+
tracked val c = 22 // c.type =:= 22
144+
```
145+
146+
Here, the `tracked` modifier ensures that the type of `a` in `N` is `22` and not
147+
`Int`. But the type of `b` is `N` is `Int` since it's explicitly declared as
148+
`Int`. `tracked` members can also be immediately initialized, as in the case of
149+
`c`.
150+
151+
## Tracked syntax change
152+
153+
```
154+
LocalModifier ::= ‘tracked’
155+
```
156+
157+
The (soft) `tracked` modifier is allowed as a local modifier.
158+
159+
137160
## Allow Class Parents to be Refined Types
138161

139162
Since `tracked` parameters create refinements in constructor types,

tests/neg/abstract-tracked-1.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import scala.language.experimental.modularity
2+
import scala.language.future
3+
4+
trait F:
5+
tracked val a: Int
6+
7+
class G:
8+
val a: Int = 1
9+
10+
def Test =
11+
val g = new G
12+
summon[g.a.type <:< 1] // error

tests/pos/abstract-tracked.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ class L
2222
trait M:
2323
val f: Int
2424

25+
class N extends F:
26+
val a = 10
27+
2528
object Test:
2629
val f = new F:
2730
val a = 1
@@ -39,6 +42,7 @@ object Test:
3942
}
4043
val m = new M:
4144
tracked val f = 9
45+
val n = new N
4246

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

0 commit comments

Comments
 (0)