i128 bitwise + Shl/Shr (overflow trap); BitNot unary. Set/Set on |/&/^ means union/intersection/symmetric-diff; other types use the bitwise path. */
(&mut self, op: OpCode, chunk: &SSAChunk, slots: &mut [Val])
| 269 | |
| 270 | /* i128 bitwise + Shl/Shr (overflow trap); BitNot unary. Set/Set on |/&/^ means union/intersection/symmetric-diff; other types use the bitwise path. */ |
| 271 | pub(crate) fn handle_bitwise(&mut self, op: OpCode, chunk: &SSAChunk, slots: &mut [Val]) -> Result<(), VmErr> { |
| 272 | // Augmented set bitwise reuses the plain path but mutates the left set in place. |
| 273 | let inplace = matches!(op, OpCode::InPlaceBitOr | OpCode::InPlaceBitAnd | OpCode::InPlaceBitXor); |
| 274 | let op = match op { |
| 275 | OpCode::InPlaceBitOr => OpCode::BitOr, |
| 276 | OpCode::InPlaceBitAnd => OpCode::BitAnd, |
| 277 | OpCode::InPlaceBitXor => OpCode::BitXor, |
| 278 | other => other, |
| 279 | }; |
| 280 | if op == OpCode::BitNot { |
| 281 | let v = self.pop()?; |
| 282 | let i = self.as_i128(v).ok_or(cold_type("~ requires an integer"))?; |
| 283 | let out = self.int_to_val(Some(!i))?; |
| 284 | self.push(out); |
| 285 | return Ok(()); |
| 286 | } |
| 287 | |
| 288 | let (a, b) = self.pop2()?; |
| 289 | |
| 290 | // User instance operands dispatch __or__/__and__/__xor__ (and reflected) first. |
| 291 | let roots = self.temp_roots.len(); |
| 292 | self.temp_roots.push(a); |
| 293 | self.temp_roots.push(b); |
| 294 | let dunder = self.try_binary_dunder(op, a, b, chunk, slots); |
| 295 | self.temp_roots.truncate(roots); |
| 296 | if let Some(r) = dunder? { self.push(r); return Ok(()); } |
| 297 | |
| 298 | if self.is_set_like(a) && self.is_set_like(b) |
| 299 | && matches!(op, OpCode::BitAnd | OpCode::BitOr | OpCode::BitXor) { |
| 300 | return if inplace { self.set_iop_and_push(a, b, op) } else { self.set_binop_and_push(a, b, op) }; |
| 301 | } |
| 302 | // `dict | dict` (and `|=`) merges, right operand winning. |
| 303 | if op == OpCode::BitOr && a.is_heap() && b.is_heap() |
| 304 | && matches!(self.heap.get(a), HeapObj::Dict(_)) |
| 305 | && matches!(self.heap.get(b), HeapObj::Dict(_)) { |
| 306 | let mut merged = DictMap::with_capacity(0); |
| 307 | if let HeapObj::Dict(d) = self.heap.get(a) { for (k, v) in d.borrow().entries.iter() { merged.insert(*k, *v, &self.heap); } } |
| 308 | if let HeapObj::Dict(d) = self.heap.get(b) { for (k, v) in d.borrow().entries.iter() { merged.insert(*k, *v, &self.heap); } } |
| 309 | return self.alloc_and_push_dict(merged); |
| 310 | } |
| 311 | let result = match op { |
| 312 | OpCode::BitAnd => self.bitwise_op(a, b, |x, y| x & y)?, |
| 313 | OpCode::BitOr => self.bitwise_op(a, b, |x, y| x | y)?, |
| 314 | OpCode::BitXor => self.bitwise_op(a, b, |x, y| x ^ y)?, |
| 315 | OpCode::Shl => self.exec_shl(a, b)?, |
| 316 | OpCode::Shr => self.exec_shr(a, b)?, |
| 317 | _ => return Err(cold_runtime("non-bitwise opcode in handle_bitwise")), |
| 318 | }; |
| 319 | self.push(result); |
| 320 | Ok(()) |
| 321 | } |
| 322 | |
| 323 | fn exec_shl(&mut self, a: Val, b: Val) -> Result<Val, VmErr> { |
| 324 | if !b.is_int() { return Err(cold_type("shift count must be an integer")); } |
no test coverage detected