Skip to content

Commit 9a6f744

Browse files
authored
no in-place mutation for str and bytes (#231)
1 parent fe9f22a commit 9a6f744

File tree

8 files changed

+17
-112
lines changed

8 files changed

+17
-112
lines changed

crates/monty/src/bytecode/vm/binary.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ impl<T: ResourceTracker> VM<'_, '_, T> {
223223
let (lhs, this) = lhs_guard.as_parts_mut();
224224

225225
// Try in-place operation first (for mutable types like lists)
226-
if lhs.py_iadd(rhs.clone_with_heap(this.heap), this.heap, lhs.ref_id(), this.interns)? {
226+
if lhs.py_iadd(rhs, this.heap, lhs.ref_id(), this.interns)? {
227227
// In-place operation succeeded - push lhs back
228228
let (lhs, this) = lhs_guard.into_parts();
229229
this.push(lhs);

crates/monty/src/heap.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -589,22 +589,15 @@ impl PyTrait for HeapData {
589589

590590
fn py_iadd(
591591
&mut self,
592-
other: Value,
592+
other: &Value,
593593
heap: &mut Heap<impl ResourceTracker>,
594594
self_id: Option<HeapId>,
595595
interns: &Interns,
596596
) -> Result<bool, crate::resource::ResourceError> {
597597
match self {
598-
Self::Str(s) => s.py_iadd(other, heap, self_id, interns),
599-
Self::Bytes(b) => b.py_iadd(other, heap, self_id, interns),
600598
Self::List(l) => l.py_iadd(other, heap, self_id, interns),
601-
Self::Tuple(t) => t.py_iadd(other, heap, self_id, interns),
602599
Self::Dict(d) => d.py_iadd(other, heap, self_id, interns),
603-
_ => {
604-
// Drop other if it's a Ref (ensure proper refcounting for unsupported types)
605-
other.drop_with_heap(heap);
606-
Ok(false)
607-
}
600+
_ => Ok(false),
608601
}
609602
}
610603

crates/monty/src/heap_data.rs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -618,27 +618,6 @@ impl PyTrait for HeapDataMut<'_> {
618618
}
619619
}
620620

621-
fn py_iadd(
622-
&mut self,
623-
other: Value,
624-
heap: &mut Heap<impl ResourceTracker>,
625-
self_id: Option<HeapId>,
626-
interns: &Interns,
627-
) -> Result<bool, crate::resource::ResourceError> {
628-
match self {
629-
Self::Str(s) => s.py_iadd(other, heap, self_id, interns),
630-
Self::Bytes(b) => b.py_iadd(other, heap, self_id, interns),
631-
Self::List(l) => l.py_iadd(other, heap, self_id, interns),
632-
Self::Tuple(t) => t.py_iadd(other, heap, self_id, interns),
633-
Self::Dict(d) => d.py_iadd(other, heap, self_id, interns),
634-
_ => {
635-
// Drop other if it's a Ref (ensure proper refcounting for unsupported types)
636-
other.drop_with_heap(heap);
637-
Ok(false)
638-
}
639-
}
640-
}
641-
642621
fn py_call_attr(
643622
&mut self,
644623
self_id: HeapId,

crates/monty/src/types/bytes.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,6 @@ impl Bytes {
177177
&self.0
178178
}
179179

180-
/// Returns a mutable reference to the inner byte vector.
181-
pub fn as_vec_mut(&mut self) -> &mut Vec<u8> {
182-
&mut self.0
183-
}
184-
185180
/// Creates bytes from the `bytes()` constructor call.
186181
///
187182
/// - `bytes()` with no args returns empty bytes

crates/monty/src/types/list.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -366,13 +366,13 @@ impl PyTrait for List {
366366

367367
fn py_iadd(
368368
&mut self,
369-
other: Value,
369+
other: &Value,
370370
heap: &mut Heap<impl ResourceTracker>,
371371
self_id: Option<HeapId>,
372372
_interns: &Interns,
373373
) -> Result<bool, crate::resource::ResourceError> {
374374
// Extract the value ID first, keeping `other` around to drop later
375-
let Value::Ref(other_id) = &other else { return Ok(false) };
375+
let Value::Ref(other_id) = other else { return Ok(false) };
376376

377377
if Some(*other_id) == self_id {
378378
// Self-extend: clone our own items with proper refcounting
@@ -408,8 +408,6 @@ impl PyTrait for List {
408408
}
409409
}
410410

411-
// Drop the other value - we've extracted its contents and are done with the temporary reference
412-
other.drop_with_heap(heap);
413411
Ok(true)
414412
}
415413

crates/monty/src/types/py_trait.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,13 +241,11 @@ pub trait PyTrait {
241241
/// The `interns` parameter provides access to interned string content for InternString/InternBytes.
242242
fn py_iadd(
243243
&mut self,
244-
other: Value,
245-
heap: &mut Heap<impl ResourceTracker>,
244+
_other: &Value,
245+
_heap: &mut Heap<impl ResourceTracker>,
246246
_self_id: Option<HeapId>,
247247
_interns: &Interns,
248248
) -> Result<bool, ResourceError> {
249-
// Drop other if it's a Ref (ensure proper refcounting for unsupported types)
250-
other.drop_with_heap(heap);
251249
Ok(false)
252250
}
253251

crates/monty/src/types/str.rs

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ use crate::{
2626
/// Wraps a Rust `String` and provides Python-compatible operations.
2727
/// `len()` returns the number of Unicode codepoints (characters), matching Python semantics.
2828
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)]
29-
pub(crate) struct Str(String);
29+
pub(crate) struct Str(Box<str>);
3030

3131
impl Str {
3232
/// Creates a new Str from a Rust String.
3333
#[must_use]
3434
pub fn new(s: String) -> Self {
35-
Self(s)
35+
Self(s.into())
3636
}
3737

3838
/// Returns a reference to the inner string.
@@ -41,11 +41,6 @@ impl Str {
4141
&self.0
4242
}
4343

44-
/// Returns a mutable reference to the inner string.
45-
pub fn as_string_mut(&mut self) -> &mut String {
46-
&mut self.0
47-
}
48-
4944
/// Creates a string from the `str()` constructor call.
5045
///
5146
/// - `str()` with no args returns an empty string
@@ -81,19 +76,19 @@ impl Str {
8176

8277
impl From<String> for Str {
8378
fn from(s: String) -> Self {
84-
Self(s)
79+
Self(s.into())
8580
}
8681
}
8782

8883
impl From<&str> for Str {
8984
fn from(s: &str) -> Self {
90-
Self(s.to_string())
85+
Self(s.into())
9186
}
9287
}
9388

9489
impl From<Str> for String {
9590
fn from(value: Str) -> Self {
96-
value.0
91+
value.0.into_string()
9792
}
9893
}
9994

@@ -202,7 +197,7 @@ pub(crate) fn get_str_slice(s: &str, start: usize, stop: usize, step: i64) -> St
202197
}
203198

204199
impl std::ops::Deref for Str {
205-
type Target = String;
200+
type Target = str;
206201

207202
fn deref(&self) -> &Self::Target {
208203
&self.0
@@ -270,7 +265,7 @@ impl PyTrait for Str {
270265
}
271266

272267
fn py_str(&self, _heap: &Heap<impl ResourceTracker>, _interns: &Interns) -> Cow<'static, str> {
273-
self.0.clone().into()
268+
self.0.clone().into_string().into()
274269
}
275270

276271
fn py_add(
@@ -284,35 +279,6 @@ impl PyTrait for Str {
284279
Ok(Some(Value::Ref(id)))
285280
}
286281

287-
fn py_iadd(
288-
&mut self,
289-
other: Value,
290-
heap: &mut Heap<impl ResourceTracker>,
291-
self_id: Option<HeapId>,
292-
interns: &Interns,
293-
) -> Result<bool, crate::resource::ResourceError> {
294-
match &other {
295-
Value::Ref(other_id) => {
296-
if Some(*other_id) == self_id {
297-
let rhs = self.0.clone();
298-
self.0.push_str(&rhs);
299-
} else if let HeapData::Str(rhs) = heap.get(*other_id) {
300-
self.0.push_str(rhs.as_str());
301-
} else {
302-
return Ok(false);
303-
}
304-
// Drop the other value - we've consumed it
305-
other.drop_with_heap(heap);
306-
Ok(true)
307-
}
308-
Value::InternString(string_id) => {
309-
self.0.push_str(interns.get_str(*string_id));
310-
Ok(true)
311-
}
312-
_ => Ok(false),
313-
}
314-
}
315-
316282
fn py_call_attr(
317283
&mut self,
318284
_self_id: HeapId,

crates/monty/src/value.rs

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -642,12 +642,12 @@ impl PyTrait for Value {
642642

643643
fn py_iadd(
644644
&mut self,
645-
other: Self,
645+
other: &Self,
646646
heap: &mut Heap<impl ResourceTracker>,
647647
_self_id: Option<HeapId>,
648648
interns: &Interns,
649649
) -> Result<bool, crate::resource::ResourceError> {
650-
match (&self, &other) {
650+
match (&self, other) {
651651
(Self::Int(v1), Self::Int(v2)) => {
652652
if let Some(result) = v1.checked_add(*v2) {
653653
*self = Self::Int(result);
@@ -675,18 +675,8 @@ impl PyTrait for Value {
675675
} else {
676676
false
677677
};
678-
// Drop the other value - we've consumed it
679-
other.drop_with_heap(heap);
680678
Ok(result)
681679
}
682-
(Self::Ref(id1), Self::InternString(string_id)) => {
683-
if let HeapDataMut::Str(s1) = heap.get_mut(*id1) {
684-
s1.as_string_mut().push_str(interns.get_str(*string_id));
685-
Ok(true)
686-
} else {
687-
Ok(false)
688-
}
689-
}
690680
// same for bytes
691681
(Self::InternBytes(b1), Self::InternBytes(b2)) => {
692682
let bytes1 = interns.get_bytes(*b1);
@@ -708,26 +698,12 @@ impl PyTrait for Value {
708698
} else {
709699
false
710700
};
711-
// Drop the other value - we've consumed it
712-
other.drop_with_heap(heap);
713701
Ok(result)
714702
}
715-
(Self::Ref(id1), Self::InternBytes(bytes_id)) => {
716-
if let HeapDataMut::Bytes(b1) = heap.get_mut(*id1) {
717-
b1.as_vec_mut().extend_from_slice(interns.get_bytes(*bytes_id));
718-
Ok(true)
719-
} else {
720-
Ok(false)
721-
}
722-
}
723703
(Self::Ref(id), Self::Ref(_)) => {
724704
heap.with_entry_mut(*id, |heap, mut data| data.py_iadd(other, heap, Some(*id), interns))
725705
}
726-
_ => {
727-
// Drop other if it's a Ref (ensure proper refcounting for unsupported type combinations)
728-
other.drop_with_heap(heap);
729-
Ok(false)
730-
}
706+
_ => Ok(false),
731707
}
732708
}
733709

0 commit comments

Comments
 (0)