Skip to content

Commit

Permalink
checker, cgen: implement fixed array index()
Browse files Browse the repository at this point in the history
  • Loading branch information
yuyi98 committed Oct 20, 2024
1 parent 555bad7 commit b2baa19
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 40 deletions.
35 changes: 35 additions & 0 deletions vlib/builtin/fixed_array_index_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
fn test_index_of_ints() {
ia := [1, 2, 3]!
ii := ia.index(2)
dump(ii)
assert ii == 1
}

fn test_index_of_strings() {
sa := ['a', 'b', 'c']!
si := sa.index('b')
dump(si)
assert si == 1
}

fn test_index_of_voidptrs() {
pa := [voidptr(123), voidptr(45), voidptr(99)]!
pi := pa.index(voidptr(45))
dump(pi)
assert pi == 1
}

//////////

fn a() {}

fn b() {}

fn c() {}

fn test_index_of_fns() {
fa := [a, b, c]!
fi := fa.index(b)
dump(fi)
assert fi == 1
}
31 changes: 31 additions & 0 deletions vlib/v/checker/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -2064,6 +2064,9 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
if final_left_sym.kind == .array && array_builtin_methods_chk.matches(method_name)
&& !(left_sym.kind == .alias && left_sym.has_method(method_name)) {
return c.array_builtin_method_call(mut node, left_type)
} else if final_left_sym.kind == .array_fixed && method_name in ['index', 'all', 'any', 'map']
&& !(left_sym.kind == .alias && left_sym.has_method(method_name)) {
return c.fixed_array_builtin_method_call(mut node, left_type)
} else if final_left_sym.kind == .map
&& method_name in ['clone', 'keys', 'values', 'move', 'delete'] && !(left_sym.kind == .alias
&& left_sym.has_method(method_name)) {
Expand Down Expand Up @@ -3466,6 +3469,34 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
return node.return_type
}

fn (mut c Checker) fixed_array_builtin_method_call(mut node ast.CallExpr, left_type ast.Type) ast.Type {
left_sym := c.table.final_sym(left_type)
method_name := node.name
unwrapped_left_type := c.unwrap_generic(left_type)
unaliased_left_type := c.table.unaliased_type(unwrapped_left_type)
array_info := if left_sym.info is ast.ArrayFixed {
left_sym.info as ast.ArrayFixed
} else {
c.table.sym(unaliased_left_type).info as ast.ArrayFixed
}
elem_typ := array_info.elem_type
if method_name == 'index' {
if node.args.len != 1 {
c.error('`.index()` expected 1 argument, but got ${node.args.len}', node.pos)
} else if !left_sym.has_method('index') {
arg_typ := c.expr(mut node.args[0].expr)
c.check_expected_call_arg(arg_typ, elem_typ, node.language, node.args[0]) or {
c.error('${err.msg()} in argument 1 to `.index()`', node.args[0].pos)
}
}
for i, mut arg in node.args {
node.args[i].typ = c.expr(mut arg.expr)
}
node.return_type = ast.int_type
}
return node.return_type
}

