C3 merge of the bases' linearizations plus the bases list itself, the tail of `L[cls] = cls :: merge(...)`. `cls` is prepended by the caller (it isn't allocated yet at validation time). Errs on an inconsistent hierarchy, matching CPython's `TypeError` at class creation. */
(&self, bases: &[Val])
| 38 | |
| 39 | /* C3 merge of the bases' linearizations plus the bases list itself, the tail of `L[cls] = cls :: merge(...)`. `cls` is prepended by the caller (it isn't allocated yet at validation time). Errs on an inconsistent hierarchy, matching CPython's `TypeError` at class creation. */ |
| 40 | pub(crate) fn c3_merge(&self, bases: &[Val]) -> Result<alloc::vec::Vec<Val>, VmErr> { |
| 41 | let mut seqs: alloc::vec::Vec<alloc::vec::Vec<Val>> = bases.iter().map(|&b| self.mro_of(b)).collect(); |
| 42 | if !bases.is_empty() { seqs.push(bases.to_vec()); } |
| 43 | let mut out = alloc::vec::Vec::new(); |
| 44 | loop { |
| 45 | seqs.retain(|s| !s.is_empty()); |
| 46 | if seqs.is_empty() { break; } |
| 47 | // A valid head appears in no sequence's tail; take the first such across sequences (C3 order). |
| 48 | let mut head = None; |
| 49 | for s in &seqs { |
| 50 | let h = s[0]; |
| 51 | let in_tail = seqs.iter().any(|t| t.len() > 1 && t[1..].iter().any(|&x| x.0 == h.0)); |
| 52 | if !in_tail { head = Some(h); break; } |
| 53 | } |
| 54 | let Some(h) = head else { |
| 55 | return Err(cold_type("Cannot create a consistent method resolution order (MRO) for bases")); |
| 56 | }; |
| 57 | out.push(h); |
| 58 | for s in &mut seqs { s.retain(|&x| x.0 != h.0); } |
| 59 | } |
| 60 | Ok(out) |
| 61 | } |
| 62 | |
| 63 | // Member lookup along the C3 MRO; first hit wins. Falls back to a direct-then-DFS walk for uncached classes (native classes have no bases, so DFS = own members). Returns `(value, defining_class)` so callers building `BoundUserMethod` / `InstanceMethod` record where the method came from for `super()`. |
| 64 | pub(crate) fn lookup_class_member(&self, cls: Val, name: &str) -> Option<(Val, Val)> { |