Properly handle non-principal rules with origins in incremental algo
Fixes IncrClassHierarchy.modifyClsHierarchyTypeParams test. Key point is that changes to rules causing their regeneration must be reflected in program. There appeared to be cases when it's not true. It's a case of non-principal rules with origins. Such rules are called "weak principal" rules. Matches of weak principal rules are not recorded in Journal (as is for principal rules), but instead they become part of their parent MatchChunk. (In essense, tags of weak principal rules are recorded in parent chunk). With this, when a weak principal rule changes, matches of all principal rules whose computation depends on them are correctly invalidated. (Example rules: findHierarchy_Classifier) Relevant for MPSCR-62.
This commit is contained in:
parent
a37bd44de0
commit
8629e5abfc
|
|
@ -86,7 +86,7 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di
|
|||
while (it.hasNext()) {
|
||||
val chunk = it.next()
|
||||
|
||||
if (chunk is MatchJournal.MatchChunk && ruleIds.contains(chunk.ruleUniqueTag)) {
|
||||
if (chunk is MatchJournal.MatchChunk && chunk.dependsOnAny(ruleIds)) {
|
||||
justificationRoots.add(chunk)
|
||||
}
|
||||
|
||||
|
|
@ -341,4 +341,8 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di
|
|||
private fun RuleMatch.isPrincipal() = ispec.isPrincipal(this.rule())
|
||||
|
||||
private fun Occurrence.isPrincipal() = ispec.isPrincipal(this.constraint())
|
||||
|
||||
private fun MatchJournal.MatchChunk.dependsOnAny(utags: Iterable<Any>): Boolean =
|
||||
utags.contains(this.ruleUniqueTag) || utags.any { utag -> dependsOnRule(utag) }
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,7 +189,17 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk>, EvidenceSource {
|
|||
* [Chunk] corresponding to a [RuleMatch] of a principal [Rule].
|
||||
*/
|
||||
interface MatchChunk : Chunk {
|
||||
/**
|
||||
* Returns true if this [Chunk] depends on changes to specified rule.
|
||||
* Relevant for rules with origin. Doesn't include rule from [match].
|
||||
*/
|
||||
fun dependsOnRule(utag: Any): Boolean
|
||||
|
||||
/**
|
||||
* [RuleMatch] which defines this [Chunk]
|
||||
*/
|
||||
val match: RuleMatch
|
||||
|
||||
val ruleUniqueTag: Any get() = match.rule().uniqueTag()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,10 +40,14 @@ internal open class MatchJournalImpl(
|
|||
}
|
||||
|
||||
private class MatchChunkImpl(override val evidence: Evidence, override val match: RuleMatch) : ChunkImpl(), MatchChunk {
|
||||
override fun dependsOnRule(utag: Any): Boolean = rulesWithOrigin.contains(utag)
|
||||
|
||||
override fun justifications(): Justifications = justifications
|
||||
|
||||
private val justifications = match.collectJustifications(evidence)
|
||||
|
||||
val rulesWithOrigin = HashSet<Any>(4)
|
||||
|
||||
override fun toString() = "(id=$evidence, ${justifications()}, ${match.rule().tag()}, $entries)"
|
||||
}
|
||||
|
||||
|
|
@ -132,7 +136,7 @@ internal open class MatchJournalImpl(
|
|||
}
|
||||
|
||||
private fun logJustificationsFrom(match: RuleMatch) {
|
||||
val parent: Chunk = parentChunk()
|
||||
val parent: MatchChunk = parentChunk()
|
||||
|
||||
val moreJustified = match.allHeads().filter {
|
||||
// Filter to avoid justifying parent by its child!
|
||||
|
|
@ -144,6 +148,12 @@ internal open class MatchJournalImpl(
|
|||
child.justifyByAll(moreJustified)
|
||||
}
|
||||
}
|
||||
|
||||
match.rule().let {
|
||||
if (it.isWeakPrincipal()) {
|
||||
(parent as MatchChunkImpl).rulesWithOrigin.add(it.uniqueTag())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun logActivation(occ: Occurrence): OccChunk? {
|
||||
|
|
@ -352,6 +362,7 @@ internal open class MatchJournalImpl(
|
|||
}
|
||||
|
||||
private fun RuleMatch.isPrincipal() = ispec.isPrincipal(this.rule())
|
||||
private fun Rule.isWeakPrincipal() = ispec.isWeakPrincipal(this)
|
||||
private fun Occurrence.isPrincipal() = ispec.isPrincipal(this.constraint())
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20,12 +20,15 @@ public interface IncrementalProgramSpec {
|
|||
|
||||
boolean isPrincipal(Constraint ctr);
|
||||
boolean isPrincipal(Rule rule);
|
||||
boolean isWeakPrincipal(Rule rule);
|
||||
|
||||
class NonIncrSpec implements IncrementalProgramSpec {
|
||||
@Override
|
||||
public boolean isPrincipal(Constraint ctr) { return false; }
|
||||
@Override
|
||||
public boolean isPrincipal(Rule rule) { return false; }
|
||||
@Override
|
||||
public boolean isWeakPrincipal(Rule rule) { return false; }
|
||||
}
|
||||
|
||||
NonIncrSpec DefaultSpec = new NonIncrSpec();
|
||||
|
|
|
|||
|
|
@ -22,4 +22,5 @@ import jetbrains.mps.logic.reactor.program.Rule
|
|||
class MockIncrProgSpec(val principalRuleTags: Set<Any>, val principalCtrSyms: Set<ConstraintSymbol>) : IncrementalProgramSpec {
|
||||
override fun isPrincipal(ctr: Constraint): Boolean = principalCtrSyms.contains(ctr.symbol())
|
||||
override fun isPrincipal(rule: Rule): Boolean = principalRuleTags.contains(rule.uniqueTag())
|
||||
override fun isWeakPrincipal(rule: Rule): Boolean = false
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ class TestStoreAwareJournal {
|
|||
private object LegacyMockIncrProgSpec : IncrementalProgramSpec {
|
||||
override fun isPrincipal(ctr: Constraint): Boolean = ctr.isPrincipal
|
||||
override fun isPrincipal(rule: Rule): Boolean = rule.all().any { it is Constraint && it.isPrincipal }
|
||||
override fun isWeakPrincipal(rule: Rule): Boolean = false
|
||||
}
|
||||
|
||||
private class JournalDispatcherHelper(
|
||||
|
|
|
|||
Loading…
Reference in New Issue