@@ -17,6 +17,7 @@ import typer.Checking.{checkBounds, checkAppliedTypesIn}
1717import typer .ErrorReporting .{Addenda , NothingToAdd , err }
1818import typer .ProtoTypes .{LhsProto , WildcardSelectionProto , SelectionProto }
1919import util .{SimpleIdentitySet , EqHashMap , EqHashSet , SrcPos , Property }
20+ import util .chaining .tap
2021import transform .{Recheck , PreRecheck , CapturedVars }
2122import Recheck .*
2223import scala .collection .mutable
@@ -247,6 +248,11 @@ class CheckCaptures extends Recheck, SymTransformer:
247248
248249 val ccState1 = new CCState // Dotty problem: Rename to ccState ==> Crash in ExplicitOuter
249250
251+ /** A cache that stores for each class the classifiers of all fresh instances
252+ * in the types of its fields.
253+ */
254+ val knownFresh = new util.EqHashMap [Symbol , List [ClassSymbol ]]
255+
250256 class CaptureChecker (ictx : Context ) extends Rechecker (ictx), CheckerAPI :
251257
252258 // println(i"checking ${ictx.source}"(using ictx))
@@ -620,9 +626,7 @@ class CheckCaptures extends Recheck, SymTransformer:
620626 fn.tpe.widenDealias match
621627 case tl : TypeLambda => tl.paramNames
622628 case ref : AppliedType if ref.typeSymbol.isClass => ref.typeSymbol.typeParams.map(_.name)
623- case t =>
624- println(i " parent type: $t" )
625- args.map(_ => EmptyTypeName )
629+ case t => args.map(_ => EmptyTypeName )
626630
627631 for case (arg : TypeTree , pname) <- args.lazyZip(paramNames) do
628632 def where = if sym.exists then i " in an argument of $sym" else " "
@@ -870,7 +874,7 @@ class CheckCaptures extends Recheck, SymTransformer:
870874 var allCaptures : CaptureSet =
871875 if core.derivesFromCapability
872876 then initCs ++ FreshCap (Origin .NewCapability (core)).singletonCaptureSet
873- else initCs
877+ else initCs ++ impliedByFields(core)
874878 for (getterName, argType) <- mt.paramNames.lazyZip(argTypes) do
875879 val getter = cls.info.member(getterName).suchThat(_.isRefiningParamAccessor).symbol
876880 if ! getter.is(Private ) && getter.hasTrackedParts then
@@ -894,20 +898,53 @@ class CheckCaptures extends Recheck, SymTransformer:
894898 val (refined, cs) = addParamArgRefinements(core, initCs)
895899 refined.capturing(cs)
896900
897- /*
898- def impliedFresh: CaptureSet =
899- cls.info.fields.foldLeft(CaptureSet.empty: CaptureSet): (cs, field) =>
900- if !cs.isAlwaysEmpty || field.symbol.is(ParamAccessor) then
901- cs
902- else
903- val fieldFreshCaps = field.info.spanCaptureSet.elems.filter(_.isTerminalCapability)
904- if fieldFreshCaps.isEmpty then cs
905- else
906- val classFresh = FreshCap(ctx.owner, NoPrefix, )
907- if fieldFreshCaps.forall(_.isReadOnly)
908- then cs + classFresh.readOnly
909- else cs + classFresh
910- */
901+ /** The additional capture set implied by the capture sets of its fields. This
902+ * is either empty or, if some fields have a terminal capability in their span
903+ * capture sets, it consists of a single fresh cap that subsumes all these terminal
904+ * capabiltities. Class parameters are not counted.
905+ */
906+ def impliedByFields (core : Type ): CaptureSet =
907+ var infos : List [String ] = Nil
908+ def pushInfo (msg : => String ) =
909+ if ctx.settings.YccVerbose .value then infos = msg :: infos
910+
911+ /** The classifiers of the fresh caps in the span capture sets of all fields
912+ * in the given class `cls`.
913+ */
914+ def impliedClassifiers (cls : Symbol ): List [ClassSymbol ] = cls match
915+ case cls : ClassSymbol =>
916+ val fieldClassifiers =
917+ for
918+ sym <- cls.info.decls.toList
919+ if sym.isField && ! sym.isOneOf(DeferredOrTermParamOrAccessor )
920+ && ! sym.hasAnnotation(defn.UntrackedCapturesAnnot )
921+ case fresh : FreshCap <- sym.info.spanCaptureSet.elems
922+ .filter(_.isTerminalCapability)
923+ .map(_.stripReadOnly)
924+ .toList
925+ _ = pushInfo(i " Note: ${sym.showLocated} captures a $fresh" )
926+ yield fresh.hiddenSet.classifier
927+ val parentClassifiers =
928+ cls.parentSyms.map(impliedClassifiers).filter(_.nonEmpty)
929+ if fieldClassifiers.isEmpty && parentClassifiers.isEmpty
930+ then Nil
931+ else parentClassifiers.foldLeft(fieldClassifiers.distinct)(dominators)
932+ case _ => Nil
933+
934+ def fresh =
935+ FreshCap (Origin .NewInstance (core)).tap: fresh =>
936+ if ctx.settings.YccVerbose .value then
937+ pushInfo(i " Note: instance of $cls captures a $fresh that comes from a field " )
938+ report.echo(infos.mkString(" \n " ), ctx.owner.srcPos)
939+
940+ knownFresh.getOrElseUpdate(cls, impliedClassifiers(cls)) match
941+ case Nil => CaptureSet .empty
942+ case cl :: Nil =>
943+ val result = fresh
944+ result.hiddenSet.adoptClassifier(cl)
945+ result.singletonCaptureSet
946+ case _ => fresh.singletonCaptureSet
947+ end impliedByFields
911948
912949 augmentConstructorType(resType, capturedVars(cls))
913950 .showing(i " constr type $mt with $argTypes%, % in $constr = $result" , capt)
0 commit comments