Skip to content

Commit 30c78c1

Browse files
committed
Implement pickling/unpickling for dependent refinements
Needs an addition to Tasty format: TRACKED as a modifier.
1 parent a4656b1 commit 30c78c1

File tree

6 files changed

+69
-47
lines changed

6 files changed

+69
-47
lines changed

compiler/src/dotty/tools/dotc/core/NamerOps.scala

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package core
55
import Contexts.*, Symbols.*, Types.*, Flags.*, Scopes.*, Decorators.*, Names.*, NameOps.*
66
import SymDenotations.{LazyType, SymDenotation}, StdNames.nme
77
import TypeApplications.EtaExpansion
8+
import collection.mutable
89

910
/** Operations that are shared between Namer and TreeUnpickler */
1011
object NamerOps:
@@ -24,6 +25,47 @@ object NamerOps:
2425
resType = RefinedType(resType, param.name, param.termRef)
2526
resType
2627

28+
/** Split dependent class refinements off parent type and add them to `refinements` */
29+
extension (tp: Type)
30+
def separateRefinements(refinements: mutable.LinkedHashMap[Name, Type])(using Context): Type =
31+
tp match
32+
case RefinedType(tp1, rname, rinfo) =>
33+
try tp1.separateRefinements(refinements)
34+
finally
35+
refinements(rname) = refinements.get(rname) match
36+
case Some(tp) => tp & rinfo
37+
case None => rinfo
38+
case tp => tp
39+
40+
/** Add all parent `refinements` to the result type of the info of the dependent
41+
* class constructor `constr`. Parent refinements refer to parameter accessors
42+
* in the current class. These have to be mapped to the paramRefs of the
43+
* constructor info.
44+
*/
45+
def integrateParentRefinements(
46+
constr: Symbol, refinements: mutable.LinkedHashMap[Name, Type])(using Context): Unit =
47+
48+
/** @param info the (remaining part) of the constructor info
49+
* @param nameToParamRef the map from parameter names to paramRefs of
50+
* previously encountered parts of `info`.
51+
*/
52+
def recur(info: Type, nameToParamRef: mutable.Map[Name, Type]): Type = info match
53+
case info: MethodOrPoly =>
54+
info.derivedLambdaType(resType =
55+
recur(info.resType, nameToParamRef ++= info.paramNames.zip(info.paramRefs)))
56+
case _ =>
57+
val mapParams = new TypeMap:
58+
def apply(t: Type) = t match
59+
case t: TermRef if t.symbol.is(ParamAccessor) && t.symbol.owner == constr.owner =>
60+
nameToParamRef(t.name)
61+
case _ =>
62+
mapOver(t)
63+
refinements.foldLeft(info): (info, refinement) =>
64+
val (rname, rinfo) = refinement
65+
RefinedType(info, rname, mapParams(rinfo))
66+
constr.info = recur(constr.info, mutable.Map())
67+
end integrateParentRefinements
68+
2769
/** If isConstructor, make sure it has at least one non-implicit parameter list
2870
* This is done by adding a () in front of a leading old style implicit parameter,
2971
* or by adding a () as last -- or only -- parameter list if the constructor has

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,7 @@ class TreePickler(pickler: TastyPickler) {
776776
if (flags.is(Exported)) writeModTag(EXPORTED)
777777
if (flags.is(Given)) writeModTag(GIVEN)
778778
if (flags.is(Implicit)) writeModTag(IMPLICIT)
779+
if (flags.is(Tracked)) writeModTag(TRACKED)
779780
if (isTerm) {
780781
if (flags.is(Lazy, butNot = Module)) writeModTag(LAZY)
781782
if (flags.is(AbsOverride)) { writeModTag(ABSTRACT); writeModTag(OVERRIDE) }
@@ -787,7 +788,6 @@ class TreePickler(pickler: TastyPickler) {
787788
if (flags.is(Extension)) writeModTag(EXTENSION)
788789
if (flags.is(ParamAccessor)) writeModTag(PARAMsetter)
789790
if (flags.is(SuperParamAlias)) writeModTag(PARAMalias)
790-
if (flags.is(Tracked)) writeModTag(TRACKED)
791791
assert(!(flags.is(Label)))
792792
}
793793
else {

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,12 +1012,20 @@ class TreeUnpickler(reader: TastyReader,
10121012
* but skip constructor arguments. Return any trees that were partially
10131013
* parsed in this way as InferredTypeTrees.
10141014
*/
1015-
def readParents(withArgs: Boolean)(using Context): List[Tree] =
1015+
def readParents(cls: ClassSymbol, withArgs: Boolean)(using Context): List[Tree] =
10161016
collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) {
10171017
nextUnsharedTag match
10181018
case APPLY | TYPEAPPLY | BLOCK =>
1019-
if withArgs then readTree()
1020-
else InferredTypeTree().withType(readParentType())
1019+
if withArgs then
1020+
readTree()
1021+
else if cls.is(Dependent) then
1022+
val parentReader = fork
1023+
val parentCoreType = readParentType()
1024+
if parentCoreType.dealias.typeSymbol.is(Dependent)
1025+
then parentReader.readTree() // read the whole tree since we need to see the refinement
1026+
else InferredTypeTree().withType(parentCoreType)
1027+
else
1028+
InferredTypeTree().withType(readParentType())
10211029
case _ => readTpt()
10221030
}
10231031

