Skip to content

Commit 345b29d

Browse files
authored
checker: fix shared array slice type with implicit clone (fixes #26663) (#26666)
1 parent e938c46 commit 345b29d

2 files changed

Lines changed: 88 additions & 1 deletion

File tree

‎vlib/v/checker/checker.v‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5063,6 +5063,32 @@ fn (mut c Checker) lock_expr(mut node ast.LockExpr) ast.Type {
50635063
mut last_stmt := node.stmts.last()
50645064
if mut last_stmt is ast.ExprStmt {
50655065
c.expected_type = expected_type
5066+
// Check for shared array slice - auto clone for safety
5067+
if mut last_stmt.expr is ast.IndexExpr {
5068+
index_expr := last_stmt.expr
5069+
if index_expr.index is ast.RangeExpr && index_expr.left_type.has_flag(.shared_f)
5070+
&& !c.inside_unsafe {
5071+
// Slicing a shared array creates a view over shared memory.
5072+
// Auto-clone for safety to avoid data races.
5073+
c.add_error_detail_with_pos('To silence this notice, use either an explicit `.clone()`,
5074+
or use an explicit `unsafe{ ... }` block, if you do not want a copy of the slice.',
5075+
index_expr.pos)
5076+
c.note('an implicit clone of the shared array slice was done here',
5077+
index_expr.pos)
5078+
slice_type := index_expr.typ
5079+
last_stmt.expr = ast.CallExpr{
5080+
name: 'clone'
5081+
kind: .clone
5082+
left: index_expr
5083+
left_type: slice_type
5084+
is_method: true
5085+
receiver_type: slice_type
5086+
return_type: slice_type
5087+
scope: c.fn_scope
5088+
is_return_used: true
5089+
}
5090+
}
5091+
}
50665092
ret_type = c.expr(mut last_stmt.expr)
50675093
}
50685094
}
@@ -5529,12 +5555,13 @@ fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type {
55295555
}
55305556
// array[1..2] => array
55315557
// fixed_array[1..2] => array
5558+
// shared array[1..2] => array (slicing creates a new non-shared array)
55325559
if typ_sym.kind == .array_fixed {
55335560
elem_type := c.table.value_type(typ)
55345561
idx := c.table.find_or_register_array(elem_type)
55355562
typ = ast.new_type(idx)
55365563
} else {
5537-
typ = typ.set_nr_muls(0)
5564+
typ = typ.set_nr_muls(0).clear_flag(.shared_f)
55385565
}
55395566
} else { // [1]
55405567
if typ_sym.kind == .map {

‎vlib/v/tests/concurrency/shared_lock_expr_test.v‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,66 @@ fn test_shared_lock_array_index_expr() {
9898
}
9999
}
100100

101+
fn test_shared_lock_array_slice_expr() {
102+
// Test slicing shared arrays in rlock expressions (fixes issue #26663)
103+
// Note: slicing a shared array automatically clones for safety,
104+
// since a slice is a view over the same memory buffer.
105+
shared a := ['a', 'b', 'c', 'd']
106+
107+
// Basic slice - implicit clone happens (safe independent copy)
108+
slice1 := lock {
109+
a[1..3]
110+
}
111+
assert slice1.len == 2
112+
assert slice1[0] == 'b'
113+
assert slice1[1] == 'c'
114+
115+
// Full slice
116+
slice2 := lock {
117+
a[..]
118+
}
119+
assert slice2.len == 4
120+
assert slice2 == ['a', 'b', 'c', 'd']
121+
122+
// Slice from start
123+
slice3 := lock {
124+
a[..2]
125+
}
126+
assert slice3.len == 2
127+
assert slice3[0] == 'a'
128+
assert slice3[1] == 'b'
129+
130+
// Slice to end
131+
slice4 := lock {
132+
a[2..]
133+
}
134+
assert slice4.len == 2
135+
assert slice4[0] == 'c'
136+
assert slice4[1] == 'd'
137+
138+
// Test with int array
139+
shared arr := [1, 2, 3, 4, 5]
140+
slice5 := lock {
141+
arr[1..4]
142+
}
143+
assert slice5.len == 3
144+
assert slice5[0] == 2
145+
assert slice5[1] == 3
146+
assert slice5[2] == 4
147+
148+
// Explicit clone - same behavior, no warning
149+
slice6 := lock {
150+
a[1..3].clone()
151+
}
152+
assert slice6 == ['b', 'c']
153+
154+
// Unsafe block - get a view (user takes responsibility)
155+
slice7 := lock {
156+
unsafe { a[1..3] }
157+
}
158+
assert slice7 == ['b', 'c']
159+
}
160+
101161
struct DummySt {
102162
a int
103163
}

0 commit comments

Comments
 (0)