From 345b29d88db4abbe4d38ffc10e212559b6284043 Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Sat, 28 Feb 2026 14:17:19 +0800 Subject: [PATCH] checker: fix shared array slice type with implicit clone (fixes #26663) (#26666) --- vlib/v/checker/checker.v | 29 ++++++++- .../tests/concurrency/shared_lock_expr_test.v | 60 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index d03f10584..099e7d53c 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -5063,6 +5063,32 @@ fn (mut c Checker) lock_expr(mut node ast.LockExpr) ast.Type { mut last_stmt := node.stmts.last() if mut last_stmt is ast.ExprStmt { c.expected_type = expected_type + // Check for shared array slice - auto clone for safety + if mut last_stmt.expr is ast.IndexExpr { + index_expr := last_stmt.expr + if index_expr.index is ast.RangeExpr && index_expr.left_type.has_flag(.shared_f) + && !c.inside_unsafe { + // Slicing a shared array creates a view over shared memory. + // Auto-clone for safety to avoid data races. + c.add_error_detail_with_pos('To silence this notice, use either an explicit `.clone()`, +or use an explicit `unsafe{ ... }` block, if you do not want a copy of the slice.', + index_expr.pos) + c.note('an implicit clone of the shared array slice was done here', + index_expr.pos) + slice_type := index_expr.typ + last_stmt.expr = ast.CallExpr{ + name: 'clone' + kind: .clone + left: index_expr + left_type: slice_type + is_method: true + receiver_type: slice_type + return_type: slice_type + scope: c.fn_scope + is_return_used: true + } + } + } ret_type = c.expr(mut last_stmt.expr) } } @@ -5529,12 +5555,13 @@ fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type { } // array[1..2] => array // fixed_array[1..2] => array + // shared array[1..2] => array (slicing creates a new non-shared array) if typ_sym.kind == .array_fixed { elem_type := c.table.value_type(typ) idx := c.table.find_or_register_array(elem_type) typ = ast.new_type(idx) } else { - typ = typ.set_nr_muls(0) + typ = typ.set_nr_muls(0).clear_flag(.shared_f) } } else { // [1] if typ_sym.kind == .map { diff --git a/vlib/v/tests/concurrency/shared_lock_expr_test.v b/vlib/v/tests/concurrency/shared_lock_expr_test.v index 390cc9197..b3cea7d0c 100644 --- a/vlib/v/tests/concurrency/shared_lock_expr_test.v +++ b/vlib/v/tests/concurrency/shared_lock_expr_test.v @@ -98,6 +98,66 @@ fn test_shared_lock_array_index_expr() { } } +fn test_shared_lock_array_slice_expr() { + // Test slicing shared arrays in rlock expressions (fixes issue #26663) + // Note: slicing a shared array automatically clones for safety, + // since a slice is a view over the same memory buffer. + shared a := ['a', 'b', 'c', 'd'] + + // Basic slice - implicit clone happens (safe independent copy) + slice1 := lock { + a[1..3] + } + assert slice1.len == 2 + assert slice1[0] == 'b' + assert slice1[1] == 'c' + + // Full slice + slice2 := lock { + a[..] + } + assert slice2.len == 4 + assert slice2 == ['a', 'b', 'c', 'd'] + + // Slice from start + slice3 := lock { + a[..2] + } + assert slice3.len == 2 + assert slice3[0] == 'a' + assert slice3[1] == 'b' + + // Slice to end + slice4 := lock { + a[2..] + } + assert slice4.len == 2 + assert slice4[0] == 'c' + assert slice4[1] == 'd' + + // Test with int array + shared arr := [1, 2, 3, 4, 5] + slice5 := lock { + arr[1..4] + } + assert slice5.len == 3 + assert slice5[0] == 2 + assert slice5[1] == 3 + assert slice5[2] == 4 + + // Explicit clone - same behavior, no warning + slice6 := lock { + a[1..3].clone() + } + assert slice6 == ['b', 'c'] + + // Unsafe block - get a view (user takes responsibility) + slice7 := lock { + unsafe { a[1..3] } + } + assert slice7 == ['b', 'c'] +} + struct DummySt { a int } -- 2.39.5