From 8629e5abfc6e7d58c4640cc522a89128ad72ccc4 Mon Sep 17 00:00:00 2001 From: Grigorii Kirgizov Date: Wed, 24 Jun 2020 17:59:15 +0300 Subject: [PATCH] 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. --- .../reactor/core/internal/ConstraintsProcessing.kt | 6 +++++- .../mps/logic/reactor/core/internal/MatchJournal.kt | 10 ++++++++++ .../logic/reactor/core/internal/MatchJournalImpl.kt | 13 ++++++++++++- .../reactor/program/IncrementalProgramSpec.java | 3 +++ reactor/Test/test/MockIncrProgSpec.kt | 1 + reactor/Test/test/TestStoreAwareJournal.kt | 1 + 6 files changed, 32 insertions(+), 2 deletions(-) diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt index 56ca1506..42ded52a 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt @@ -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): Boolean = + utags.contains(this.ruleUniqueTag) || utags.any { utag -> dependsOnRule(utag) } + } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt index 68c466c4..6a05af93 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt @@ -189,7 +189,17 @@ interface MatchJournal : MutableIterable, 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() } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt index aa88e828..f4ad0ecf 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt @@ -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(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()) /** diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/program/IncrementalProgramSpec.java b/reactor/Core/src/jetbrains/mps/logic/reactor/program/IncrementalProgramSpec.java index 88047652..928df908 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/program/IncrementalProgramSpec.java +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/program/IncrementalProgramSpec.java @@ -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(); diff --git a/reactor/Test/test/MockIncrProgSpec.kt b/reactor/Test/test/MockIncrProgSpec.kt index 47bd05f6..3993383f 100644 --- a/reactor/Test/test/MockIncrProgSpec.kt +++ b/reactor/Test/test/MockIncrProgSpec.kt @@ -22,4 +22,5 @@ import jetbrains.mps.logic.reactor.program.Rule class MockIncrProgSpec(val principalRuleTags: Set, val principalCtrSyms: Set) : 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 } \ No newline at end of file diff --git a/reactor/Test/test/TestStoreAwareJournal.kt b/reactor/Test/test/TestStoreAwareJournal.kt index 32cdc2ec..9f9a16d2 100644 --- a/reactor/Test/test/TestStoreAwareJournal.kt +++ b/reactor/Test/test/TestStoreAwareJournal.kt @@ -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(