Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Read type version and heap layout through Py<PyType>
Inline-cache checks use tp_version_tag(). Heap-type weakref checks use
heaptype_ext().

Assisted-by: Grok CLI:grok-4.7
  • Loading branch information
youknowone committed Sep 25, 2026
commit de5e7e4e30b58f17df4fe6569d9cf0b36f5e7780
2 changes: 1 addition & 1 deletion crates/vm/src/builtins/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ fn object_getstate_default(obj: &PyObject, required: bool, vm: &VirtualMachine)
}

// Add __weakref__ size if type has weakref support
let has_weakref = if let Some(ref ext) = obj.class().heaptype_ext {
let has_weakref = if let Some(ext) = obj.class().heaptype_ext() {
match &ext.slots {
None => true, // Heap type without __slots__ has automatic weakref
Some(slots) => slots.iter().any(|s| s.as_bytes() == b"__weakref__"),
Expand Down
10 changes: 10 additions & 0 deletions crates/vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1691,6 +1691,16 @@ impl Py<PyType> {
&self.payload().attributes
}

#[inline]
pub fn tp_version_tag(&self) -> &AtomicU32 {
&self.payload().tp_version_tag
}

#[inline]
pub fn heaptype_ext(&self) -> Option<&HeapTypeExt> {
self.payload().heaptype_ext.as_deref()
}

pub fn is_subtype(&self, other: &Self) -> bool {
is_subtype_with_mro(&self.mro.read(), self, other)
}
Expand Down
39 changes: 22 additions & 17 deletions crates/vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6015,7 +6015,7 @@ impl ExecutingFrame<'_> {
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);

if type_version != 0
&& owner.class().tp_version_tag.load(Acquire) == type_version
&& owner.class().tp_version_tag().load(Acquire) == type_version
&& let Some(func) = self.try_read_cached_descriptor(cache_base, type_version)
{
let owner = self.pop_stackref();
Expand All @@ -6034,7 +6034,7 @@ impl ExecutingFrame<'_> {
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);

if type_version != 0
&& owner.class().tp_version_tag.load(Acquire) == type_version
&& owner.class().tp_version_tag().load(Acquire) == type_version
&& !owner.has_instance_dict()
&& let Some(func) = self.try_read_cached_descriptor(cache_base, type_version)
{
Expand All @@ -6054,7 +6054,8 @@ impl ExecutingFrame<'_> {
let owner = self.top_value();
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);

if type_version != 0 && owner.class().tp_version_tag.load(Acquire) == type_version {
if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version
{
// Check instance dict doesn't shadow the method.
let shadowed = match self.shadowing_instance_attr(cache_base, attr_name, vm) {
Ok(shadowed) => shadowed.is_some(),
Expand Down Expand Up @@ -6082,7 +6083,8 @@ impl ExecutingFrame<'_> {
let owner = self.top_value();
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);

if type_version != 0 && owner.class().tp_version_tag.load(Acquire) == type_version {
if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version
{
// Type version matches — no data descriptor for this attr.
// Try direct dict lookup, skipping full descriptor protocol.
if let Some(dict) = owner.dict()
Expand All @@ -6105,7 +6107,7 @@ impl ExecutingFrame<'_> {
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);

if type_version != 0
&& owner.class().tp_version_tag.load(Acquire) == type_version
&& owner.class().tp_version_tag().load(Acquire) == type_version
&& let Some(dict) = owner.dict()
{
// Try the cached entry index first; a hit is an identity
Expand Down Expand Up @@ -6143,7 +6145,7 @@ impl ExecutingFrame<'_> {
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);

if type_version != 0
&& owner.class().tp_version_tag.load(Acquire) == type_version
&& owner.class().tp_version_tag().load(Acquire) == type_version
&& let Some(module) = owner.downcast_ref_if_exact::<PyModule>(vm)
&& let Ok(value) = module.get_attr(attr_name, vm)
{
Expand All @@ -6166,7 +6168,7 @@ impl ExecutingFrame<'_> {
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);

if type_version != 0
&& owner.class().tp_version_tag.load(Acquire) == type_version
&& owner.class().tp_version_tag().load(Acquire) == type_version
&& let Some(attr) = self.try_read_cached_descriptor(cache_base, type_version)
{
self.pop_stackref();
Expand All @@ -6188,7 +6190,8 @@ impl ExecutingFrame<'_> {
let owner = self.top_value();
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);

if type_version != 0 && owner.class().tp_version_tag.load(Acquire) == type_version {
if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version
{
// Instance dict has priority — check if attr is shadowed
if let Some(value) = self.shadowing_instance_attr(cache_base, attr_name, vm)? {
self.pop_stackref();
Expand Down Expand Up @@ -6251,7 +6254,7 @@ impl ExecutingFrame<'_> {
&& metaclass_version != 0
&& let Some(owner_type) = owner.downcast_ref::<PyType>()
&& owner_type.tp_version_tag.load(Acquire) == type_version
&& owner.class().tp_version_tag.load(Acquire) == metaclass_version
&& owner.class().tp_version_tag().load(Acquire) == metaclass_version
&& let Some(attr) = self.try_read_cached_descriptor(cache_base, type_version)
{
self.pop_stackref();
Expand All @@ -6276,7 +6279,7 @@ impl ExecutingFrame<'_> {
&& !self.specialization_eval_frame_active(vm)
&& type_version != 0
&& func_version != 0
&& owner.class().tp_version_tag.load(Acquire) == type_version
&& owner.class().tp_version_tag().load(Acquire) == type_version
&& let Some(func_obj) =
self.try_read_cached_descriptor(cache_base, type_version)
&& let Some(func) = func_obj.downcast_ref_if_exact::<PyFunction>(vm)
Expand All @@ -6300,7 +6303,8 @@ impl ExecutingFrame<'_> {
let owner = self.top_value();
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);

if type_version != 0 && owner.class().tp_version_tag.load(Acquire) == type_version {
if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version
{
let slot_offset =
self.code.instructions.read_cache_u32(cache_base + 3) as usize;
if let Some(value) = owner.get_slot(slot_offset) {
Expand All @@ -6326,7 +6330,7 @@ impl ExecutingFrame<'_> {

if type_version != 0
&& !self.specialization_eval_frame_active(vm)
&& owner.class().tp_version_tag.load(Acquire) == type_version
&& owner.class().tp_version_tag().load(Acquire) == type_version
&& let Some(fget_obj) =
self.try_read_cached_descriptor(cache_base, type_version)
&& let Some(func) = fget_obj.downcast_ref_if_exact::<PyFunction>(vm)
Expand All @@ -6349,7 +6353,7 @@ impl ExecutingFrame<'_> {
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);

if type_version != 0
&& owner.class().tp_version_tag.load(Acquire) == type_version
&& owner.class().tp_version_tag().load(Acquire) == type_version
&& let Some(dict) = owner.dict()
{
self.pop_stackref(); // owner
Expand All @@ -6371,7 +6375,7 @@ impl ExecutingFrame<'_> {
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);

if type_version != 0
&& owner.class().tp_version_tag.load(Acquire) == type_version
&& owner.class().tp_version_tag().load(Acquire) == type_version
&& let Some(dict) = owner.dict()
{
self.pop_stackref(); // owner
Expand All @@ -6387,7 +6391,7 @@ impl ExecutingFrame<'_> {
let type_version = self.code.instructions.read_cache_u32(cache_base + 1);
let version_match = type_version != 0 && {
let owner = self.top_value();
owner.class().tp_version_tag.load(Acquire) == type_version
owner.class().tp_version_tag().load(Acquire) == type_version
};

if version_match {
Expand Down Expand Up @@ -6473,7 +6477,7 @@ impl ExecutingFrame<'_> {
let owner = self.nth_value(1);
if !self.specialization_eval_frame_active(vm)
&& type_version != 0
&& owner.class().tp_version_tag.load(Acquire) == type_version
&& owner.class().tp_version_tag().load(Acquire) == type_version
&& let Some((func, func_version)) =
owner.class().get_cached_getitem_for_specialization()
&& func.func_version() == func_version
Expand Down Expand Up @@ -7706,7 +7710,8 @@ impl ExecutingFrame<'_> {
let cache_base = instr_idx + 1;
let obj = self.top_value();
let cached_version = self.code.instructions.read_cache_u32(cache_base + 1);
if cached_version != 0 && obj.class().tp_version_tag.load(Acquire) == cached_version
if cached_version != 0
&& obj.class().tp_version_tag().load(Acquire) == cached_version
{
self.pop_stackref();
self.push_bool_or_fused_jump(instruction.cache_entries(), true, vm);
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/protocol/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ impl PyObject {
// A type carrying a sequence table turns the deletion down in
// PySequence_DelItem's words instead; every heap type carries one.
let name = self.class().slot_name();
let msg = if seq.slots().has_any() || self.class().heaptype_ext.is_some() {
let msg = if seq.slots().has_any() || self.class().heaptype_ext().is_some() {
format!("'{name}' object doesn't support item deletion")
} else {
format!("'{name}' object does not support item deletion")
Expand Down
Loading