@@ -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