Skip to content

Fix LT-22575: Deleting a duplicated phonological rule deletes objects the original still uses#387

Merged
jasonleenaylor merged 1 commit into
masterfrom
claude/exciting-hertz-ef2eda
Jul 7, 2026
Merged

Fix LT-22575: Deleting a duplicated phonological rule deletes objects the original still uses#387
jasonleenaylor merged 1 commit into
masterfrom
claude/exciting-hertz-ef2eda

Conversation

@jasonleenaylor

@jasonleenaylor jasonleenaylor commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Problem

LT-22575: after duplicating a phonological rule (LT-21904, #342), deleting either copy corrupts the survivor.

A PhRegularRule's alpha-variable PhFeatureConstraints and its sequence/iteration environment members are owned by the PhPhonData pools (FeatConstraintsOS, ContextsOS) and only referenced by the rule's contexts. CopyObject correctly 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's LeftContextOA/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, no CopyObject changes, 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.CanDelete counts referrers before allowing deletion).

Tests

Four tests in LingTests.cs, each red before the fix and green after:

  • DuplicateRegularRule_DeletingCloneDoesNotDeleteOriginalsFeatureConstraints — the reported repro
  • DuplicateRegularRule_SharedConstraintSurvivesUntilLastRuleDeleted — constraint shared (not copied), survives first deletion, GCed on last
  • DuplicateRegularRule_RemovingContextKeepsConstraintUsedByClone — the edit path
  • DuplicateRegularRule_DeletingCloneKeepsSharedEnvironmentMembers — the environment-wipe cascade

Full SIL.LCModel.Tests suite (net8.0): 1701 passed, 0 failed.

Notes for reviewers

  • PhMetathesisRule needs no change (it has no constraint GC).
  • MoAffixProcess shares the same FeatureConstraints structure but has never GCed constraints on deletion; that pre-existing leak-by-omission is out of scope.
  • GetFeatureConstraintsExcept is no longer used by the edit-path GC but remains public API and backs the FeatureConstraints virtual property.

🤖 Generated with Claude Code


This change is Reviewable

…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>
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

LCM Tests

    16 files  ± 0      16 suites  ±0   3m 13s ⏱️ +16s
 2 867 tests + 4   2 847 ✅ + 4   20 💤 ±0  0 ❌ ±0 
11 416 runs  +16  11 248 ✅ +16  168 💤 ±0  0 ❌ ±0 

Results for commit 6061912. ± Comparison against base commit a581a64.

@jasonleenaylor
jasonleenaylor merged commit 8f855bc into master Jul 7, 2026
4 checks passed
@jasonleenaylor
jasonleenaylor deleted the claude/exciting-hertz-ef2eda branch July 7, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants