Skip to content
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

Implement tracked members #21761

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
12 changes: 10 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1007,12 +1007,13 @@ object desugar {
if mods.isAllOf(Given | Inline | Transparent) then
report.error("inline given instances cannot be trasparent", cdef)
var classMods = if mods.is(Given) then mods &~ (Inline | Transparent) | Synthetic else mods
if vparamAccessors.exists(_.mods.is(Tracked)) then
val newBody = tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths
if newBody.collect { case d: ValOrDefDef => d }.exists(_.mods.is(Tracked)) then
classMods |= Dependent
cpy.TypeDef(cdef: TypeDef)(
name = className,
rhs = cpy.Template(impl)(constr, parents1, clsDerived, self1,
tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths)
newBody)
).withMods(classMods)
}

Expand Down Expand Up @@ -1480,6 +1481,12 @@ object desugar {
rhsOK(rhs)
}

val legalTracked: Context ?=> MemberDefTest = {
case valdef @ ValDef(_, _, _) =>
val sym = valdef.symbol
!ctx.owner.exists || ctx.owner.isClass || ctx.owner.is(Case) || ctx.owner.isConstructor || valdef.mods.is(Param) || valdef.mods.is(ParamAccessor)
}

def checkOpaqueAlias(tree: MemberDef)(using Context): MemberDef =
def check(rhs: Tree): MemberDef = rhs match
case bounds: TypeBoundsTree if bounds.alias.isEmpty =>
Expand All @@ -1505,6 +1512,7 @@ object desugar {
} else tested
tested = checkOpaqueAlias(tested)
tested = checkApplicable(Opaque, legalOpaque)
tested = checkApplicable(Tracked, legalTracked)
tested
case _ =>
tree
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ object Scanners {

def featureEnabled(name: TermName) = Feature.enabled(name)(using languageImportContext)
def erasedEnabled = featureEnabled(Feature.erasedDefinitions)
def trackedEnabled = featureEnabled(Feature.modularity)

private var postfixOpsEnabledCache = false
private var postfixOpsEnabledCtx: Context = NoContext
Expand Down Expand Up @@ -1195,7 +1196,7 @@ object Scanners {

def isSoftModifier: Boolean =
token == IDENTIFIER
&& (softModifierNames.contains(name) || name == nme.erased && erasedEnabled)
&& (softModifierNames.contains(name) || name == nme.erased && erasedEnabled || name == nme.tracked && trackedEnabled)

def isSoftModifierInModifierPosition: Boolean =
isSoftModifier && inModifierPosition()
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -877,16 +877,16 @@ class Namer { typer: Typer =>
protected def addAnnotations(sym: Symbol): Unit = original match {
case original: untpd.MemberDef =>
lazy val annotCtx = annotContext(original, sym)
original.setMods:
original.setMods:
original.mods.withAnnotations :
original.mods.annotations.mapConserve: annotTree =>
original.mods.annotations.mapConserve: annotTree =>
val cls = typedAheadAnnotationClass(annotTree)(using annotCtx)
if (cls eq sym)
report.error(em"An annotation class cannot be annotated with iself", annotTree.srcPos)
annotTree
else
val ann =
if cls.is(JavaDefined) then Checking.checkNamedArgumentForJavaAnnotation(annotTree, cls.asClass)
val ann =
if cls.is(JavaDefined) then Checking.checkNamedArgumentForJavaAnnotation(annotTree, cls.asClass)
else annotTree
val ann1 = Annotation.deferred(cls)(typedAheadExpr(ann)(using annotCtx))
sym.addAnnotation(ann1)
Expand Down
24 changes: 18 additions & 6 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2411,7 +2411,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
else if ctx.reporter.errorsReported then UnspecifiedErrorType
else errorType(em"cannot infer type; expected type $pt is not fully defined", tree.srcPos))

def typedTypeTree(tree: untpd.TypeTree, pt: Type)(using Context): Tree =
def typedTypeTree(tree: untpd.TypeTree, pt: Type)(using Context): Tree = {
tree match
case tree: untpd.DerivedTypeTree =>
tree.ensureCompletions
Expand All @@ -2427,6 +2427,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
}
case _ =>
completeTypeTree(InferredTypeTree(), pt, tree)
}

def typedInLambdaTypeTree(tree: untpd.InLambdaTypeTree, pt: Type)(using Context): Tree =
val tp =
Expand Down Expand Up @@ -2833,11 +2834,20 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
case rhs =>
excludeDeferredGiven(rhs, sym):
typedExpr(_, tpt1.tpe.widenExpr)
val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym)
postProcessInfo(vdef1, sym)
vdef1.setDefTree
setAbstractTrackedInfo(sym, rhs1, tpt)
val tpt2 = if sym.flags.is(Tracked) && tpt.isEmpty && !sym.flags.is(ParamAccessor) && !sym.flags.is(Param) then TypeTree(rhs1.tpe) else tpt1
val vdef2 = assignType(cpy.ValDef(vdef)(name, tpt2, rhs1), sym)
postProcessInfo(vdef2, sym)
vdef2.setDefTree
}

private def setAbstractTrackedInfo(sym: Symbol, rhs: Tree, tpt: untpd.Tree)(using Context): Unit =
if !sym.flags.is(ParamAccessor) && !sym.flags.is(Param) then
if sym.allOverriddenSymbols.exists(_.flags.is(Tracked)) then
sym.setFlag(Tracked)
if sym.flags.is(Tracked) && tpt.isEmpty then
sym.info = rhs.tpe
KacperFKorban marked this conversation as resolved.
Show resolved Hide resolved

private def retractDefDef(sym: Symbol)(using Context): Tree =
// it's a discarded method (synthetic case class method or synthetic java record constructor or overridden member), drop it
val canBeInvalidated: Boolean =
Expand Down Expand Up @@ -3628,7 +3638,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
}

/** Typecheck and adapt tree, returning a typed tree. Parameters as for `typedUnadapted` */
def typed(tree: untpd.Tree, pt: Type, locked: TypeVars)(using Context): Tree =
def typed(tree: untpd.Tree, pt: Type, locked: TypeVars)(using Context): Tree = {
trace(i"typing $tree, pt = $pt", typr, show = true) {
record(s"typed $getClass")
record("typed total")
Expand All @@ -3640,6 +3650,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
tree.withType(WildcardType)
else adapt(typedUnadapted(tree, pt, locked), pt, locked)
}
}

def typed(tree: untpd.Tree, pt: Type = WildcardType)(using Context): Tree =
typed(tree, pt, ctx.typerState.ownedVars)
Expand Down Expand Up @@ -3755,7 +3766,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
def typedExpr(tree: untpd.Tree, pt: Type = WildcardType)(using Context): Tree =
withoutMode(Mode.PatternOrTypeBits)(typed(tree, pt))

def typedType(tree: untpd.Tree, pt: Type = WildcardType, mapPatternBounds: Boolean = false)(using Context): Tree =
def typedType(tree: untpd.Tree, pt: Type = WildcardType, mapPatternBounds: Boolean = false)(using Context): Tree = {
val tree1 = withMode(Mode.Type) { typed(tree, pt) }
if mapPatternBounds && ctx.mode.is(Mode.Pattern) && !ctx.isAfterTyper then
tree1 match
Expand All @@ -3771,6 +3782,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
case tree1 =>
tree1
else tree1
}

def typedPattern(tree: untpd.Tree, selType: Type = WildcardType)(using Context): Tree =
withMode(Mode.Pattern)(typed(tree, selType))
Expand Down
20 changes: 20 additions & 0 deletions tests/neg/abstract-tracked.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-- [E156] Syntax Error: tests/neg/abstract-tracked.scala:4:14 ----------------------------------------------------------
4 |tracked trait F // error
|^^^^^^^^^^^^^^^
|Modifier tracked is not allowed for this definition
-- [E156] Syntax Error: tests/neg/abstract-tracked.scala:9:15 ----------------------------------------------------------
9 |tracked object O // error
|^^^^^^^^^^^^^^^^
|Modifier tracked is not allowed for this definition
-- [E156] Syntax Error: tests/neg/abstract-tracked.scala:11:14 ---------------------------------------------------------
11 |tracked class C // error
|^^^^^^^^^^^^^^^
|Modifier tracked is not allowed for this definition
-- [E156] Syntax Error: tests/neg/abstract-tracked.scala:7:14 ----------------------------------------------------------
7 | tracked def f: F // error
| ^^^^^^^^^^^^^^^^
| Modifier tracked is not allowed for this definition
-- [E156] Syntax Error: tests/neg/abstract-tracked.scala:14:14 ---------------------------------------------------------
14 | tracked val x = 1 // error
| ^^^^^^^^^^^^^^^^^
| Modifier tracked is not allowed for this definition
14 changes: 14 additions & 0 deletions tests/neg/abstract-tracked.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import scala.language.experimental.modularity
import scala.language.future

