Skip to content

Commit 468955b

Browse files
committed
Fix potential soundness hole when adding references to a mapped set
Fix soundness hole when adding references to a set that is the image of an idempotent `tm` map `tm`. If the element `ref` did not come from the source of the set, we still assumed that `tm(ref) = ref`, so that we simply added the reference to the set and also back-propagated it to source. But that is not necessarily the case (although it is the case in our complete test suite, so I am not sure this case can actually arise in practice. Nevertheless, it's better to not leave a potential soundness hole here. In the new implementation, we test whether `tm(ref) = ref`, and only proceed as before if that's the case. If not there are two sub-cases: - `{ref} <:< tm(ref)` and the variance of the set is positive. In that case we can soundly add `tm(ref)` to the set while back-propagating `ref` as before. - Otherwise there's nothing obvious left to do except fail (which is always sound.
1 parent 2283de3 commit 468955b

File tree

1 file changed

+37
-3
lines changed

1 file changed

+37
-3
lines changed

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

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,9 @@ object CaptureSet:
682682
// `r` is _one_ possible solution in `source` that would make an `r` appear in this set.
683683
// It's not necessarily the only possible solution, so the scheme is incomplete.
684684
source.tryInclude(elem, this)
685-
else if !mapIsIdempotent && variance <= 0 && !origin.isConst && (origin ne initial) && (origin ne source) then
685+
else if ccConfig.allowUnsoundMaps && !mapIsIdempotent
686+
&& variance <= 0 && !origin.isConst && (origin ne initial) && (origin ne source)
687+
then
686688
// The map is neither a BiTypeMap nor an idempotent type map.
687689
// In that case there's no much we can do.
688690
// The scheme then does not propagate added elements back to source and rejects adding
@@ -696,8 +698,11 @@ object CaptureSet:
696698
def propagateIf(cond: Boolean): CompareResult =
697699
if cond then propagate else CompareResult.OK
698700

699-
if origin eq source then // elements have to be mapped
700-
val mapped = extrapolateCaptureRef(elem, tm, variance)
701+
val mapped = extrapolateCaptureRef(elem, tm, variance)
702+
def isFixpoint =
703+
mapped.isConst && mapped.elems.size == 1 && mapped.elems.contains(elem)
704+
705+
def addMapped =
701706
val added = mapped.elems.filter(!accountsFor(_))
702707
addNewElems(added)
703708
.andAlso:
@@ -706,6 +711,35 @@ object CaptureSet:
706711
else CompareResult.Fail(this :: Nil)
707712
.andAlso:
708713
propagateIf(!added.isEmpty)
714+
715+
def failNoFixpoint =
716+
val reason =
717+
if variance <= 0 then i"the set's variance is $variance"
718+
else i"$elem gets mapped to $mapped, which is not a supercapture."
719+
report.warning(em"""trying to add $elem from unrecognized source $origin of mapped set $this$whereCreated
720+
|The reference cannot be added since $reason""")
721+
CompareResult.Fail(this :: Nil)
722+
723+
if origin eq source then // elements have to be mapped
724+
addMapped
725+
.andAlso:
726+
if mapped.isConst then CompareResult.OK
727+
else if mapped.asVar.recordDepsState() then { addAsDependentTo(mapped); CompareResult.OK }
728+
else CompareResult.Fail(this :: Nil)
729+
else if !isFixpoint then
730+
// We did not yet observe the !isFixpoint condition in our tests, but it's a
731+
// theoretical possibility. In that case, it would be inconsistent to both
732+
// add `elem` to the set and back-propagate it. But if `{elem} <:< tm(elem)`
733+
// and the variance of the set is positive, we can soundly add `tm(ref)` to
734+
// the set while back-propagating `ref` as before. Otherwise there's nothing
735+
// obvious left to do except fail (which is always sound).
736+
if variance > 0
737+
&& elem.singletonCaptureSet.subCaptures(mapped, frozen = true).isOK then
738+
// widen to fixpoint. mapped is known to be a fixpoint since tm is idempotent.
739+
// The widening is sound, but loses completeness.
740+
addMapped
741+
else
742+
failNoFixpoint
709743
else if accountsFor(elem) then
710744
CompareResult.OK
711745
else

0 commit comments

Comments
 (0)