Skip to content

Commit a818744

Browse files
authored
builtin: add arr.pop_left() func (#25133)
1 parent b85782b commit a818744

11 files changed

Lines changed: 110 additions & 9 deletions

File tree

‎vlib/builtin/array.v‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,50 @@ pub fn (a array) last() voidptr {
511511
}
512512
}
513513

514+
// pop_left returns the first element of the array and removes it by advancing the data pointer.
515+
// If the `array` is empty, this will panic.
516+
// NOTE: This function:
517+
// - Reduces both length and capacity by 1
518+
// - Advances the underlying data pointer by one element
519+
// - Leaves subsequent elements in-place (no memory copying)
520+
// Sliced views will retain access to the original first element position,
521+
// which is now detached from the array's active memory range.
522+
//
523+
// Example:
524+
// ```v
525+
// mut a := [1, 2, 3, 4, 5]
526+
// b := unsafe { a[..5] } // full slice view
527+
// first := a.pop_left()
528+
//
529+
// // Array now starts from second element
530+
// dump(a) // a: [2, 3, 4, 5]
531+
// assert a.len == 4
532+
// assert a.cap == 4
533+
//
534+
// // Slice retains original memory layout
535+
// dump(b) // b: [1, 2, 3, 4, 5]
536+
// assert b.len == 5
537+
//
538+
// assert first == 1
539+
//
540+
// // Modifications affect both array and slice views
541+
// a[0] = 99
542+
// assert b[1] == 99 // changed in both
543+
// ```
544+
pub fn (mut a array) pop_left() voidptr {
545+
if a.len == 0 {
546+
panic('array.pop_left: array is empty')
547+
}
548+
first_elem := a.data
549+
unsafe {
550+
a.data = &u8(a.data) + u64(a.element_size)
551+
}
552+
a.offset += a.element_size
553+
a.len--
554+
a.cap--
555+
return first_elem
556+
}
557+
514558
// pop returns the last element of the array, and removes it.
515559
// If the `array` is empty, this will panic.
516560
// NOTE: this function reduces the length of the given array,

‎vlib/builtin/array_d_gcboehm_opt.v‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,21 @@ fn (mut a array) prepend_many_noscan(val voidptr, size int) {
216216
unsafe { a.insert_many_noscan(0, val, size) }
217217
}
218218

219+
// pop_left returns the first element of the array and removes it by advancing the data pointer.
220+
fn (mut a array) pop_left_noscan() voidptr {
221+
if a.len == 0 {
222+
panic('array.pop_left: array is empty')
223+
}
224+
first_elem := a.data
225+
unsafe {
226+
a.data = &u8(a.data) + u64(a.element_size)
227+
}
228+
a.offset += a.element_size
229+
a.len--
230+
a.cap--
231+
return unsafe { memdup_noscan(first_elem, a.element_size) }
232+
}
233+
219234
// pop returns the last element of the array, and removes it.
220235
fn (mut a array) pop_noscan() voidptr {
221236
// in a sense, this is the opposite of `a << x`

‎vlib/builtin/array_test.v‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,40 @@ fn test_reverse_in_place() {
11971197
assert c == [[5, 6], [3, 4], [1, 2]]
11981198
}
11991199

1200+
fn test_array_int_pop_left() {
1201+
mut a := [1, 2, 3, 4, 5]
1202+
b := unsafe { a[..5] } // full slice view
1203+
assert a.len == 5
1204+
first := a[0]
1205+
x := a.pop_left()
1206+
assert first == x
1207+
assert a.len == 4
1208+
assert a.cap == 4
1209+
y := a.pop_left()
1210+
assert y == 2
1211+
a[0] = 100
1212+
// NOTE: update a[0] also update b[2]
1213+
assert b == [1, 2, 100, 4, 5]
1214+
1215+
mut one_elem := [1]
1216+
one := one_elem.pop_left()
1217+
assert one_elem.len == 0
1218+
assert one_elem.cap == 0
1219+
assert one == 1
1220+
}
1221+
1222+
fn test_array_string_pop_left() {
1223+
mut a := ['abc', 'def', 'xyz']
1224+
assert a.len == 3
1225+
x := a.first()
1226+
y := a.pop_left()
1227+
assert x == y
1228+
assert a.pop_left() == 'def'
1229+
assert a.pop_left() == 'xyz'
1230+
assert a.len == 0
1231+
assert a.cap == 0
1232+
}
1233+
12001234
fn test_array_int_pop() {
12011235
mut a := [1, 2, 3, 4, 5]
12021236
assert a.len == 5

‎vlib/v/ast/table.v‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub mut:
1919
arr_insert bool // arr.insert()
2020
arr_first bool // arr.first()
2121
arr_last bool // arr.last()
22+
arr_pop_left bool // arr.pop_left()
2223
arr_pop bool // arr.pop()
2324
arr_delete bool // arr.delete()
2425
arr_reverse bool // arr.reverse()

‎vlib/v/checker/checker.v‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const generic_fn_postprocess_iterations_cutoff_limit = 1_000_000
3030
// Note that methods that do not return anything, or that return known types, are not listed here, since they are just ordinary non generic methods.
3131
pub const array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort',
3232
'sort_with_compare', 'sorted', 'sorted_with_compare', 'contains', 'index', 'wait', 'any', 'all',
33-
'first', 'last', 'pop', 'delete', 'insert', 'prepend', 'count']
33+
'first', 'last', 'pop_left', 'pop', 'delete', 'insert', 'prepend', 'count']
3434
pub const array_builtin_methods_chk = token.new_keywords_matcher_from_array_trie(array_builtin_methods)
3535
pub const fixed_array_builtin_methods = ['contains', 'index', 'any', 'all', 'wait', 'map', 'sort',
3636
'sorted', 'sort_with_compare', 'sorted_with_compare', 'reverse', 'reverse_in_place', 'count']

