Skip to content

Refactor reference types: Replace Rc<RefCell<T>> with dynamic reference system#55

Draft
dhedey wants to merge 11 commits intofeat/references-reworkfrom
claude/refactor-shared-references-0kGrv
Draft

Refactor reference types: Replace Rc<RefCell<T>> with dynamic reference system#55
dhedey wants to merge 11 commits intofeat/references-reworkfrom
claude/refactor-shared-references-0kGrv

Conversation

@dhedey
Copy link
Owner

@dhedey dhedey commented Feb 23, 2026

Summary

This PR replaces the old Rc<RefCell<T>> based reference system with a new dynamic reference tracking system. The changes introduce SharedReference<T> and MutableReference<T> as the primary reference types, backed by a ReferenceCore that tracks active/inactive states and reference metadata (creation spans, paths).

Key Changes

  • New Dynamic Reference Types: Introduced SharedReference<T> and MutableReference<T> to replace SharedSubRcRefCell and MutableSubRcRefCell
  • Type Aliases for Compatibility: Added type aliases (Shared<T>, Mutable<T>, DisabledShared<T>, DisabledMutable<T>) mapping old names to new reference types for gradual migration
  • Referenceable Refactoring: Changed Referenceable from a generic type alias to a concrete struct that wraps AnyValue, with methods to create inactive references and attempt unwrapping
  • Reference Tracking: Added current_span() methods to reference types and emplacers to preserve span information during type narrowing operations
  • Emplacer API Updates: Updated emplace() and emplace_unchecked() methods to accept PathExtension and SpanRange parameters for accurate error reporting
  • Safety Improvements: Marked mapping operations on references as unsafe with requirements for correct PathExtension values
  • Form System Updates: Updated QqqShared<T> and QqqMutable<T> type aliases to use new reference types; updated IsDynCompatibleForm implementations to pass path and span information
  • Removed Old Implementations: Removed manual Mutable<T> and Shared<T> struct definitions and their trait implementations, now delegating to the new reference types

Notable Implementation Details

  • The new system tracks reference creation spans for better error messages when reference conflicts occur
  • PathExtension is used to describe how a reference navigates through the value hierarchy (e.g., Child for property access, Tightened for type narrowing)
  • InactiveSharedReference and InactiveMutableReference can be cloned and later activated, enabling flexible reference management
  • Bridge methods like disable() and enable() maintain API compatibility with the old system
  • The Referenceable type is now non-generic and always wraps AnyValue, simplifying the reference creation API

https://claude.ai/code/session_01XaCsWwXFYntkKqZNf8kizU

claude and others added 11 commits February 22, 2026 23:56
Replace the old Rc<RefCell<T>>-based reference system with the new
custom dynamic reference types:

- Shared<T> becomes a type alias for SharedReference<T>
- Mutable<T> becomes a type alias for MutableReference<T>
- DisabledShared<T> becomes InactiveSharedReference<T>
- DisabledMutable<T> becomes InactiveMutableReference<T>
- VariableContent::Referenceable uses new Referenceable struct

Key changes:
- Added bridge methods (_legacy suffix) to SharedReference and
  MutableReference for backward compatibility with old map/try_map
  patterns that didn't require PathExtension
- QqqShared<T> and QqqMutable<T> now alias directly to the reference
  types, collapsing the old form/wrapper type distinction
- Added explicit IsArgument impls for SharedReference<AnyValue> and
  MutableReference<AnyValue> since the blanket impl only covers leaf
  types
- Manual Clone impls for InactiveSharedReference/InactiveMutableReference
  to avoid unnecessary T: Clone bound from derive
- CopyOnWrite conversions use replace_legacy pattern instead of
  into_content() for non-leaf types
- Updated trybuild test for improved aliasing error message from new
  reference tracking system

https://claude.ai/code/session_01XaCsWwXFYntkKqZNf8kizU
…d SpanRange