@@ -1043,9 +1051,10 @@ class TreeUnpickler(reader: TastyReader,
10431051
while (bodyIndexer.reader.nextByte != DEFDEF) bodyIndexer.skipTree()
10441052
bodyIndexer.indexStats(end)
10451053
}
1046-
val parentReader = fork
1047-
val parents = readParents(withArgs = false)(using parentCtx)
1048-
val parentTypes = parents.map(_.tpe.dealias)
1054+
val parentsReader = fork
1055+
val parents = readParents(cls, withArgs = false)(using parentCtx)
1056+
val parentRefinements = mutable.LinkedHashMap[Name, Type]()
1057+
val parentTypes = parents.map(_.tpe.dealias.separateRefinements(parentRefinements))
10491058
val self =
10501059
if (nextByte == SELFDEF) {
10511060
readByte()
@@ -1058,11 +1067,13 @@ class TreeUnpickler(reader: TastyReader,
10581067
selfInfo = if (self.isEmpty) NoType else self.tpt.tpe
10591068
).integrateOpaqueMembers
10601069
val constr = readIndexedDef().asInstanceOf[DefDef]
1070+
if parentRefinements.nonEmpty then
1071+
integrateParentRefinements(constr.symbol, parentRefinements)
10611072
val mappedParents: LazyTreeList =
10621073
if parents.exists(_.isInstanceOf[InferredTypeTree]) then
10631074
// parents were not read fully, will need to be read again later on demand
1064-
new LazyReader(parentReader, localDummy, ctx.mode, ctx.source,
1065-
_.readParents(withArgs = true)
1075+
new LazyReader(parentsReader, localDummy, ctx.mode, ctx.source,
1076+
_.readParents(cls, withArgs = true)
10661077
.map(_.changeOwner(localDummy, constr.symbol)))
10671078
else parents
10681079

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1504,7 +1504,6 @@ class Namer { typer: Typer =>
15041504
if ptype.typeParams.isEmpty
15051505
//&& !ptype.dealias.typeSymbol.primaryConstructor.info.finalResultType.isInstanceOf[RefinedType]
15061506
&& !ptype.dealias.typeSymbol.is(Dependent)
1507-
|| ctx.erasedTypes
15081507
then
15091508
ptype
15101509
else
@@ -1613,46 +1612,12 @@ class Namer { typer: Typer =>
16131612
/** The refinements coming from all parent class constructor applications */
16141613
val parentRefinements = mutable.LinkedHashMap[Name, Type]()
16151614

1616-
/** Split refinements off parent type and add them to `parentRefinements` */
1617-
def separateRefinements(tp: Type): Type = tp match
1618-
case RefinedType(tp1, rname, rinfo) =>
1619-
try separateRefinements(tp1)
1620-
finally
1621-
parentRefinements(rname) = parentRefinements.get(rname) match
1622-
case Some(tp) => tp & rinfo
1623-
case None => rinfo
1624-
case tp => tp
1625-
1626-
/** Add all parent refinements to the result type of the `info` of
1627-
* the class constructor. Parent refinements refer to parameter accessors
1628-
* in the current class. These have to be mapped to the paramRefs of the
1629-
* constructor info.
1630-
* @param info The (remaining part) of the constructor info
1631-
* @param nameToParamRef The map from parameter names to paramRefs of
1632-
* previously encountered parts of `info`.
1633-
*/
1634-
def integrateParentRefinements(info: Type, nameToParamRef: Map[Name, Type]): Type = info match
1635-
case info: MethodOrPoly =>
1636-
info.derivedLambdaType(resType =
1637-
integrateParentRefinements(info.resType,
1638-
nameToParamRef ++ info.paramNames.zip(info.paramRefs)))
1639-
case _ =>
1640-
val mapParams = new TypeMap:
1641-
def apply(t: Type) = t match
1642-
case t: TermRef if t.symbol.is(ParamAccessor) && t.symbol.owner == cls =>
1643-
nameToParamRef(t.name)
1644-
case _ =>
1645-
mapOver(t)
1646-
parentRefinements.foldLeft(info): (info, refinement) =>
1647-
val (rname, rinfo) = refinement
1648-
RefinedType(info, rname, mapParams(rinfo))
1649-
16501615
val parentTypes =
16511616
defn.adjustForTuple(cls, cls.typeParams,
16521617
defn.adjustForBoxedUnit(cls,
16531618
addUsingTraits(
16541619
ensureFirstIsClass(cls, parents.map(checkedParentType(_)))
1655-
))).map(separateRefinements)
1620+
))).map(_.separateRefinements(parentRefinements))
16561621

16571622
typr.println(i"completing $denot, parents = $parents%, %, stripped parent types = $parentTypes%, %")
16581623
typr.println(i"constr type = ${cls.primaryConstructor.infoOrCompleter}, refinements = ${parentRefinements.toList}")
@@ -1671,8 +1636,7 @@ class Namer { typer: Typer =>
16711636
tempInfo = null // The temporary info can now be garbage-collected
16721637

16731638
if parentRefinements.nonEmpty then
1674-
val constr = cls.primaryConstructor
1675-
constr.info = integrateParentRefinements(constr.info, Map())
1639+
integrateParentRefinements(cls.primaryConstructor, parentRefinements)
16761640
cls.setFlag(Dependent)
16771641
Checking.checkWellFormed(cls)
16781642
if (isDerivedValueClass(cls)) cls.setFlag(Final)

tasty/src/dotty/tools/tasty/TastyFormat.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ Standard-Section: "ASTs" TopLevelStat*
219219
EXPORTED -- An export forwarder
220220
OPEN -- an open class
221221
INVISIBLE -- invisible during typechecking
222+
TRACKED -- a tracked class parameter / a dependent class
222223
Annotation
223224
224225
Variance = STABLE -- invariant

tests/neg/i3964.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ object Test1:
1010
trait Foo { val x: Animal }
1111
val foo: Foo { val x: Cat } = new Foo { val x = new Cat } // error, but should work
1212

13+
object Test3:
14+
trait Vec(tracked val size: Int)
15+
class Vec8 extends Vec(8):
16+
val s: 8 = size // error, but should work

0 commit comments

Comments
 (0)