Skip to content

Commit de4e56f

Browse files
committed
Refine parameter accessors that have nonempty deep capture sets
A parameter accessor with a nonempty deep capture set needs to be tracked in refinements even if it is pure, as long as it might contain captures that can be referenced using a reach capability.
1 parent ec1e66d commit de4e56f

File tree

6 files changed

+17
-4
lines changed

6 files changed

+17
-4
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,9 @@ extension (sym: Symbol)
501501
&& !param.hasAnnotation(defn.UntrackedCapturesAnnot)
502502
}
503503

504+
def hasTrackedParts(using Context): Boolean =
505+
!CaptureSet.deepCaptureSet(sym.info).isAlwaysEmpty
506+
504507
extension (tp: AnnotatedType)
505508
/** Is this a boxed capturing type? */
506509
def isBoxed(using Context): Boolean = tp.annot match

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,7 @@ object CaptureSet:
10901090
recur(tp)
10911091
//.showing(i"capture set of $tp = $result", captDebug)
10921092

1093-
private def deepCaptureSet(tp: Type)(using Context): CaptureSet =
1093+
def deepCaptureSet(tp: Type)(using Context): CaptureSet =
10941094
val collect = new TypeAccumulator[CaptureSet]:
10951095
def apply(cs: CaptureSet, t: Type) = t.dealias match
10961096
case t @ CapturingType(p, cs1) =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ class CheckCaptures extends Recheck, SymTransformer:
554554
if core.derivesFromCapability then CaptureSet.universal else initCs
555555
for (getterName, argType) <- mt.paramNames.lazyZip(argTypes) do
556556
val getter = cls.info.member(getterName).suchThat(_.isRefiningParamAccessor).symbol
557-
if !getter.is(Private) && getter.termRef.isTracked then
557+
if !getter.is(Private) && getter.hasTrackedParts then
558558
refined = RefinedType(refined, getterName, argType)
559559
allCaptures ++= argType.captureSet
560560
(refined, allCaptures)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
186186
case cls: ClassSymbol
187187
if !defn.isFunctionClass(cls) && cls.is(CaptureChecked) =>
188188
cls.paramGetters.foldLeft(tp) { (core, getter) =>
189-
if atPhase(thisPhase.next)(getter.termRef.isTracked)
189+
if atPhase(thisPhase.next)(getter.hasTrackedParts)
190190
&& getter.isRefiningParamAccessor
191191
&& !getter.is(Tracked)
192192
then

compiler/src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,8 @@ object Symbols extends SymUtils {
846846
/** Map given symbols, subjecting their attributes to the mappings
847847
* defined in the given TreeTypeMap `ttmap`.
848848
* Cross symbol references are brought over from originals to copies.
849-
* Do not copy any symbols if all attributes of all symbols stay the same.
849+
* Do not copy any symbols if all attributes of all symbols stay the same
850+
* and mapAlways is false.
850851
*/
851852
def mapSymbols(originals: List[Symbol], ttmap: TreeTypeMap, mapAlways: Boolean = false)(using Context): List[Symbol] =
852853
if (originals.forall(sym =>

tests/pos/reach-problem.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import language.experimental.captureChecking
2+
3+
class Box[T](items: Seq[T^]):
4+
def getOne: T^{items*} = ???
5+
6+
object Box:
7+
def getOne[T](items: Seq[T^]): T^{items*} =
8+
val bx = Box(items)
9+
bx.getOne

0 commit comments

Comments
 (0)