@@ -40,6 +40,29 @@ trait SetupAPI:
4040 /** Check to do after the capture checking traversal */
4141 def postCheck ()(using Context ): Unit
4242
43+ /** A map from currently compiled class symbols to those of their fields
44+ * that have an explicit type given. Used in `captureSetImpliedByFields`
45+ * to avoid forcing fields with inferred types prematurely. The test file
46+ * where this matters is i24335.scala. The precise failure scenario which
47+ * this avoids is described in #24335.
48+ */
49+ def fieldsWithExplicitTypes : collection.Map [ClassSymbol , List [Symbol ]]
50+
51+ /** Used for error reporting:
52+ * Maps mutable variables to the symbols that capture them (in the
53+ * CheckCaptures sense, i.e. symbol is referred to from a different method
54+ * than the one it is defined in).
55+ */
56+ def capturedBy : collection.Map [Symbol , Symbol ]
57+
58+ /** Used for error reporting:
59+ * Maps anonymous functions appearing as function arguments to
60+ * the function that is called.
61+ */
62+ def anonFunCallee : collection.Map [Symbol , Symbol ]
63+
64+ end SetupAPI
65+
4366object Setup :
4467
4568 val name : String = " setupCC"
@@ -475,6 +498,12 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
475498 extension (sym : Symbol ) def nextInfo (using Context ): Type =
476499 atPhase(thisPhase.next)(sym.info)
477500
501+ val fieldsWithExplicitTypes : mutable.HashMap [ClassSymbol , List [Symbol ]] = mutable.HashMap ()
502+
503+ val capturedBy : mutable.HashMap [Symbol , Symbol ] = mutable.HashMap ()
504+
505+ val anonFunCallee : mutable.HashMap [Symbol , Symbol ] = mutable.HashMap ()
506+
478507 /** A traverser that adds knownTypes and updates symbol infos */
479508 def setupTraverser (checker : CheckerAPI ) = new TreeTraverserWithPreciseImportContexts :
480509 import checker .*
@@ -664,59 +693,65 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
664693 case tree : Bind =>
665694 val sym = tree.symbol
666695 updateInfo(sym, transformInferredType(sym.info), sym.owner)
667- case tree : TypeDef =>
668- tree.symbol match
669- case cls : ClassSymbol =>
670- checkClassifiedInheritance(cls)
671- val cinfo @ ClassInfo (prefix, _, ps, decls, selfInfo) = cls.classInfo
672-
673- // Compute new self type
674- def isInnerModule = cls.is(ModuleClass ) && ! cls.isStatic
675- val selfInfo1 =
676- if (selfInfo ne NoType ) && ! isInnerModule then
677- // if selfInfo is explicitly given then use that one, except if
678- // self info applies to non-static modules, these still need to be inferred
679- selfInfo
680- else if cls.isPureClass then
681- // is cls is known to be pure, nothing needs to be added to self type
682- selfInfo
683- else if ! cls.isEffectivelySealed && ! cls.baseClassHasExplicitNonUniversalSelfType then
684- // assume {cap} for completely unconstrained self types of publicly extensible classes
685- CapturingType (cinfo.selfType, CaptureSet .universal)
686- else
687- // Infer the self type for the rest, which is all classes without explicit
688- // self types (to which we also add nested module classes), provided they are
689- // neither pure, nor are publicily extensible with an unconstrained self type.
690- val cs = CaptureSet .ProperVar (cls, CaptureSet .emptyRefs, nestedOK = false , isRefining = false )
691- if cls.derivesFrom(defn.Caps_Capability ) then
692- // If cls is a capability class, we need to add a fresh capability to ensure
693- // we cannot treat the class as pure.
694- CaptureSet .fresh(cls, cls.thisType, Origin .InDecl (cls)).subCaptures(cs)
695- CapturingType (cinfo.selfType, cs)
696-
697- // Compute new parent types
698- val ps1 = inContext(ctx.withOwner(cls)):
699- ps.mapConserve(transformExplicitType(_, NoSymbol , freshen = false ))
700-
701- // Install new types and if it is a module class also update module object
702- if (selfInfo1 ne selfInfo) || (ps1 ne ps) then
703- val newInfo = ClassInfo (prefix, cls, ps1, decls, selfInfo1)
704- updateInfo(cls, newInfo, cls.owner)
705- capt.println(i " update class info of $cls with parents $ps selfinfo $selfInfo to $newInfo" )
706- cls.thisType.asInstanceOf [ThisType ].invalidateCaches()
707- if cls.is(ModuleClass ) then
708- // if it's a module, the capture set of the module reference is the capture set of the self type
709- val modul = cls.sourceModule
710- val selfCaptures = selfInfo1 match
711- case CapturingType (_, refs) => refs
712- case _ => CaptureSet .empty
713- // Note: Can't do val selfCaptures = selfInfo1.captureSet here.
714- // This would potentially give stackoverflows when setup is run repeatedly.
715- // One test case is pos-custom-args/captures/checkbounds.scala under
716- // ccConfig.alwaysRepeatRun = true.
717- updateInfo(modul, CapturingType (modul.info, selfCaptures), modul.owner)
718- modul.termRef.invalidateCaches()
719- case _ =>
696+ case tree @ TypeDef (_, impl : Template ) =>
697+ val cls : ClassSymbol = tree.symbol.asClass
698+
699+ fieldsWithExplicitTypes(cls) =
700+ for
701+ case vd @ ValDef (_, tpt : TypeTree , _) <- impl.body
702+ if ! tpt.isInferred && vd.symbol.exists && ! vd.symbol.is(NonMember )
703+ yield
704+ vd.symbol
705+
706+ checkClassifiedInheritance(cls)
707+ val cinfo @ ClassInfo (prefix, _, ps, decls, selfInfo) = cls.classInfo
708+
709+ // Compute new self type
710+ def isInnerModule = cls.is(ModuleClass ) && ! cls.isStatic
711+ val selfInfo1 =
712+ if (selfInfo ne NoType ) && ! isInnerModule then
713+ // if selfInfo is explicitly given then use that one, except if
714+ // self info applies to non-static modules, these still need to be inferred
715+ selfInfo
716+ else if cls.isPureClass then
717+ // is cls is known to be pure, nothing needs to be added to self type
718+ selfInfo
719+ else if ! cls.isEffectivelySealed && ! cls.baseClassHasExplicitNonUniversalSelfType then
720+ // assume {cap} for completely unconstrained self types of publicly extensible classes
721+ CapturingType (cinfo.selfType, CaptureSet .universal)
722+ else
723+ // Infer the self type for the rest, which is all classes without explicit
724+ // self types (to which we also add nested module classes), provided they are
725+ // neither pure, nor are publicily extensible with an unconstrained self type.
726+ val cs = CaptureSet .ProperVar (cls, CaptureSet .emptyRefs, nestedOK = false , isRefining = false )
727+ if cls.derivesFrom(defn.Caps_Capability ) then
728+ // If cls is a capability class, we need to add a fresh capability to ensure
729+ // we cannot treat the class as pure.
730+ CaptureSet .fresh(cls, cls.thisType, Origin .InDecl (cls)).subCaptures(cs)
731+ CapturingType (cinfo.selfType, cs)
732+
733+ // Compute new parent types
734+ val ps1 = inContext(ctx.withOwner(cls)):
735+ ps.mapConserve(transformExplicitType(_, NoSymbol , freshen = false ))
736+
737+ // Install new types and if it is a module class also update module object
738+ if (selfInfo1 ne selfInfo) || (ps1 ne ps) then
739+ val newInfo = ClassInfo (prefix, cls, ps1, decls, selfInfo1)
740+ updateInfo(cls, newInfo, cls.owner)
741+ capt.println(i " update class info of $cls with parents $ps selfinfo $selfInfo to $newInfo" )
742+ cls.thisType.asInstanceOf [ThisType ].invalidateCaches()
743+ if cls.is(ModuleClass ) then
744+ // if it's a module, the capture set of the module reference is the capture set of the self type
745+ val modul = cls.sourceModule
746+ val selfCaptures = selfInfo1 match
747+ case CapturingType (_, refs) => refs
748+ case _ => CaptureSet .empty
749+ // Note: Can't do val selfCaptures = selfInfo1.captureSet here.
750+ // This would potentially give stackoverflows when setup is run repeatedly.
751+ // One test case is pos-custom-args/captures/checkbounds.scala under
752+ // ccConfig.alwaysRepeatRun = true.
753+ updateInfo(modul, CapturingType (modul.info, selfCaptures), modul.owner)
754+ modul.termRef.invalidateCaches()
720755 case _ =>
721756 end postProcess
722757
0 commit comments