Skip to content

Commit 09e05f0

Browse files
committed
Fix SubstBindings BiTypeMap logic
1. Also apply `SubstBindings` map to root annotations (previously it was only single `SubstBinding` maps). 2. Don't preserve BiTypeMaps for sets that are set up for the first time.
1 parent 56de8df commit 09e05f0

File tree

6 files changed

+74
-12
lines changed

6 files changed

+74
-12
lines changed

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

+21
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ class CCState:
120120

121121
private var capIsRoot: Boolean = false
122122

123+
/** If true, apply a BiTypeMap also to elements added to the set in the future
124+
* (and use its inverse when back-progating).
125+
*/
126+
private var mapFutureElems = true
127+
123128
var iterCount = 1
124129

125130
object CCState:
@@ -178,9 +183,25 @@ object CCState:
178183
try op finally ccs.capIsRoot = saved
179184
else op
180185

186+
/** Don't map future elements in this `op` */
187+
inline def withoutMappedFutureElems[T](op: => T)(using Context): T =
188+
val ccs = ccState
189+
val saved = ccs.mapFutureElems
190+
ccs.mapFutureElems = false
191+
try op finally ccs.mapFutureElems = saved
192+
181193
/** Is `caps.cap` a root capability that is allowed to subsume other capabilities? */
182194
def capIsRoot(using Context): Boolean = ccState.capIsRoot
183195

196+
/** When mapping a capture set with a BiTypeMap, should we create a BiMapped set
197+
* so that future elements can also be mapped, and elements added to the BiMapped
198+
* are back-propagated? Turned off when creating capture set variables for the
199+
* first time, since we then do not want to change the binder to the original type
200+
* without capture sets when back propagating. Error case where this shows:
201+
* pos-customargs/captures/lists.scala, method m2c.
202+
*/
203+
def mapFutureElems(using Context) = ccState.mapFutureElems
204+
184205
/** The currently opened existential scopes */
185206
def openExistentialScopes(using Context): List[MethodType] = ccState.openExistentialScopes
186207

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

+8-5
Original file line numberDiff line numberDiff line change
@@ -322,13 +322,14 @@ sealed abstract class CaptureSet extends Showable:
322322
if isConst then
323323
if mappedElems == elems then this
324324
else Const(mappedElems)
325-
else
325+
else if CCState.mapFutureElems then
326326
def unfused = BiMapped(asVar, tm, mappedElems)
327327
this match
328328
case self: BiMapped => self.bimap.fuse(tm) match
329329
case Some(fused: BiTypeMap) => BiMapped(self.source, fused, mappedElems)
330330
case _ => unfused
331331
case _ => unfused
332+
else this
332333
case tm: IdentityCaptRefMap =>
333334
this
334335
case tm: AvoidMap if this.isInstanceOf[HiddenSet] =>
@@ -732,11 +733,13 @@ object CaptureSet:
732733
* is not derived from some other variable.
733734
*/
734735
protected def ids(using Context): String =
736+
def descr = getClass.getSimpleName.nn.take(1)
735737
val trail = this.match
736-
case dv: DerivedVar => dv.source.ids
737-
case _ => ""
738-
val descr = getClass.getSimpleName.nn.take(1)
739-
s"$id$descr$trail"
738+
case dv: DerivedVar =>
739+
def summary = if ctx.settings.YccVerbose.value then dv.summarize else descr
740+
s"$summary${dv.source.ids}"
741+
case _ => descr
742+
s"$id$trail"
740743
override def toString = s"Var$id$elems"
741744
end Var
742745

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

+4-3
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,10 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
302302
apply(parent)
303303
case tp: TypeLambda =>
304304
// Don't recurse into parameter bounds, just cleanup any stray retains annotations
305-
tp.derivedLambdaType(
306-
paramInfos = tp.paramInfos.mapConserve(_.dropAllRetains.bounds),
307-
resType = this(tp.resType))
305+
withoutMappedFutureElems:
306+
tp.derivedLambdaType(
307+
paramInfos = tp.paramInfos.mapConserve(_.dropAllRetains.bounds),
308+
resType = this(tp.resType))
308309
case _ =>
309310
mapFollowingAliases(tp)
310311
addVar(

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

+11-1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ object root:
112112
case Kind.Result(binder) => tm match
113113
case tm: Substituters.SubstBindingMap[MethodType] @unchecked if tm.from eq binder =>
114114
derivedAnnotation(tm.to)
115+
case tm: Substituters.SubstBindingsMap =>
116+
var i = 0
117+
while i < tm.from.length && (tm.from(i) ne binder) do i += 1
118+
if i < tm.from.length then derivedAnnotation(tm.to(i).asInstanceOf[MethodType])
119+
else this
115120
case _ => this
116121
case _ => this
117122
end Annot
@@ -317,7 +322,12 @@ object root:
317322
case t: (LazyRef | TypeVar) =>
318323
mapConserveSuper(t)
319324
case _ =>
320-
if keepAliases then mapOver(t) else mapFollowingAliases(t)
325+
try
326+
if keepAliases then mapOver(t)
327+
else mapFollowingAliases(t)
328+
catch case ex: AssertionError =>
329+
println(i"error while mapping $t")
330+
throw ex
321331
end toResultInResults
322332

323333
/** If `refs` contains an occurrence of `cap` or `cap.rd`, the current context

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

+29
Original file line numberDiff line numberDiff line change
@@ -4219,6 +4219,35 @@ object Types extends TypeUtils {
42194219
}
42204220
mt
42214221
}
4222+
4223+
/** Not safe to use in general: Check that all references to an enclosing
4224+
* TermParamRef name point to that TermParamRef
4225+
*/
4226+
def checkValid2(mt: MethodType)(using Context): mt.type = {
4227+
var t = new TypeTraverser:
4228+
val ps = mt.paramNames.zip(mt.paramRefs).toMap
4229+
def traverse(t: Type) =
4230+
t match
4231+
case CapturingType(p, refs) =>
4232+
def checkRefs(refs: CaptureSet) =
4233+
for elem <- refs.elems do
4234+
elem match
4235+
case elem: TermParamRef =>
4236+
val elemName = elem.binder.paramNames(elem.paramNum)
4237+
//assert(elemName.toString != "f")
4238+
ps.get(elemName) match
4239+
case Some(elemRef) => assert(elemRef eq elem, i"bad $mt")
4240+
case _ =>
4241+
case root.Result(binder) if binder ne mt =>
4242+
assert(binder.paramNames.toList != mt.paramNames.toList, i"bad $mt")
4243+
case _ =>
4244+
checkRefs(refs)
4245+
traverse(p)
4246+
case _ =>
4247+
traverseChildren(t)
4248+
t.traverse(mt.resType)
4249+
mt
4250+
}
42224251
}
42234252

42244253
object MethodType extends MethodTypeCompanion("MethodType") {

tests/pos-custom-args/captures/lists.scala

+1-3
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ def test(c: Cap, d: Cap, e: Cap) =
4545
def m2 = [A, B] =>
4646
(f: A => B) => (xs: LIST[A]) => xs.map(f)
4747

48-
// def m2c: [A, B] -> (f: A => B) -> LIST[A] ->{f} LIST[B] = m2
49-
// !!! m2 has a bad type due to spurious level error. Need to follow up and
50-
// fix this.
48+
def m2c: [A, B] -> (f: A => B) -> LIST[A] ->{f} LIST[B] = m2
5149

5250
def eff[A](x: A) = if x == e then x else x
5351

0 commit comments

Comments
 (0)