mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
checker, cgen: allow iterating over optional array fields (#16858)
This commit is contained in:
@@ -154,7 +154,7 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
|
|||||||
if sym.kind == .string {
|
if sym.kind == .string {
|
||||||
value_type = ast.u8_type
|
value_type = ast.u8_type
|
||||||
}
|
}
|
||||||
if value_type == ast.void_type || typ.has_flag(.optional) || typ.has_flag(.result) {
|
if value_type == ast.void_type || typ.has_flag(.result) {
|
||||||
if typ != ast.void_type {
|
if typ != ast.void_type {
|
||||||
c.error('for in: cannot index `${c.table.type_to_str(typ)}`', node.cond.pos())
|
c.error('for in: cannot index `${c.table.type_to_str(typ)}`', node.cond.pos())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4218,6 +4218,11 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) is_comptime_var(node ast.Expr) bool {
|
||||||
|
return g.inside_comptime_for_field && node is ast.Ident
|
||||||
|
&& (node as ast.Ident).info is ast.IdentVar && ((node as ast.Ident).obj as ast.Var).is_comptime_field
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut g Gen) ident(node ast.Ident) {
|
fn (mut g Gen) ident(node ast.Ident) {
|
||||||
prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once
|
prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once
|
||||||
g.prevent_sum_type_unwrapping_once = false
|
g.prevent_sum_type_unwrapping_once = false
|
||||||
|
|||||||
@@ -132,11 +132,15 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) {
|
|||||||
mut node := unsafe { node_ }
|
mut node := unsafe { node_ }
|
||||||
if node.kind == .any {
|
if node.kind == .any {
|
||||||
g.inside_for_in_any_cond = true
|
g.inside_for_in_any_cond = true
|
||||||
unwrapped_typ := g.unwrap_generic(node.cond_type)
|
mut unwrapped_typ := g.unwrap_generic(node.cond_type)
|
||||||
unwrapped_sym := g.table.sym(unwrapped_typ)
|
mut unwrapped_sym := g.table.sym(unwrapped_typ)
|
||||||
node.kind = unwrapped_sym.kind
|
node.kind = unwrapped_sym.kind
|
||||||
node.cond_type = unwrapped_typ
|
node.cond_type = unwrapped_typ
|
||||||
if node.key_var.len > 0 {
|
if node.key_var.len > 0 {
|
||||||
|
if g.is_comptime_var(node.cond) {
|
||||||
|
unwrapped_typ = g.unwrap_generic(g.comptime_for_field_type)
|
||||||
|
unwrapped_sym = g.table.sym(unwrapped_typ)
|
||||||
|
}
|
||||||
key_type := match unwrapped_sym.kind {
|
key_type := match unwrapped_sym.kind {
|
||||||
.map { unwrapped_sym.map_info().key_type }
|
.map { unwrapped_sym.map_info().key_type }
|
||||||
else { ast.int_type }
|
else { ast.int_type }
|
||||||
@@ -163,10 +167,20 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) {
|
|||||||
} else if node.kind == .array {
|
} else if node.kind == .array {
|
||||||
// `for num in nums {`
|
// `for num in nums {`
|
||||||
// g.writeln('// FOR IN array')
|
// g.writeln('// FOR IN array')
|
||||||
styp := g.typ(node.val_type)
|
mut styp := g.typ(node.val_type)
|
||||||
val_sym := g.table.sym(node.val_type)
|
mut val_sym := g.table.sym(node.val_type)
|
||||||
|
|
||||||
|
if g.is_comptime_var(node.cond) {
|
||||||
|
unwrapped_typ := g.unwrap_generic(g.comptime_for_field_type)
|
||||||
|
val_sym = g.table.sym(unwrapped_typ)
|
||||||
|
node.val_type = g.table.value_type(unwrapped_typ)
|
||||||
|
styp = g.typ(node.val_type)
|
||||||
|
node.scope.update_var_type(node.val_var, node.val_type)
|
||||||
|
node.cond_type = node.val_type
|
||||||
|
}
|
||||||
mut cond_var := ''
|
mut cond_var := ''
|
||||||
if node.cond is ast.Ident || node.cond is ast.SelectorExpr {
|
if (node.cond is ast.Ident && !node.cond_type.has_flag(.optional))
|
||||||
|
|| node.cond is ast.SelectorExpr {
|
||||||
cond_var = g.expr_string(node.cond)
|
cond_var = g.expr_string(node.cond)
|
||||||
} else {
|
} else {
|
||||||
cond_var = g.new_tmp_var()
|
cond_var = g.new_tmp_var()
|
||||||
@@ -180,7 +194,13 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) {
|
|||||||
share_accessor := if node.cond_type.share() == .shared_t { 'val.' } else { '' }
|
share_accessor := if node.cond_type.share() == .shared_t { 'val.' } else { '' }
|
||||||
op_field := field_accessor + share_accessor
|
op_field := field_accessor + share_accessor
|
||||||
g.empty_line = true
|
g.empty_line = true
|
||||||
g.writeln('for (int ${i} = 0; ${i} < ${cond_var}${op_field}len; ++${i}) {')
|
opt_expr := '(*(${g.typ(node.cond_type.clear_flag(.optional))}*)${cond_var}${op_field}data)'
|
||||||
|
cond_expr := if node.cond_type.has_flag(.optional) {
|
||||||
|
'/*opt*/ ${opt_expr}${op_field}len'
|
||||||
|
} else {
|
||||||
|
'${cond_var}${op_field}len'
|
||||||
|
}
|
||||||
|
g.writeln('for (int ${i} = 0; ${i} < ${cond_expr}; ++${i}) {')
|
||||||
if node.val_var != '_' {
|
if node.val_var != '_' {
|
||||||
if val_sym.kind == .function {
|
if val_sym.kind == .function {
|
||||||
g.write('\t')
|
g.write('\t')
|
||||||
@@ -196,7 +216,9 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) {
|
|||||||
// instead of
|
// instead of
|
||||||
// `int* val = ((int**)arr.data)[i];`
|
// `int* val = ((int**)arr.data)[i];`
|
||||||
// right := if node.val_is_mut { styp } else { styp + '*' }
|
// right := if node.val_is_mut { styp } else { styp + '*' }
|
||||||
right := if node.val_is_mut || node.val_is_ref {
|
right := if node.cond_type.has_flag(.optional) {
|
||||||
|
'/*opt*/ ((${styp}*)${opt_expr}${op_field}data)[${i}]'
|
||||||
|
} else if node.val_is_mut || node.val_is_ref {
|
||||||
'((${styp})${cond_var}${op_field}data) + ${i}'
|
'((${styp})${cond_var}${op_field}data) + ${i}'
|
||||||
} else {
|
} else {
|
||||||
'((${styp}*)${cond_var}${op_field}data)[${i}]'
|
'((${styp}*)${cond_var}${op_field}data)[${i}]'
|
||||||
|
|||||||
44
vlib/v/tests/for_loop_with_optional2_test.v
Normal file
44
vlib/v/tests/for_loop_with_optional2_test.v
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
struct Test {
|
||||||
|
a ?[]int
|
||||||
|
b ?[]string
|
||||||
|
c ?[]f64
|
||||||
|
d ?[][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_for_in_optional_fields() {
|
||||||
|
mut out := []string{}
|
||||||
|
test := Test{
|
||||||
|
a: [1, 2, 3]
|
||||||
|
b: ['foo', 'bar']
|
||||||
|
c: [1.2, 2.3]
|
||||||
|
d: [['foo'], ['bar']]
|
||||||
|
}
|
||||||
|
//
|
||||||
|
for element in test.a {
|
||||||
|
out << '${element}'
|
||||||
|
}
|
||||||
|
dump(out)
|
||||||
|
assert out == ['1', '2', '3']
|
||||||
|
out.clear()
|
||||||
|
//
|
||||||
|
for element in test.b {
|
||||||
|
out << '${element}'
|
||||||
|
}
|
||||||
|
dump(out)
|
||||||
|
assert out == ['foo', 'bar']
|
||||||
|
out.clear()
|
||||||
|
//
|
||||||
|
for element in test.c {
|
||||||
|
out << '${element}'
|
||||||
|
}
|
||||||
|
dump(out)
|
||||||
|
assert out == ['1.2', '2.3']
|
||||||
|
out.clear()
|
||||||
|
//
|
||||||
|
for element in test.d {
|
||||||
|
out << '${element}'
|
||||||
|
}
|
||||||
|
dump(out)
|
||||||
|
assert out == ["['foo']", "['bar']"]
|
||||||
|
out.clear()
|
||||||
|
}
|
||||||
35
vlib/v/tests/for_loop_with_optional_test.v
Normal file
35
vlib/v/tests/for_loop_with_optional_test.v
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
struct Test {
|
||||||
|
a ?[]int
|
||||||
|
b ?[]string
|
||||||
|
c ?[]f64
|
||||||
|
d ?[][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_loop[U](val U, field_name string) []string {
|
||||||
|
mut out := []string{}
|
||||||
|
$for field in U.fields {
|
||||||
|
variable := val.$(field.name)
|
||||||
|
if field_name == field.name {
|
||||||
|
for element in variable {
|
||||||
|
println(element)
|
||||||
|
out << element.str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_main() {
|
||||||
|
test := Test{
|
||||||
|
a: [1, 2, 3]
|
||||||
|
b: ['foo', 'bar']
|
||||||
|
c: [1.2, 2.3]
|
||||||
|
d: [['foo'], ['bar']]
|
||||||
|
}
|
||||||
|
|
||||||
|
// println(run_loop(test, 'a'))
|
||||||
|
assert run_loop(test, 'a').str() == "['1', '2', '3']"
|
||||||
|
assert run_loop(test, 'b').str() == "['foo', 'bar']"
|
||||||
|
assert run_loop(test, 'c').str() == "['1.2', '2.3']"
|
||||||
|
assert run_loop(test, 'd').str() == "['['foo']', '['bar']']"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user