Replace all _legacy bridge methods (map_legacy, try_map_legacy,
map_optional_legacy, replace_legacy, emplace_unchecked_legacy) with
calls using proper PathExtension values and real SpanRange values.

Key changes:
- Add current_span() to EmplacerCore, SharedEmplacerV2, MutableEmplacerV2,
  SharedReference, MutableReference, AnyRefEmplacer, and AnyMutEmplacer
- Use PathExtension::Tightened(T::type_kind()) for type-narrowing operations
  (leaf_to_dyn, into_shared, into_mutable, into_assignee, etc.)
- Use PathExtension::Child(ObjectChild(name), AnyType) for property access
- Use real spans from emplacer.current_span() instead of Span::call_site()
- Make AnyRef/AnyMut map methods and emplacers take PathExtension + SpanRange
- Remove all _legacy bridge methods from SharedReference and MutableReference

https://claude.ai/code/session_01XaCsWwXFYntkKqZNf8kizU
- Introduce MappedRef and MappedMut types with unsafe constructors to
  confine unsafety to construction rather than use sites
- Update SharedReference/MutableReference map/try_map to accept closures
  returning MappedRef/MappedMut, making callers safe
- Fix &mut aliasing UB in AnyMut::emplace_map by storing *mut T in
  emplacer state instead of duplicate &mut T
- Have PropertyAccessInterface/IndexAccessInterface return
  MappedRef/MappedMut with proper ChildSpecifier (ObjectChild/ArrayChild)
- Add output_span_range to PropertyAccessCallContext/IndexAccessCallContext
- Thread SpanRange through DynResolveFrom, leaf_to_dyn, and
  from_argument_value to eliminate None span arguments
- Remove all TODO[references] comments

https://claude.ai/code/session_01XaCsWwXFYntkKqZNf8kizU
…ields private

- Make MappedRef/MappedMut fields private, add into_parts() for
  decomposition. This preserves the invariant that construction must go
  through the unsafe new() constructor.
- Add emplace() method back to SharedEmplacer/MutableEmplacer with
  lifetime-checked &'e V parameter, keeping emplace_unchecked for cases
  where the compiler can't prove the lifetime (e.g. __InlineMapper).
- Switch callers from emplace_unchecked to emplace where the lifetime
  is available: SharedReference::map/try_map, MutableReference::map/
  try_map, AnyRef::map_optional, AnyMut::map_optional, and leaf_to_dyn
  implementations in shared.rs, mutable.rs, and assignee.rs.

https://claude.ai/code/session_01XaCsWwXFYntkKqZNf8kizU
…ectly

- Move MappedRef to shared_reference.rs and MappedMut to mutable_reference.rs
- Add new_unchecked constructors for lifetime transmute cases (__InlineMapper)
- Change all emplacer emplace methods to take MappedRef/MappedMut, making them safe
- Drop emplace_unchecked from all emplacers (SharedEmplacer, MutableEmplacer,
  AnyRefEmplacer, AnyMutEmplacer)
- All unsafe is now confined to MappedRef/MappedMut constructors

https://claude.ai/code/session_01XaCsWwXFYntkKqZNf8kizU
The swap_itself.stderr error was pointing to `let a = "a"` instead of
the second `a` in `a.swap(a)` because enable() ignored its span parameter
and creation_span was never updated from the root span.

- Add set_tracked_span to SharedReference, MutableReference, and their
  inactive variants
- Update enable() to call set_tracked_span before activate(), so
  borrow-conflict errors point to the usage site
- Update VariableBinding::into_mut/into_shared/into_late_bound to set
  the tracked span before initial activation too

https://claude.ai/code/session_01XaCsWwXFYntkKqZNf8kizU
Move span-setting into activate() so callers don't need a separate
set_tracked_span step. Remove enable() and set_tracked_span() from
Inactive*Reference types. Add new_active_shared/new_active_mutable
convenience methods to Referenceable.

https://claude.ai/code/session_01XaCsWwXFYntkKqZNf8kizU
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