‎vlib/v/checker/fn.v‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3506,13 +3506,13 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
35063506
node.args[i].typ = c.expr(mut arg.expr)
35073507
}
35083508
node.return_type = ast.int_type
3509-
} else if method_name in ['first', 'last', 'pop'] {
3509+
} else if method_name in ['first', 'last', 'pop_left', 'pop'] {
35103510
c.markused_array_method(!c.is_builtin_mod, method_name)
35113511
if node_args_len != 0 {
35123512
c.error('`.${method_name}()` does not have any arguments', arg0.pos)
35133513
}
35143514
node.return_type = array_info.elem_type
3515-
if method_name == 'pop' {
3515+
if method_name in ['pop_left', 'pop'] {
35163516
c.check_for_mut_receiver(mut node.left)
35173517
node.receiver_type = node.left_type.ref()
35183518
} else {

‎vlib/v/checker/used_features.v‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ fn (mut c Checker) markused_array_method(check bool, method_name string) {
201201
'last' {
202202
c.table.used_features.arr_last = true
203203
}
204+
'pop_left' {
205+
c.table.used_features.arr_pop_left = true
206+
}
204207
'pop' {
205208
c.table.used_features.arr_pop = true
206209
}

‎vlib/v/gen/c/fn.v‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,15 +1207,15 @@ fn (mut g Gen) gen_array_method_call(node ast.CallExpr, left_type ast.Type, left
12071207
g.expr(node.args[0].expr)
12081208
g.write(')')
12091209
}
1210-
'first', 'last', 'pop' {
1210+
'first', 'last', 'pop_left', 'pop' {
12111211
mut noscan := ''
12121212
array_info := left_sym.info as ast.Array
1213-
if node.name == 'pop' {
1213+
if node.name in ['pop_left', 'pop'] {
12141214
noscan = g.check_noscan(array_info.elem_type)
12151215
}
12161216
return_type_str := g.styp(node.return_type)
12171217
g.write('(*(${return_type_str}*)array_${node.name}${noscan}(')
1218-
if node.name == 'pop' {
1218+
if node.name in ['pop_left', 'pop'] {
12191219
g.gen_arg_from_type(left_type, node.left)
12201220
} else {
12211221
if node.left_type.is_ptr() {
@@ -1475,7 +1475,7 @@ fn (mut g Gen) resolve_receiver_name(node ast.CallExpr, unwrapped_rec_type ast.T
14751475
receiver_type_name = 'map'
14761476
}
14771477
if final_left_sym.kind == .array && !(left_sym.kind == .alias && left_sym.has_method(node.name))
1478-
&& node.name in ['clear', 'repeat', 'sort_with_compare', 'sorted_with_compare', 'push_many', 'trim', 'first', 'last', 'pop', 'clone', 'reverse', 'slice', 'pointers'] {
1478+
&& node.name in ['clear', 'repeat', 'sort_with_compare', 'sorted_with_compare', 'push_many', 'trim', 'first', 'last', 'pop_left', 'pop', 'clone', 'reverse', 'slice', 'pointers'] {
14791479
if !(left_sym.info is ast.Alias && typ_sym.has_method(node.name)) {
14801480
// `array_Xyz_clone` => `array_clone`
14811481
receiver_type_name = 'array'

‎vlib/v/gen/js/fn.v‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ fn (mut g JsGen) method_call(node ast.CallExpr) {
319319
}
320320

321321
if node.name in ['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', 'first', 'last',
322-
'pop', 'clone', 'reverse', 'slice', 'pointers'] {
322+
'pop_left', 'pop', 'clone', 'reverse', 'slice', 'pointers'] {
323323
if !(left_sym.info is ast.Alias && typ_sym.has_method(node.name)) {
324324
// `array_Xyz_clone` => `array_clone`
325325
receiver_type_name = 'array'

‎vlib/v/markused/markused.v‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
8888
if table.used_features.arr_reverse {
8989
core_fns << array_idx_str + '.reverse'
9090
}
91+
if table.used_features.arr_pop_left {
92+
core_fns << ref_array_idx_str + '.pop_left'
93+
core_fns << ref_array_idx_str + '.pop_left_noscan'
94+
}
9195
if table.used_features.arr_pop {
9296
core_fns << ref_array_idx_str + '.pop'
9397
core_fns << ref_array_idx_str + '.pop_noscan'

0 commit comments

Comments
 (0)