fn (mut c Checker) check_for_mut_receiver(mut expr ast.Expr) (string, token.Pos) {
to_lock, pos := c.fail_if_immutable(mut expr)
if !expr.is_lvalue() {
Expand Down
131 changes: 91 additions & 40 deletions vlib/v/gen/c/array.v
Original file line number Diff line number Diff line change
Expand Up @@ -1096,50 +1096,96 @@ fn (mut g Gen) gen_array_index_methods() {
final_left_sym := g.table.final_sym(t)
mut left_type_str := g.typ(t)
fn_name := '${left_type_str}_index'
info := final_left_sym.info as ast.Array
mut elem_type_str := g.typ(info.elem_type)
elem_sym := g.table.sym(info.elem_type)
if elem_sym.kind == .function {
left_type_str = 'Array_voidptr'
elem_type_str = 'voidptr'
}
g.type_definitions.writeln('static int ${fn_name}(${left_type_str} a, ${elem_type_str} v); // auto')
mut fn_builder := strings.new_builder(512)
fn_builder.writeln('static int ${fn_name}(${left_type_str} a, ${elem_type_str} v) {')
fn_builder.writeln('\t${elem_type_str}* pelem = a.data;')
fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i, ++pelem) {')
if elem_sym.kind == .string {
fn_builder.writeln('\t\tif (fast_string_eq(*pelem, v)) {')
} else if elem_sym.kind in [.array, .array_fixed] && !info.elem_type.is_ptr() {
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq(*pelem, v)) {')
} else if elem_sym.kind == .function && !info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif ( *pelem == v) {')
} else if elem_sym.kind == .map && !info.elem_type.is_ptr() {
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_map_eq((*pelem, v))) {')
} else if elem_sym.kind == .struct && !info.elem_type.is_ptr() {
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq(*pelem, v)) {')
} else if elem_sym.kind == .interface {
ptr_typ := g.equality_fn(info.elem_type)
if info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif (${ptr_typ}_interface_eq(**pelem, *v)) {')

if final_left_sym.kind == .array {
info := final_left_sym.info as ast.Array
mut elem_type_str := g.typ(info.elem_type)
elem_sym := g.table.sym(info.elem_type)
if elem_sym.kind == .function {
left_type_str = 'Array_voidptr'
elem_type_str = 'voidptr'
}
g.type_definitions.writeln('static int ${fn_name}(${left_type_str} a, ${elem_type_str} v); // auto')
fn_builder.writeln('static int ${fn_name}(${left_type_str} a, ${elem_type_str} v) {')
fn_builder.writeln('\t${elem_type_str}* pelem = a.data;')
fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i, ++pelem) {')
if elem_sym.kind == .string {
fn_builder.writeln('\t\tif (fast_string_eq(*pelem, v)) {')
} else if elem_sym.kind in [.array, .array_fixed] && !info.elem_type.is_ptr() {
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq(*pelem, v)) {')
} else if elem_sym.kind == .function && !info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif ( *pelem == v) {')
} else if elem_sym.kind == .map && !info.elem_type.is_ptr() {
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_map_eq((*pelem, v))) {')
} else if elem_sym.kind == .struct && !info.elem_type.is_ptr() {
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq(*pelem, v)) {')
} else if elem_sym.kind == .interface {
ptr_typ := g.equality_fn(info.elem_type)
if info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif (${ptr_typ}_interface_eq(**pelem, *v)) {')
} else {
fn_builder.writeln('\t\tif (${ptr_typ}_interface_eq(*pelem, v)) {')
}
} else if elem_sym.kind == .sum_type {
ptr_typ := g.equality_fn(info.elem_type)
if info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif (${ptr_typ}_sumtype_eq(**pelem, *v)) {')
} else {
fn_builder.writeln('\t\tif (${ptr_typ}_sumtype_eq(*pelem, v)) {')
}
} else if elem_sym.kind == .alias {
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_alias_eq(*pelem, v)) {')
} else {
fn_builder.writeln('\t\tif (${ptr_typ}_interface_eq(*pelem, v)) {')
fn_builder.writeln('\t\tif (*pelem == v) {')
}
} else if final_left_sym.kind == .array_fixed {
info := final_left_sym.info as ast.ArrayFixed
mut elem_type_str := g.typ(info.elem_type)
elem_sym := g.table.sym(info.elem_type)
if elem_sym.kind == .function {
elem_type_str = 'voidptr'
}
} else if elem_sym.kind == .sum_type {
ptr_typ := g.equality_fn(info.elem_type)
if info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif (${ptr_typ}_sumtype_eq(**pelem, *v)) {')
g.type_definitions.writeln('static int ${fn_name}(${left_type_str} a, ${elem_type_str} v); // auto')
fn_builder.writeln('static int ${fn_name}(${left_type_str} a, ${elem_type_str} v) {')
fn_builder.writeln('\tfor (int i = 0; i < ${info.size}; ++i) {')
if elem_sym.kind == .string {
fn_builder.writeln('\t\tif (fast_string_eq(a[i], v)) {')
} else if elem_sym.kind in [.array, .array_fixed] && !info.elem_type.is_ptr() {
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq(a[i], v)) {')
} else if elem_sym.kind == .function && !info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif (a[i] == v) {')
} else if elem_sym.kind == .map && !info.elem_type.is_ptr() {
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_map_eq((a[i], v))) {')
} else if elem_sym.kind == .struct && !info.elem_type.is_ptr() {
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq(a[i], v)) {')
} else if elem_sym.kind == .interface {
ptr_typ := g.equality_fn(info.elem_type)
if info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif (${ptr_typ}_interface_eq(*a[i], *v)) {')
} else {
fn_builder.writeln('\t\tif (${ptr_typ}_interface_eq(a[i], v)) {')
}
} else if elem_sym.kind == .sum_type {
ptr_typ := g.equality_fn(info.elem_type)
if info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif (${ptr_typ}_sumtype_eq(*a[i], *v)) {')
} else {
fn_builder.writeln('\t\tif (${ptr_typ}_sumtype_eq(a[i], v)) {')
}
} else if elem_sym.kind == .alias {
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_alias_eq(a[i], v)) {')
} else {
fn_builder.writeln('\t\tif (${ptr_typ}_sumtype_eq(*pelem, v)) {')
fn_builder.writeln('\t\tif (a[i] == v) {')
}
} else if elem_sym.kind == .alias {
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_alias_eq(*pelem, v)) {')
} else {
fn_builder.writeln('\t\tif (*pelem == v) {')
}
fn_builder.writeln('\t\t\treturn i;')
fn_builder.writeln('\t\t}')
Expand All @@ -1160,7 +1206,12 @@ fn (mut g Gen) gen_array_index(node ast.CallExpr) {
g.expr(node.left)
g.write(', ')

elem_typ := g.table.sym(node.left_type).array_info().elem_type
left_sym := g.table.final_sym(node.left_type)
elem_typ := if left_sym.kind == .array {
left_sym.array_info().elem_type
} else {
left_sym.array_fixed_info().elem_type
}
// auto deref var is redundant for interfaces and sum types.
if node.args[0].expr.is_auto_deref_var()
&& g.table.sym(elem_typ).kind !in [.interface, .sum_type] {
Expand Down
18 changes: 18 additions & 0 deletions vlib/v/gen/c/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,18 @@ fn (mut g Gen) gen_array_method_call(node ast.CallExpr, left_type ast.Type, left
return true
}

fn (mut g Gen) gen_fixed_array_method_call(node ast.CallExpr, left_type ast.Type, left_sym ast.TypeSymbol) bool {
match node.name {
'index' {
g.gen_array_index(node)
}
else {
return false
}
}
return true
}

fn (mut g Gen) gen_to_str_method_call(node ast.CallExpr) bool {
mut rec_type := node.receiver_type
if rec_type.has_flag(.shared_f) {
Expand Down Expand Up @@ -1680,6 +1692,12 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
return
}
}
if final_left_sym.kind == .array_fixed && !(left_sym.kind == .alias
&& left_sym.has_method(node.name)) {
if g.gen_fixed_array_method_call(node, left_type, final_left_sym) {
return
}
}
if final_left_sym.kind == .map && !(left_sym.kind == .alias && left_sym.has_method(node.name)) {
if g.gen_map_method_call(node, left_type, final_left_sym) {
return
Expand Down
9 changes: 9 additions & 0 deletions vlib/v/tests/builtin_arrays/fixed_array_literal_infix_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,12 @@ fn test_array_of_fixed_array_index() {
println(ret)
assert ret == 0
}

fn test_fixed_array_of_fixed_array_index() {
mut a := [2][2]int{}
a[0] = [1, 2]!
println(a.index([1, 2]!))
ret := a.index([1, 2]!)
println(ret)
assert ret == 0
}

0 comments on commit b2baa19

Please sign in to comment.