tracked trait F // error

trait G:
tracked def f: F // error

tracked object O // error

tracked class C // error

def f =
tracked val x = 1 // error
50 changes: 16 additions & 34 deletions tests/neg/tracked.check
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,6 @@
7 | def foo(tracked a: Int) = // error
| ^
| ':' expected, but identifier found
-- Error: tests/neg/tracked.scala:8:12 ---------------------------------------------------------------------------------
8 | tracked val b: Int = 2 // error
| ^^^
| end of statement expected but 'val' found
-- Error: tests/neg/tracked.scala:11:10 --------------------------------------------------------------------------------
11 | tracked object Foo // error // error
| ^^^^^^
| end of statement expected but 'object' found
-- Error: tests/neg/tracked.scala:14:10 --------------------------------------------------------------------------------
14 | tracked class D // error // error
| ^^^^^
| end of statement expected but 'class' found
-- Error: tests/neg/tracked.scala:17:10 --------------------------------------------------------------------------------
17 | tracked type T = Int // error // error
| ^^^^
| end of statement expected but 'type' found
-- Error: tests/neg/tracked.scala:20:25 --------------------------------------------------------------------------------
20 | given g2: (tracked val x: Int) => C = C(x) // error
| ^^^^^^^^^^^^^^^^^^
Expand All @@ -30,21 +14,19 @@
4 |class C2(tracked var x: Int) // error
| ^
| mutable variables may not be `tracked`
-- [E006] Not Found Error: tests/neg/tracked.scala:11:2 ----------------------------------------------------------------
11 | tracked object Foo // error // error
| ^^^^^^^
| Not found: tracked
|
| longer explanation available when compiling with `-explain`
-- [E006] Not Found Error: tests/neg/tracked.scala:14:2 ----------------------------------------------------------------
14 | tracked class D // error // error
| ^^^^^^^
| Not found: tracked
|
| longer explanation available when compiling with `-explain`
-- [E006] Not Found Error: tests/neg/tracked.scala:17:2 ----------------------------------------------------------------
17 | tracked type T = Int // error // error
| ^^^^^^^
| Not found: tracked
|
| longer explanation available when compiling with `-explain`
-- [E156] Syntax Error: tests/neg/tracked.scala:8:16 -------------------------------------------------------------------
8 | tracked val b: Int = 2 // error
| ^^^^^^^^^^^^^^^^^^^^^^
| Modifier tracked is not allowed for this definition
-- [E156] Syntax Error: tests/neg/tracked.scala:11:17 ------------------------------------------------------------------
11 | tracked object Foo // error
| ^^^^^^^^^^^^^^^^^^
| Modifier tracked is not allowed for this definition
-- [E156] Syntax Error: tests/neg/tracked.scala:14:16 ------------------------------------------------------------------
14 | tracked class D // error
| ^^^^^^^^^^^^^^^
| Modifier tracked is not allowed for this definition
-- [E156] Syntax Error: tests/neg/tracked.scala:17:15 ------------------------------------------------------------------
17 | tracked type T = Int // error
| ^^^^^^^^^^^^^^^^^^^^
| Modifier tracked is not allowed for this definition
6 changes: 3 additions & 3 deletions tests/neg/tracked.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ object A:
tracked val b: Int = 2 // error

object B:
tracked object Foo // error // error
tracked object Foo // error

object C:
tracked class D // error // error
tracked class D // error

object D:
tracked type T = Int // error // error
tracked type T = Int // error

object E:
given g2: (tracked val x: Int) => C = C(x) // error
50 changes: 50 additions & 0 deletions tests/pos/abstract-tracked.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import scala.language.experimental.modularity
import scala.language.future

trait F:
tracked val a: Int

trait G:
tracked val b: Int

trait H:
tracked val c: Int = 3

trait I extends F

trait J extends F:
val a: Int = 1

class K(tracked val d: Int)

class L

trait M:
val f: Int

object Test:
val f = new F:
val a = 1
val g = new G:
val b: 2 = 2
val h = new H:
override val c = 4
val i = new I:
val a = 5
val j = new J:
override val a = 6
val k = new K(7)
val l = new L {
tracked val e = 8
}
val m = new M:
tracked val f = 9

summon[f.a.type <:< 1]
summon[g.b.type <:< 2]
summon[h.c.type <:< 4]
summon[i.a.type <:< 5]
summon[j.a.type <:< 6]
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]
Loading