Fix LT-22575: Deleting a duplicated phonological rule deletes objects the original still uses#387
Merged
Merged
Conversation
…objects Duplicating a rule (LT-21904) gives the copy references to the original's pooled PhFeatureConstraints and environment member contexts; PhPhonData owns both, rules only reference them. The deletion side effects removed those pooled objects unconditionally, assuming a single referring rule, so deleting either copy destroyed constraints and environment members the other still used (and could null the survivor's RHS contexts entirely). Guard the four GC sites with incoming-reference checks: a pooled object is removed from its pool only when the dying rule or context is its last referrer. GC still completes when the last referrer is deleted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jtmaxwell3
approved these changes
Jul 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
LT-22575: after duplicating a phonological rule (LT-21904, #342), deleting either copy corrupts the survivor.
A
PhRegularRule's alpha-variablePhFeatureConstraints and its sequence/iteration environment members are owned by thePhPhonDatapools (FeatConstraintsOS,ContextsOS) and only referenced by the rule's contexts.CopyObjectcorrectly shares references to objects outside the copied subtree, so a duplicate references the same pooled objects as the original. The deletion side effects, written long before duplication existed, removed those pooled objects from their owning pools unconditionally — assuming the dying rule was the sole referrer. Deleting one copy therefore deleted the other copy's feature constraints, and could cascade through a shared sequence environment to null the survivor'sLeftContextOA/RightContextOA, silently wiping its environment.Fix
Guard the four GC sites with the model's incoming-reference bookkeeping (
ICmObject.ReferringObjects,IPhContextOrVar.Rule) — no new mechanism, noCopyObjectchanges, no object copies:PhRegularRule.OnBeforeObjectDeleted: GC a pooled constraint only when every referring context resolves to the dying rule.PhRegularRule.RemoveObjectSideEffectsInternal(StrucDesc edit path): GC only when the removed context is the constraint's last referrer anywhere, not just within the editing rule. The LT-21729 remove-ref-first ordering is preserved.PhSequenceContext/PhIterationContext.OnBeforeObjectDeleted: GC a pooled member only when the dying container is its last referrer.GC still completes: when the last referring rule is deleted, the pooled objects are removed with it (each test asserts the pool empties). This matches the codebase's established referrer-aware precedent (
MoMorphSynAnalysis.CanDeletecounts referrers before allowing deletion).Tests
Four tests in
LingTests.cs, each red before the fix and green after:DuplicateRegularRule_DeletingCloneDoesNotDeleteOriginalsFeatureConstraints— the reported reproDuplicateRegularRule_SharedConstraintSurvivesUntilLastRuleDeleted— constraint shared (not copied), survives first deletion, GCed on lastDuplicateRegularRule_RemovingContextKeepsConstraintUsedByClone— the edit pathDuplicateRegularRule_DeletingCloneKeepsSharedEnvironmentMembers— the environment-wipe cascadeFull SIL.LCModel.Tests suite (net8.0): 1701 passed, 0 failed.
Notes for reviewers
PhMetathesisRuleneeds no change (it has no constraint GC).MoAffixProcessshares the sameFeatureConstraintsstructure but has never GCed constraints on deletion; that pre-existing leak-by-omission is out of scope.GetFeatureConstraintsExceptis no longer used by the edit-path GC but remains public API and backs theFeatureConstraintsvirtual property.🤖 Generated with Claude Code
This change is