Skip to content

Commit

Permalink
cgen: fix comparing interface values of structs containing optional r…
Browse files Browse the repository at this point in the history
…ef interface fields (fix #20212) (#20221)
  • Loading branch information
shove70 committed Dec 20, 2023
1 parent ecb1dc1 commit 85c5c86
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 47 deletions.
49 changes: 6 additions & 43 deletions vlib/v/gen/c/auto_eq_methods.v
Expand Up @@ -130,17 +130,7 @@ fn (mut g Gen) read_field(struct_type ast.Type, field_name string, var_name stri
@[inline]
fn (mut g Gen) read_opt_field(struct_type ast.Type, field_name string, var_name string, field_typ ast.Type) string {
return if field_typ.has_flag(.option) {
field_typ_ := if g.table.sym(field_typ).kind in [.interface_, .string] && field_typ.is_ptr() {
field_typ.deref()
} else {
field_typ
}
opt := if g.table.sym(field_typ).kind == .interface_ && field_typ.is_ptr() {
'_option_'
} else {
''
}
'*(${opt}${g.base_type(field_typ_)}*)${g.read_field(struct_type, field_name, var_name)}.data'
'*(${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)
}
Expand Down Expand Up @@ -215,38 +205,11 @@ fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string {
fn_builder.write_string('${eq_fn}_alias_eq(${left_arg}, ${right_arg})')
} else if field_type.sym.kind == .function && !field.typ.has_flag(.option) {
fn_builder.write_string('*((voidptr*)(${left_arg})) == *((voidptr*)(${right_arg}))')
} else if field_type.sym.kind == .interface_ {
if field.typ.has_flag(.option) {
field_typ_ := if field.typ.is_ptr() {
field.typ.deref()
} else {
field.typ
}
if field.typ.is_ptr() {
left_arg_opt := g.read_opt_field(left_type, field_name, 'a', field.typ)
right_arg_opt := g.read_opt_field(left_type, field_name, 'b',
field.typ)
ptr := if field_typ_.is_ptr() {
'*'.repeat(field_typ_.nr_muls())
} else {
''
}
eq_fn := g.gen_interface_equality_fn(field_typ_)
fn_builder.write_string('${eq_fn}_interface_eq(${ptr}${left_arg_opt}, ${ptr}${right_arg_opt})')
} else {
ptr := if field_typ_.is_ptr() {
'*'.repeat(field_typ_.nr_muls())
} else {
''
}
eq_fn := g.gen_interface_equality_fn(field_typ_)
fn_builder.write_string('${eq_fn}_interface_eq(${ptr}${left_arg}, ${ptr}${right_arg})')
}
} else {
ptr := if field.typ.is_ptr() { '*'.repeat(field.typ.nr_muls()) } else { '' }
eq_fn := g.gen_interface_equality_fn(field.typ)
fn_builder.write_string('${eq_fn}_interface_eq(${ptr}${left_arg}, ${ptr}${right_arg})')
}
} else if field_type.sym.kind == .interface_
&& (!field.typ.has_flag(.option) || !field.typ.is_ptr()) {
ptr := if field.typ.is_ptr() { '*'.repeat(field.typ.nr_muls()) } else { '' }
eq_fn := g.gen_interface_equality_fn(field.typ)
fn_builder.write_string('${eq_fn}_interface_eq(${ptr}${left_arg}, ${ptr}${right_arg})')
} else if field.typ.has_flag(.option) {
fn_builder.write_string('${left_arg}.state == ${right_arg}.state && !memcmp(&${left_arg}.data, &${right_arg}.data, sizeof(${g.base_type(field.typ)}))')
} else {
Expand Down
25 changes: 21 additions & 4 deletions vlib/v/tests/interface_eq_methods_with_option_and_ref_test.v
@@ -1,21 +1,38 @@
// for issue 19441
// for issue 19441, 20212.
// The issue 19441 manifests itself in the following way:
// when the Mixin struct is present in the code, it causes a cgen error,
// and when the Mixin struct is not included, the eq method results causes the final assertion to fail
pub interface Iface {}

pub struct Derived {}
// test ref
pub struct Derived {
field1 &Iface = unsafe { nil }
}

// test option and ref
pub struct Struct {
field ?&Iface
field2 ?&Iface
}

// test non-ref and embeded
pub struct Mixin {
Derived
Struct
field3 Iface = Iface(1)
field4 ?Iface
}

fn test_main() {
mut arr := []&Iface{}
arr << &Derived{}
arr << &Derived{}

assert arr[0] == arr[1]

s1 := Iface(Struct{})
s2 := Iface(Struct{})
assert s1 == s2

i1 := Iface(Mixin{})
i2 := Iface(Mixin{})
assert i1 == i2
}

0 comments on commit 85c5c86

Please sign in to comment.