You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
#[vm::pyclass] / #[vm::pymethod] causes infinite loop in macro expansion #8097
cargo check hangs indefinitely — no error, just a rustc process pegged at 100% CPU —
when any #[pyclass] or #[pymethod] attribute is written with a path prefix such as #[vm::pymethod].
Affected:rustpython-derive-impl 0.5.0 on rustc 1.96.0 / aarch64-apple-darwin
Root cause
attrs_to_content_items (crates/derive-impl/src/pyclass.rs ~line 1986) scans the
attribute list of each impl item with a Peekable iterator. peek() reads the current
element without advancing; only next() moves forward. The loop places iter.next()
after an early continue, so it is skipped whenever get_ident() returns None:
whileletSome((_, attr)) = iter.peek(){let attr_name = ifletSome(ident) = attr.get_ident(){
ident.to_string()}else{continue;// ← iter.next() skipped; same attribute peeked forever};// ...
iter.next();// ← never reached when get_ident() returns None}
syn::Attribute::get_ident() only returns Some(name) for single-word attribute names
like pymethod. For a prefixed name like vm::pymethod it returns None, because vm::pymethod is two path segments, not one. That None triggers continue, the
iterator never advances, and the loop spins forever.
Writing #[pymethod] (no prefix) works fine because get_ident() returns Some("pymethod"), which hits the break in ALL_ALLOWED_NAMES.
Minimal reproduction
Cargo.toml:
[package]
name = "reproduce-pyclass-hang"version = "0.1.0"edition = "2021"
[dependencies]
rustpython-vm = { version = "0.5", default-features = false, features = ["compiler", "gc"] }
src/lib.rs:
use rustpython_vm as vm;#[derive(Debug, vm::PyPayload)]structItem{value:i64}#[vm::pyclass]implItem{#[vm::pymethod]// ← qualified path triggers the hangfnvalue(&self) -> i64{self.value}}
Run cargo check — rustc pegs a CPU core indefinitely with no output.
Fix
Move iter.next() outside the if let so it always fires, even when get_ident()
returns None. Unrecognised attribute paths are then simply skipped over.
// crates/derive-impl/src/pyclass.rs (~line 1986)whileletSome((_, attr)) = iter.peek(){// Wrap in if-let so multi-segment paths (e.g. vm::pymethod) that return// None from get_ident() are skipped instead of looping forever.ifletSome(ident) = attr.get_ident(){let attr_name = ident.to_string();if attr_name == "cfg"{
cfgs.push(attr.clone());}elseifALL_ALLOWED_NAMES.contains(&attr_name.as_str()){break;}}
iter.next();}
Activity