@@ -16,12 +16,12 @@ class SepChecker(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
16
16
import checker .*
17
17
18
18
extension (cs : CaptureSet )
19
- def footprint (using Context ): CaptureSet =
19
+ def footprint (using Context ): CaptureSet . Const =
20
20
def recur (elems : CaptureSet .Refs , newElems : List [CaptureRef ]): CaptureSet .Refs = newElems match
21
21
case newElem :: newElems1 =>
22
22
val superElems = newElem.captureSetOfInfo.elems.filter: superElem =>
23
23
! superElem.isMaxCapability && ! elems.contains(superElem)
24
- recur(superElems ++ elems, superElems.toList ++ newElems1 )
24
+ recur(elems ++ superElems, newElems1 ++ superElems.toList )
25
25
case Nil => elems
26
26
val elems : CaptureSet .Refs = cs.elems.filter(! _.isMaxCapability)
27
27
CaptureSet (recur(elems, elems.toList))
@@ -56,12 +56,23 @@ class SepChecker(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
56
56
recur(cs)
57
57
end hidden
58
58
59
- def captures (arg : Tree )(using Context ): CaptureSet =
59
+ /** The captures of an argument or prefix widened to the formal parameter, if
60
+ * the latter contains a cap.
61
+ */
62
+ def formalCaptures (arg : Tree )(using Context ): CaptureSet .Const =
60
63
val argType = arg.nuType
61
- if argType.hasUseAnnot then argType.deepCaptureSet else argType.captureSet
64
+ (if argType.hasUseAnnot then argType.deepCaptureSet else argType.captureSet)
65
+ .toConst
66
+
67
+ /** The captures of an argument of prefix. No widening takes place */
68
+ def actualCaptures (arg : Tree )(using Context ): CaptureSet .Const =
69
+ val argType = arg.actualType.orElse(arg.nuType)
70
+ (if arg.nuType.hasUseAnnot then argType.deepCaptureSet else argType.captureSet)
71
+ .toConst
62
72
63
73
private def sepError (fn : Tree , args : List [Tree ], argIdx : Int ,
64
- overlap : Refs , hiddenInArg : CaptureSet , footprints : List [(CaptureSet , Int )])(using Context ): Unit =
74
+ overlap : Refs , hiddenInArg : CaptureSet , footprints : List [(CaptureSet , Int )],
75
+ deps : collection.Map [Tree , List [Tree ]])(using Context ): Unit =
65
76
val arg = args(argIdx)
66
77
def paramName (mt : Type , idx : Int ): Option [Name ] = mt match
67
78
case mt @ MethodType (pnames) =>
@@ -73,11 +84,11 @@ class SepChecker(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
73
84
case _ => " "
74
85
def whatStr = if overlap.size == 1 then " this capability is" else " these capabilities are"
75
86
def funStr =
76
- if fn.symbol.exists then i " ${fn.symbol}"
77
- else " the function"
87
+ if fn.symbol.exists then i " ${fn.symbol}: ${fn.symbol.info} "
88
+ else i " a function of type ${fn.nuType.widen} "
78
89
val clashIdx = footprints
79
90
.collect:
80
- case (fp, idx) if ! hiddenInArg.footprint. overlapWith(fp).isEmpty => idx
91
+ case (fp, idx) if ! hiddenInArg.overlapWith(fp).isEmpty => idx
81
92
.head
82
93
def whereStr = clashIdx match
83
94
case 0 => " function prefix"
@@ -90,62 +101,98 @@ class SepChecker(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
90
101
else args(clashIdx - 1 )
91
102
def clashType =
92
103
clashTree.actualType.orElse(clashTree.nuType)
93
- def clashCaptures = captures (clashTree)
94
- def clashHidden = CaptureSet (hidden(clashCaptures ))
95
- def hiddenCaptures = CaptureSet (hidden(captures (arg)))
96
- def clashFootprint = clashCaptures.footprint ++ clashHidden.footprint
104
+ def clashCaptures = actualCaptures (clashTree)
105
+ def clashHidden = CaptureSet (hidden(formalCaptures(clashTree) ))
106
+ def hiddenCaptures = CaptureSet (hidden(formalCaptures (arg)))
107
+ def clashFootprint = clashCaptures.footprint
97
108
def hiddenFootprint = hiddenCaptures.footprint
109
+ def declaredFootprint = CaptureSet (deps(arg).map(actualCaptures(_).elems).foldLeft(emptySet)(_ ++ _)).footprint
110
+ def footprintOverlap = CaptureSet (hiddenFootprint.overlapWith(clashFootprint) -- declaredFootprint.elems)
98
111
def clashHiddenStr =
99
112
if clashTree.needsSepCheck then
100
- i " \n Hidden set of $whereStr : $clashHidden"
113
+ i " \n Hidden set of $whereStr : $clashHidden"
101
114
else " "
102
115
report.error(
103
- em """ Separation failure: argument of type ${arg.actualType} to capture-polymorphic parameter
104
- | ${formalName}of type ${arg.nuType} captures ${CaptureSet (overlap)}, and $whatStr also passed
105
- |separately in the ${whereStr.trim} with type $clashType to $funStr.
116
+ em """ Separation failure: argument of type ${arg.actualType}
117
+ |to $funStr
118
+ |corresponds to capture-polymorphic formal parameter ${formalName}of type ${arg.nuType}
119
+ |and captures ${CaptureSet (overlap)}, but $whatStr also passed separately
120
+ |in the ${whereStr.trim} with type $clashType.
106
121
|
107
- | Capture set of $whereStr : $clashCaptures$clashHiddenStr
108
- | Hidden set of current argument : $hiddenCaptures
109
- | Footprint of $whereStr : $clashFootprint
110
- | Hidden footprint of current argument: $hiddenFootprint
111
- | Overlap of footprints : ${CaptureSet (hiddenFootprint.overlapWith(clashFootprint))}""" ,
122
+ | Capture set of $whereStr : $clashCaptures
123
+ | Hidden set of current argument : $hiddenCaptures
124
+ | Footprint of $whereStr : $clashFootprint
125
+ | Hidden footprint of current argument : $hiddenFootprint
126
+ | Declared footprint of current argument: $declaredFootprint
127
+ | Undeclared overlap of footprints : $footprintOverlap""" ,
112
128
arg.srcPos)
113
129
end sepError
114
130
115
- private def checkApply (fn : Tree , args : List [Tree ])(using Context ): Unit =
116
- val fnCaptures =
117
- if true then
118
- fn.nuType.deepCaptureSet
119
- else methPart(fn) match
120
- case Select (qual, _) => qual.nuType.deepCaptureSet
121
- case _ => CaptureSet .empty
122
- val argCaptures = args.map(captures)
123
- capt.println(i " check separate $fn( $args), fnCaptures = $fnCaptures, argCaptures = $argCaptures" )
131
+ private def checkApply (fn : Tree , args : List [Tree ], deps : collection.Map [Tree , List [Tree ]])(using Context ): Unit =
132
+ val fnCaptures = methPart(fn) match
133
+ case Select (qual, _) => qual.nuType.captureSet
134
+ case _ => CaptureSet .empty
135
+ capt.println(i " check separate $fn( $args), fnCaptures = $fnCaptures, argCaptures = ${args.map(formalCaptures)}, deps = ${deps.toList}" )
124
136
var footprint = fnCaptures.footprint
125
137
val footprints = mutable.ListBuffer [(CaptureSet , Int )]((footprint, 0 ))
126
138
val indexedArgs = args.zipWithIndex
139
+
140
+ def subtractDeps (elems : Refs , arg : Tree ): Refs =
141
+ deps(arg).foldLeft(elems): (elems, dep) =>
142
+ elems -- actualCaptures(dep).footprint.elems
143
+
127
144
for (arg, idx) <- indexedArgs do
128
145
if ! arg.needsSepCheck then
129
- footprint = footprint ++ captures( arg)
146
+ footprint = footprint ++ CaptureSet (subtractDeps(actualCaptures( arg).footprint.elems, arg) )
130
147
footprints += ((footprint, idx + 1 ))
131
148
for (arg, idx) <- indexedArgs do
132
149
if arg.needsSepCheck then
133
- val ac = captures (arg)
150
+ val ac = formalCaptures (arg)
134
151
val hiddenInArg = CaptureSet (hidden(ac)).footprint
135
152
// println(i"check sep $arg: $ac, footprint so far = $footprint, hidden = $hiddenInArg")
136
- val overlap = hiddenInArg.footprint. overlapWith(footprint)
153
+ val overlap = subtractDeps( hiddenInArg.overlapWith(footprint), arg )
137
154
if ! overlap.isEmpty then
138
- sepError(fn, args, idx, overlap, hiddenInArg, footprints.toList)
139
- footprint = footprint ++ hiddenInArg.footprint ++ ac .footprint
155
+ sepError(fn, args, idx, overlap, hiddenInArg, footprints.toList, deps )
156
+ footprint ++= actualCaptures(arg) .footprint
140
157
footprints += ((footprint, idx + 1 ))
141
158
end checkApply
142
159
160
+ private def collectMethodTypes (tp : Type ): List [TermLambda ] = tp match
161
+ case tp : MethodType => tp :: collectMethodTypes(tp.resType)
162
+ case tp : PolyType => collectMethodTypes(tp.resType)
163
+ case _ => Nil
164
+
165
+ private def dependencies (fn : Tree , argss : List [List [Tree ]])(using Context ): collection.Map [Tree , List [Tree ]] =
166
+ val mtpe =
167
+ if fn.symbol.exists then fn.symbol.info
168
+ else fn.tpe.widen // happens for PolyFunction applies
169
+ val mtps = collectMethodTypes(mtpe)
170
+ assert(mtps.hasSameLengthAs(argss), i " diff for $fn: ${fn.symbol} /// $mtps /// $argss" )
171
+ val mtpsWithArgs = mtps.zip(argss)
172
+ val argMap = mtpsWithArgs.toMap
173
+ val deps = mutable.HashMap [Tree , List [Tree ]]().withDefaultValue(Nil )
174
+ for
175
+ (mt, args) <- mtpsWithArgs
176
+ (formal, arg) <- mt.paramInfos.zip(args)
177
+ dep <- formal.captureSet.elems.toList
178
+ do
179
+ val referred = dep match
180
+ case dep : TermParamRef =>
181
+ argMap(dep.binder)(dep.paramNum) :: Nil
182
+ case dep : ThisType if dep.cls == fn.symbol.owner =>
183
+ val Select (qual, _) = fn : @ unchecked
184
+ qual :: Nil
185
+ case _ =>
186
+ Nil
187
+ deps(arg) ++= referred
188
+ deps
189
+
143
190
private def traverseApply (tree : Tree , argss : List [List [Tree ]])(using Context ): Unit = tree match
144
191
case Apply (fn, args) => traverseApply(fn, args :: argss)
145
192
case TypeApply (fn, args) => traverseApply(fn, argss) // skip type arguments
146
193
case _ =>
147
194
if argss.nestedExists(_.needsSepCheck) then
148
- checkApply(tree, argss.flatten)
195
+ checkApply(tree, argss.flatten, dependencies(tree, argss) )
149
196
150
197
def traverse (tree : Tree )(using Context ): Unit =
151
198
tree match
0 commit comments