@@ -17,6 +17,7 @@ import typer.Checking.{checkBounds, checkAppliedTypesIn}
17
17
import typer .ErrorReporting .{Addenda , NothingToAdd , err }
18
18
import typer .ProtoTypes .{LhsProto , WildcardSelectionProto , SelectionProto }
19
19
import util .{SimpleIdentitySet , EqHashMap , EqHashSet , SrcPos , Property }
20
+ import util .chaining .tap
20
21
import transform .{Recheck , PreRecheck , CapturedVars }
21
22
import Recheck .*
22
23
import scala .collection .mutable
@@ -247,6 +248,11 @@ class CheckCaptures extends Recheck, SymTransformer:
247
248
248
249
val ccState1 = new CCState // Dotty problem: Rename to ccState ==> Crash in ExplicitOuter
249
250
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
+
250
256
class CaptureChecker (ictx : Context ) extends Rechecker (ictx), CheckerAPI :
251
257
252
258
// println(i"checking ${ictx.source}"(using ictx))
@@ -620,9 +626,7 @@ class CheckCaptures extends Recheck, SymTransformer:
620
626
fn.tpe.widenDealias match
621
627
case tl : TypeLambda => tl.paramNames
622
628
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 )
626
630
627
631
for case (arg : TypeTree , pname) <- args.lazyZip(paramNames) do
628
632
def where = if sym.exists then i " in an argument of $sym" else " "
@@ -870,7 +874,7 @@ class CheckCaptures extends Recheck, SymTransformer:
870
874
var allCaptures : CaptureSet =
871
875
if core.derivesFromCapability
872
876
then initCs ++ FreshCap (Origin .NewCapability (core)).singletonCaptureSet
873
- else initCs
877
+ else initCs ++ impliedByFields(core)
874
878
for (getterName, argType) <- mt.paramNames.lazyZip(argTypes) do
875
879
val getter = cls.info.member(getterName).suchThat(_.isRefiningParamAccessor).symbol
876
880
if ! getter.is(Private ) && getter.hasTrackedParts then
@@ -894,20 +898,53 @@ class CheckCaptures extends Recheck, SymTransformer:
894
898
val (refined, cs) = addParamArgRefinements(core, initCs)
895
899
refined.capturing(cs)
896
900
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
911
948
912
949
augmentConstructorType(resType, capturedVars(cls))
913
950
.showing(i " constr type $mt with $argTypes%, % in $constr = $result" , capt)
0 commit comments