Skip to content

Commit

Permalink
cgen: fix if guard stmts return generated code + fix auto generated o…
Browse files Browse the repository at this point in the history
…ption map comparison code (#20169)
  • Loading branch information
felipensp committed Dec 25, 2023
1 parent 55061e4 commit 8e47c21
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 19 deletions.
73 changes: 55 additions & 18 deletions vlib/v/gen/c/auto_eq_methods.v
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,26 @@ fn (mut g Gen) read_field(struct_type ast.Type, field_name string, var_name stri
}
}

// read_map generates C code for reading option/no-option struct field
@[inline]
fn (mut g Gen) read_map_from_option(typ ast.Type, var_name string) string {
return if typ.has_flag(.option) {
return '(${g.base_type(typ)}*)&${var_name}.data'
} else {
var_name
}
}

// read_map_field generates C code for reading option/no-option struct field
@[inline]
fn (mut g Gen) read_map_field_from_option(typ ast.Type, field_name string, var_name string) string {
return if typ.has_flag(.option) {
'(*(${g.base_type(typ)}*)${var_name}.data).${field_name}'
} else {
'${var_name}.${field_name}'
}
}

// read_opt_field generates C code for reading option/no-option struct field
@[inline]
fn (mut g Gen) read_opt_field(struct_type ast.Type, field_name string, var_name string, field_typ ast.Type) string {
Expand All @@ -136,6 +156,16 @@ fn (mut g Gen) read_opt_field(struct_type ast.Type, field_name string, var_name
}
}

// read_map_opt_field generates C code for reading option/no-option map field
@[inline]
fn (mut g Gen) read_map_opt_field(struct_type ast.Type, field_name string, var_name string, field_typ ast.Type) string {
return if field_typ.has_flag(.option) {
'*(${g.base_type(field_typ)}*)${g.read_field(struct_type, field_name, var_name)}.data'
} else {
g.read_field(struct_type, field_name, var_name)
}
}

fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string {
left := g.unwrap(left_type)
ptr_styp := g.typ(left.typ.set_nr_muls(0))
Expand Down Expand Up @@ -429,64 +459,71 @@ fn (mut g Gen) gen_map_equality_fn(left_type ast.Type) string {
ptr_value_styp := g.typ(value.typ)
g.definitions.writeln('static bool ${ptr_styp}_map_eq(${ptr_styp} a, ${ptr_styp} b); // auto')

left_len := g.read_map_field_from_option(left.typ, 'len', 'a')
right_len := g.read_map_field_from_option(left.typ, 'len', 'b')
key_values := g.read_map_field_from_option(left.typ, 'key_values', 'a')

a := if left.typ.has_flag(.option) { g.read_map_from_option(left.typ, 'a') } else { '&a' }
b := if left.typ.has_flag(.option) { g.read_map_from_option(left.typ, 'b') } else { '&b' }

mut fn_builder := strings.new_builder(512)
fn_builder.writeln('static bool ${ptr_styp}_map_eq(${ptr_styp} a, ${ptr_styp} b) {')
fn_builder.writeln('\tif (a.len != b.len) {')
fn_builder.writeln('\tif (${left_len} != ${right_len}) {')
fn_builder.writeln('\t\treturn false;')
fn_builder.writeln('\t}')
fn_builder.writeln('\tfor (int i = 0; i < a.key_values.len; ++i) {')
fn_builder.writeln('\t\tif (!DenseArray_has_index(&a.key_values, i)) continue;')
fn_builder.writeln('\t\tvoidptr k = DenseArray_key(&a.key_values, i);')
fn_builder.writeln('\t\tif (!map_exists(&b, k)) return false;')
fn_builder.writeln('\tfor (int i = 0; i < ${key_values}.len; ++i) {')
fn_builder.writeln('\t\tif (!DenseArray_has_index(&${key_values}, i)) continue;')
fn_builder.writeln('\t\tvoidptr k = DenseArray_key(&${key_values}, i);')
fn_builder.writeln('\t\tif (!map_exists(${b}, k)) return false;')
kind := g.table.type_kind(value.typ)
if kind == .function {
info := value.sym.info as ast.FnType
sig := g.fn_var_signature(info.func.return_type, info.func.params.map(it.typ),
'v')
fn_builder.writeln('\t\t${sig} = *(voidptr*)map_get(&a, k, &(voidptr[]){ 0 });')
fn_builder.writeln('\t\t${sig} = *(voidptr*)map_get(${a}, k, &(voidptr[]){ 0 });')
} else {
fn_builder.writeln('\t\t${ptr_value_styp} v = *(${ptr_value_styp}*)map_get(&a, k, &(${ptr_value_styp}[]){ 0 });')
fn_builder.writeln('\t\t${ptr_value_styp} v = *(${ptr_value_styp}*)map_get(${a}, k, &(${ptr_value_styp}[]){ 0 });')
}
match kind {
.string {
fn_builder.writeln('\t\tif (!fast_string_eq(*(string*)map_get(&b, k, &(string[]){_SLIT("")}), v)) {')
fn_builder.writeln('\t\tif (!fast_string_eq(*(string*)map_get(${b}, k, &(string[]){_SLIT("")}), v)) {')
}
.sum_type {
eq_fn := g.gen_sumtype_equality_fn(value.typ)
fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq(*(${ptr_value_styp}*)map_get(&b, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq(*(${ptr_value_styp}*)map_get(${b}, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
}
.struct_ {
eq_fn := g.gen_struct_equality_fn(value.typ)
fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(*(${ptr_value_styp}*)map_get(&b, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(*(${ptr_value_styp}*)map_get(${b}, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
}
.interface_ {
eq_fn := g.gen_interface_equality_fn(value.typ)
fn_builder.writeln('\t\tif (!${eq_fn}_interface_eq(*(${ptr_value_styp}*)map_get(&b, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
fn_builder.writeln('\t\tif (!${eq_fn}_interface_eq(*(${ptr_value_styp}*)map_get(${b}, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
}
.array {
eq_fn := g.gen_array_equality_fn(value.typ)
fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(*(${ptr_value_styp}*)map_get(&b, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(*(${ptr_value_styp}*)map_get(${b}, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
}
.array_fixed {
eq_fn := g.gen_fixed_array_equality_fn(value.typ)
fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(*(${ptr_value_styp}*)map_get(&b, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(*(${ptr_value_styp}*)map_get(${b}, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
}
.map {
eq_fn := g.gen_map_equality_fn(value.typ)
fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(*(${ptr_value_styp}*)map_get(&b, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(*(${ptr_value_styp}*)map_get(${b}, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
}
.alias {
eq_fn := g.gen_alias_equality_fn(value.typ)
fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(*(${ptr_value_styp}*)map_get(&b, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(*(${ptr_value_styp}*)map_get(${b}, k, &(${ptr_value_styp}[]){ 0 }), v)) {')
}
.function {
fn_builder.writeln('\t\tif (*(voidptr*)map_get(&b, k, &(voidptr[]){ 0 }) != v) {')
fn_builder.writeln('\t\tif (*(voidptr*)map_get(${b}, k, &(voidptr[]){ 0 }) != v) {')
}
else {
if value.typ.has_flag(.option) {
fn_builder.writeln('\t\tif (memcmp(v.data, ((${ptr_value_styp}*)map_get(&b, k, &(${ptr_value_styp}[]){ 0 }))->data, sizeof(${g.base_type(value.typ)})) != 0) {')
fn_builder.writeln('\t\tif (memcmp(v.data, ((${ptr_value_styp}*)map_get(${b}, k, &(${ptr_value_styp}[]){ 0 }))->data, sizeof(${g.base_type(value.typ)})) != 0) {')
} else {
fn_builder.writeln('\t\tif (*(${ptr_value_styp}*)map_get(&b, k, &(${ptr_value_styp}[]){ 0 }) != v) {')
fn_builder.writeln('\t\tif (*(${ptr_value_styp}*)map_get(${b}, k, &(${ptr_value_styp}[]){ 0 }) != v) {')
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -1819,7 +1819,8 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) bool {
g.expr(stmt.expr)
g.writeln(';')
} else {
ret_typ := if g.inside_assign {
// on assignemnt or struct field initialization
ret_typ := if g.inside_struct_init || g.inside_assign {
stmt.typ
} else {
g.fn_decl.return_type.clear_flag(.option)
Expand Down
41 changes: 41 additions & 0 deletions vlib/v/tests/option_if_option_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import maps
import x.json2

pub type Locale = string

pub struct ApplicationCommandOptionChoice {
pub:
name string
name_localizations ?map[Locale]string
}

pub fn ApplicationCommandOptionChoice.parse(j json2.Any) !ApplicationCommandOptionChoice {
match j {
map[string]json2.Any {
return ApplicationCommandOptionChoice{
name: j['name']! as string
name_localizations: if m := j['name_localizations'] {
maps.to_map[string, json2.Any, Locale, string](m as map[string]json2.Any,
fn (k string, v json2.Any) (Locale, string) {
return k, v as string
})
} else {
none
}
}
}
else {
return error('expected application command option choice to be object, got ${j.type_name()}')
}
}
}

fn test_main() {
var := ApplicationCommandOptionChoice.parse({
'name': json2.Any('foo')
'name_localizations': {
'name': json2.Any('foo')
}
})!
assert dump(var) == var
}

0 comments on commit 8e47c21

Please sign in to comment.