Skip to content

Commit 3cf2e5c

Browse files
committed
Let implied class capabilities respect read-only
If a class has fresh fields and all fields are read-only, only imply `cap.rd`, not `cap`.
1 parent 81d56a5 commit 3cf2e5c

File tree

1 file changed

+33
-11
lines changed

1 file changed

+33
-11
lines changed

compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -939,17 +939,22 @@ class CheckCaptures extends Recheck, SymTransformer:
939939
.showing(i"constr type $mt with $argTypes%, % in $constr = $result", capt)
940940
end refineConstructorInstance
941941

942-
/** If `mbr` is a field that has (possibly restricted) FreshCaps in its span capture set,
943-
* their classifiers, otherwise the empty list.
944-
*/
945-
private def classifiersOfFreshInType(mbr: Symbol)(using Context): List[ClassSymbol] =
942+
private def memberCaps(mbr: Symbol)(using Context): List[Capability] =
946943
if contributesFreshToClass(mbr) then
947944
mbr.info.spanCaptureSet.elems
948945
.filter(_.isTerminalCapability)
949946
.toList
950-
.map(_.classifier.asClass)
951947
else Nil
952948

949+
/** If `mbr` is a field that has (possibly restricted) FreshCaps in its span capture set,
950+
* their classifiers, otherwise the empty list.
951+
*/
952+
private def classifiersOfFreshInType(mbr: Symbol)(using Context): List[ClassSymbol] =
953+
memberCaps(mbr).map(_.classifier.asClass)
954+
955+
private def allFreshInTypeAreRO(mbr: Symbol)(using Context): Boolean =
956+
memberCaps(mbr).forall(_.isReadOnly)
957+
953958
/** The additional capture set implied by the capture sets of its fields. This
954959
* is either empty or, if some fields have a terminal capability in their span
955960
* capture sets, it consists of a single fresh cap that subsumes all these terminal
@@ -962,14 +967,18 @@ class CheckCaptures extends Recheck, SymTransformer:
962967
def pushInfo(msg: => String) =
963968
if ctx.settings.YccVerbose.value then infos = msg :: infos
964969

970+
def knownFields(cls: ClassSymbol) =
971+
setup.fieldsWithExplicitTypes // pick fields with explicit types for classes in this compilation unit
972+
.getOrElse(cls, cls.info.decls.toList) // pick all symbols in class scope for other classes
973+
974+
val isSeparate = cls.typeRef.isMutableType
975+
965976
/** The classifiers of the fresh caps in the span capture sets of all fields
966977
* in the given class `cls`.
967978
*/
968979
def impliedClassifiers(cls: Symbol): List[ClassSymbol] = cls match
969980
case cls: ClassSymbol =>
970-
var fieldClassifiers = setup.fieldsWithExplicitTypes // pick fields with explicit types for classes in this compilation unit
971-
.getOrElse(cls, cls.info.decls.toList) // pick all symbols in class scope for other classes
972-
.flatMap(classifiersOfFreshInType)
981+
var fieldClassifiers = knownFields(cls).flatMap(classifiersOfFreshInType)
973982
if cls.typeRef.isMutableType then
974983
fieldClassifiers = cls.classifier :: fieldClassifiers
975984
val parentClassifiers =
@@ -979,19 +988,32 @@ class CheckCaptures extends Recheck, SymTransformer:
979988
else parentClassifiers.foldLeft(fieldClassifiers.distinct)(dominators)
980989
case _ => Nil
981990

991+
def impliedReadOnly(cls: Symbol): Boolean = cls match
992+
case cls: ClassSymbol =>
993+
val fieldsRO = knownFields(cls).forall(allFreshInTypeAreRO)
994+
val parentsRO = cls.parentSyms.forall(impliedReadOnly)
995+
fieldsRO && parentsRO
996+
case _ =>
997+
false
998+
999+
def maybeRO(ref: Capability) =
1000+
if !isSeparate && impliedReadOnly(cls) then ref.readOnly else ref
1001+
9821002
def fresh =
9831003
FreshCap(Origin.NewInstance(core)).tap: fresh =>
9841004
if ctx.settings.YccVerbose.value then
9851005
pushInfo(i"Note: instance of $cls captures a $fresh that comes from a field")
9861006
report.echo(infos.mkString("\n"), ctx.owner.srcPos)
9871007

988-
knownFresh.getOrElseUpdate(cls, impliedClassifiers(cls)) match
1008+
var implied = impliedClassifiers(cls)
1009+
if isSeparate then implied = cls.classifier :: implied
1010+
knownFresh.getOrElseUpdate(cls, implied) match
9891011
case Nil => CaptureSet.empty
9901012
case cl :: Nil =>
9911013
val result = fresh
9921014
result.hiddenSet.adoptClassifier(cl)
993-
result.singletonCaptureSet
994-
case _ => fresh.singletonCaptureSet
1015+
maybeRO(result).singletonCaptureSet
1016+
case _ => maybeRO(fresh).singletonCaptureSet
9951017
end captureSetImpliedByFields
9961018

9971019
/** Recheck type applications:

0 commit comments

Comments
 (0)