Maintain invariant that justifications of a MatchJournal.Chunk are inherited by all its children Chunks

Retroactive addition of justifications to a parent Chunk (MatchChunk.justifyBy),
introduced by a recent fix for MPSCR-47, can broke this invariant.
This commit is contained in:
Grigorii Kirgizov 2020-03-02 16:59:17 +03:00
parent 81721a0067
commit 2ec4abe860
4 changed files with 22 additions and 3 deletions

View File

@ -66,6 +66,11 @@ interface Justified {
*/
fun justifiedByAny(others: Collection<Justified>): Boolean =
others.any { this.justifiedBy(it) }
/**
* Append [Justifications] from [other] entity to justifications of this [Justified]
*/
fun justifyBy(other: Justified): Unit { justifications().addAll(other.justifications()) }
}

View File

@ -165,7 +165,9 @@ internal class ControllerImpl (
// then they must be tracked somewhere above --- i.e. in its parent
match.allHeads().filter { it.isPrincipal() && !it.justifiedBy(parent) }.forEach {
// Avoid justifying parent by its child!
parent.justifyBy(it)
processing.forEachChunkFrom(parent.toPos()) { child ->
child.justifyBy(it)
}
}
}
@ -239,6 +241,7 @@ internal class ControllerImpl (
profiler.profile<FeedbackStatus>("activate_${constraint.symbol()}") {
// fixme: maybe provide no evidence for non-principal constraints?
constraint.occurrence(logicalStateObservable(), args, processing.nextEvidence(), justifications, context.logicalContext, context.ruleUniqueTag).let { occ ->
trace.activate(occ)
processing.processActivated(this, occ, parent, status)

View File

@ -88,6 +88,13 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk>, EvidenceSource {
*/
fun replay(observable: LogicalStateObservable, futurePos: Pos)
/**
* Walk from specified [from] position in journal until current position
* while applying [action] to each visited [Chunk].
* Occurrences are accordingly reset and replayed
*/
fun forEachChunkFrom(from: Pos, action: (Chunk) -> Unit)
/**
* Returns snapshot of the journal.
*/
@ -167,8 +174,6 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk>, EvidenceSource {
class MatchChunk(override val evidence: Evidence, val match: RuleMatch) : Chunk {
private val justifications = match.justifications().apply { add(evidence) }
fun justifyBy(other: Justified): Unit { justifications.addAll(other.justifications()) }
override fun justifications(): Justifications = justifications
override var entries: MutableList<Chunk.Entry> = mutableListOf()

View File

@ -148,6 +148,12 @@ internal open class MatchJournalImpl(
if (currentPos() != pastPos) throw IllegalStateException()
}
override fun forEachChunkFrom(from: Pos, action: (Chunk) -> Unit) {
val to = currentPos()
resetPos(from, false)
replayWith(to, action)
}
override fun replay(observable: LogicalStateObservable, futurePos: MatchJournal.Pos) = replayWith(futurePos, {})
private fun replayWith(futurePos: MatchJournal.Pos, action: (Chunk) -> Unit) {