diff --git a/vlib/v/gen/c/auto_eq_methods.v b/vlib/v/gen/c/auto_eq_methods.v index 06cddc0dd8..7434191813 100644 --- a/vlib/v/gen/c/auto_eq_methods.v +++ b/vlib/v/gen/c/auto_eq_methods.v @@ -197,14 +197,14 @@ fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string { } else if field_type.sym.kind == .alias && !field.typ.is_ptr() { eq_fn := g.gen_alias_equality_fn(field.typ) fn_builder.write_string('${eq_fn}_alias_eq(${left_arg}, ${right_arg})') - } else if field_type.sym.kind == .function { + } 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_ { 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 left_type.has_flag(.option) { - fn_builder.write_string('!memcmp(&${left_arg}, &${right_arg}, sizeof(${g.base_type(field.typ)}))') + } 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 { fn_builder.write_string('${left_arg} == ${right_arg}') } @@ -230,37 +230,40 @@ fn (mut g Gen) gen_alias_equality_fn(left_type ast.Type) string { mut fn_builder := strings.new_builder(512) fn_builder.writeln('static bool ${ptr_styp}_alias_eq(${ptr_styp} a, ${ptr_styp} b) {') + is_option := info.parent_type.has_flag(.option) || left.typ.has_flag(.option) + + left_var := if is_option { '*' + g.read_opt(info.parent_type, 'a') } else { 'a' } + right_var := if is_option { '*' + g.read_opt(info.parent_type, 'b') } else { 'b' } + sym := g.table.sym(info.parent_type) if sym.kind == .string { - if info.parent_type.has_flag(.option) { - fn_builder.writeln('\treturn ((${g.read_opt(info.parent_type, 'a')})->len == (${g.read_opt(info.parent_type, - 'b')})->len && (${g.read_opt(info.parent_type, 'a')})->len == 0) || string__eq(*${g.read_opt(info.parent_type, - 'a')}, *${g.read_opt(info.parent_type, 'b')});') + if is_option { + fn_builder.writeln('\treturn ((${left_var}).len == (${right_var}).len && (${left_var}).len == 0) || string__eq(${left_var}, ${right_var});') } else { fn_builder.writeln('\treturn string__eq(a, b);') } } else if sym.kind == .sum_type && !left.typ.is_ptr() { eq_fn := g.gen_sumtype_equality_fn(info.parent_type) - fn_builder.writeln('\treturn ${eq_fn}_sumtype_eq(a, b);') + fn_builder.writeln('\treturn ${eq_fn}_sumtype_eq(${left_var}, ${right_var});') } else if sym.kind == .struct_ && !left.typ.is_ptr() { eq_fn := g.gen_struct_equality_fn(info.parent_type) - fn_builder.writeln('\treturn ${eq_fn}_struct_eq(a, b);') + fn_builder.writeln('\treturn ${eq_fn}_struct_eq(${left_var}, ${right_var});') } else if sym.kind == .interface_ && !left.typ.is_ptr() { eq_fn := g.gen_interface_equality_fn(info.parent_type) - fn_builder.writeln('\treturn ${eq_fn}_interface_eq(a, b);') + fn_builder.writeln('\treturn ${eq_fn}_interface_eq(${left_var}, ${right_var});') } else if sym.kind == .array && !left.typ.is_ptr() { eq_fn := g.gen_array_equality_fn(info.parent_type) - fn_builder.writeln('\treturn ${eq_fn}_arr_eq(a, b);') + fn_builder.writeln('\treturn ${eq_fn}_arr_eq(${left_var}, ${right_var});') } else if sym.kind == .array_fixed && !left.typ.is_ptr() { eq_fn := g.gen_fixed_array_equality_fn(info.parent_type) - fn_builder.writeln('\treturn ${eq_fn}_arr_eq(a, b);') + fn_builder.writeln('\treturn ${eq_fn}_arr_eq(${left_var}, ${right_var});') } else if sym.kind == .map && !left.typ.is_ptr() { eq_fn := g.gen_map_equality_fn(info.parent_type) - fn_builder.writeln('\treturn ${eq_fn}_map_eq(a, b);') - } else if sym.kind == .function { + fn_builder.writeln('\treturn ${eq_fn}_map_eq(${left_var}, ${right_var});') + } else if sym.kind == .function && !left.typ.has_flag(.option) { fn_builder.writeln('\treturn *((voidptr*)(a)) == *((voidptr*)(b));') } else if info.parent_type.has_flag(.option) || left.typ.has_flag(.option) { - fn_builder.writeln('\treturn !memcmp(&a.data, &b.data, sizeof(${g.base_type(info.parent_type)}));') + fn_builder.writeln('\treturn a.state == b.state && !memcmp(&a.data, &b.data, sizeof(${g.base_type(info.parent_type)}));') } else { fn_builder.writeln('\treturn a == b;') } @@ -282,38 +285,52 @@ fn (mut g Gen) gen_array_equality_fn(left_type ast.Type) string { mut fn_builder := strings.new_builder(512) fn_builder.writeln('static bool ${ptr_styp}_arr_eq(${ptr_styp} a, ${ptr_styp} b) {') - fn_builder.writeln('\tif (a.len != b.len) {') + + left_len := g.read_field(left_type, 'len', 'a') + right_len := g.read_field(left_type, 'len', 'b') + + left_data := g.read_field(left_type, 'data', 'a') + right_data := g.read_field(left_type, 'data', 'b') + + left_elem := g.read_field(left_type, 'element_size', 'a') + right_elem := g.read_field(left_type, 'element_size', 'b') + + if left_type.has_flag(.option) { + fn_builder.writeln('\tif (a.state != b.state) return false;') + } + + 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.len; ++i) {') + fn_builder.writeln('\tfor (int i = 0; i < ${left_len}; ++i) {') // compare every pair of elements of the two arrays if elem.sym.kind == .string { - fn_builder.writeln('\t\tif (!string__eq(*((${ptr_elem_styp}*)((byte*)a.data+(i*a.element_size))), *((${ptr_elem_styp}*)((byte*)b.data+(i*b.element_size))))) {') + fn_builder.writeln('\t\tif (!string__eq(*((${ptr_elem_styp}*)((byte*)${left_data}+(i*${left_elem}))), *((${ptr_elem_styp}*)((byte*)${right_data}+(i*${right_elem}))))) {') } else if elem.sym.kind == .sum_type && !elem.typ.is_ptr() { eq_fn := g.gen_sumtype_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq(((${ptr_elem_styp}*)a.data)[i], ((${ptr_elem_styp}*)b.data)[i])) {') + fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq(((${ptr_elem_styp}*)${left_data})[i], ((${ptr_elem_styp}*)${right_data})[i])) {') } else if elem.sym.kind == .struct_ && !elem.typ.is_ptr() { eq_fn := g.gen_struct_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(((${ptr_elem_styp}*)a.data)[i], ((${ptr_elem_styp}*)b.data)[i])) {') + fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(((${ptr_elem_styp}*)${left_data})[i], ((${ptr_elem_styp}*)${right_data})[i])) {') } else if elem.sym.kind == .interface_ && !elem.typ.is_ptr() { eq_fn := g.gen_interface_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_interface_eq(((${ptr_elem_styp}*)a.data)[i], ((${ptr_elem_styp}*)b.data)[i])) {') + fn_builder.writeln('\t\tif (!${eq_fn}_interface_eq(((${ptr_elem_styp}*)${left_data})[i], ((${ptr_elem_styp}*)${right_data})[i])) {') } else if elem.sym.kind == .array && !elem.typ.is_ptr() { eq_fn := g.gen_array_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(((${ptr_elem_styp}*)a.data)[i], ((${ptr_elem_styp}*)b.data)[i])) {') + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(((${ptr_elem_styp}*)${left_data})[i], ((${ptr_elem_styp}*)${right_data})[i])) {') } else if elem.sym.kind == .array_fixed && !elem.typ.is_ptr() { eq_fn := g.gen_fixed_array_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(((${ptr_elem_styp}*)a.data)[i], ((${ptr_elem_styp}*)b.data)[i])) {') + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(((${ptr_elem_styp}*)${left_data})[i], ((${ptr_elem_styp}*)${right_data})[i])) {') } else if elem.sym.kind == .map && !elem.typ.is_ptr() { eq_fn := g.gen_map_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(((${ptr_elem_styp}*)a.data)[i], ((${ptr_elem_styp}*)b.data)[i])) {') + fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(((${ptr_elem_styp}*)${left_data})[i], ((${ptr_elem_styp}*)${right_data})[i])) {') } else if elem.sym.kind == .alias && !elem.typ.is_ptr() { eq_fn := g.gen_alias_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(((${ptr_elem_styp}*)a.data)[i], ((${ptr_elem_styp}*)b.data)[i])) {') + fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(((${ptr_elem_styp}*)${left_data})[i], ((${ptr_elem_styp}*)${right_data})[i])) {') } else if elem.sym.kind == .function { - fn_builder.writeln('\t\tif (*((voidptr*)((byte*)a.data+(i*a.element_size))) != *((voidptr*)((byte*)b.data+(i*b.element_size)))) {') + fn_builder.writeln('\t\tif (*((voidptr*)((byte*)${left_data}+(i*${left_elem}))) != *((voidptr*)((byte*)${right_data}+(i*${right_elem})))) {') } else { - fn_builder.writeln('\t\tif (*((${ptr_elem_styp}*)((byte*)a.data+(i*a.element_size))) != *((${ptr_elem_styp}*)((byte*)b.data+(i*b.element_size)))) {') + fn_builder.writeln('\t\tif (*((${ptr_elem_styp}*)((byte*)${left_data}+(i*${left_elem}))) != *((${ptr_elem_styp}*)((byte*)${right_data}+(i*${right_elem})))) {') } fn_builder.writeln('\t\t\treturn false;') fn_builder.writeln('\t\t}') @@ -457,6 +474,7 @@ fn (mut g Gen) gen_map_equality_fn(left_type ast.Type) string { fn (mut g Gen) gen_interface_equality_fn(left_type ast.Type) string { left := g.unwrap(left_type) ptr_styp := g.typ(left.typ.set_nr_muls(0)) + idx_fn := g.typ(left.typ.set_nr_muls(0).clear_flag(.option)) fn_name := ptr_styp.replace('interface ', '') if left_type in g.generated_eq_fns { return fn_name @@ -471,12 +489,12 @@ fn (mut g Gen) gen_interface_equality_fn(left_type ast.Type) string { } left_arg := g.read_field(left_type, '_typ', 'a') - right_arg := g.read_field(left_type, '_typ', 'a') + right_arg := g.read_field(left_type, '_typ', 'b') - fn_builder.writeln('static int v_typeof_interface_idx_${ptr_styp}(int sidx); // for auto eq method') + fn_builder.writeln('static int v_typeof_interface_idx_${idx_fn}(int sidx); // for auto eq method') fn_builder.writeln('static bool ${fn_name}_interface_eq(${ptr_styp} a, ${ptr_styp} b) {') fn_builder.writeln('\tif (${left_arg} == ${right_arg}) {') - fn_builder.writeln('\t\tint idx = v_typeof_interface_idx_${ptr_styp}(${left_arg});') + fn_builder.writeln('\t\tint idx = v_typeof_interface_idx_${idx_fn}(${left_arg});') if info is ast.Interface { for typ in info.types { fn_builder.writeln('\t\tif (idx == ${typ.idx()}) {') @@ -484,30 +502,44 @@ fn (mut g Gen) gen_interface_equality_fn(left_type ast.Type) string { match g.table.type_kind(typ) { .struct_ { eq_fn := g.gen_struct_equality_fn(typ) - fn_builder.write_string('${eq_fn}_struct_eq(*(a._${eq_fn}), *(b._${eq_fn}))') + l_eqfn := g.read_field(left_type, '_${eq_fn}', 'a') + r_eqfn := g.read_field(left_type, '_${eq_fn}', 'b') + fn_builder.write_string('${eq_fn}_struct_eq(*(${l_eqfn}), *(${r_eqfn}))') } .string { - fn_builder.write_string('string__eq(*(a._string), *(b._string))') + l_str := g.read_field(left_type, '_string', 'a') + r_str := g.read_field(left_type, '_string', 'b') + fn_builder.write_string('string__eq(*(${l_str}), *(${r_str}))') } .sum_type { eq_fn := g.gen_sumtype_equality_fn(typ) - fn_builder.write_string('${eq_fn}_sumtype_eq(*(a._${eq_fn}), *(b._${eq_fn}))') + l_eqfn := g.read_field(left_type, '_${eq_fn}', 'a') + r_eqfn := g.read_field(left_type, '_${eq_fn}', 'b') + fn_builder.write_string('${eq_fn}_sumtype_eq(*(${l_eqfn}), *(${r_eqfn}))') } .array { eq_fn := g.gen_array_equality_fn(typ) - fn_builder.write_string('${eq_fn}_arr_eq(*(a._${eq_fn}), *(b._${eq_fn}))') + l_eqfn := g.read_field(left_type, '_${eq_fn}', 'a') + r_eqfn := g.read_field(left_type, '_${eq_fn}', 'b') + fn_builder.write_string('${eq_fn}_arr_eq(*(${l_eqfn}), *(${r_eqfn}))') } .array_fixed { eq_fn := g.gen_fixed_array_equality_fn(typ) - fn_builder.write_string('${eq_fn}_arr_eq(*(a._${eq_fn}), *(b._${eq_fn}))') + l_eqfn := g.read_field(left_type, '_${eq_fn}', 'a') + r_eqfn := g.read_field(left_type, '_${eq_fn}', 'b') + fn_builder.write_string('${eq_fn}_arr_eq(*(${l_eqfn}), *(${r_eqfn}))') } .map { eq_fn := g.gen_map_equality_fn(typ) - fn_builder.write_string('${eq_fn}_map_eq(*(a._${eq_fn}), *(b._${eq_fn}))') + l_eqfn := g.read_field(left_type, '_${eq_fn}', 'a') + r_eqfn := g.read_field(left_type, '_${eq_fn}', 'b') + fn_builder.write_string('${eq_fn}_map_eq(*(${l_eqfn}), *(${r_eqfn}))') } .alias { eq_fn := g.gen_alias_equality_fn(typ) - fn_builder.write_string('${eq_fn}_alias_eq(*(a._${eq_fn}), *(b._${eq_fn}))') + l_eqfn := g.read_field(left_type, '_${eq_fn}', 'a') + r_eqfn := g.read_field(left_type, '_${eq_fn}', 'b') + fn_builder.write_string('${eq_fn}_alias_eq(*(${l_eqfn}), *(${r_eqfn}))') } else { fn_builder.write_string('true') diff --git a/vlib/v/tests/option_struct_compare_test.v b/vlib/v/tests/option_struct_compare_test.v new file mode 100644 index 0000000000..c156056e4a --- /dev/null +++ b/vlib/v/tests/option_struct_compare_test.v @@ -0,0 +1,88 @@ +interface Interface { + a ?int +} + +struct Test2 { + a ?int +} + +type Alias = int +type AliasStruct = Test2 +type AliasStruct2 = fn () + +type AliasStruct3 = []int + +type SumType = Alias | AliasStruct2 | f64 | int + +struct Test { +mut: + a ?int + b ?f64 + c ?string + d ?[]int + e ?[]string + f ?[]f64 + g ?fn () + h ?Alias + i ?Interface = Test2{ + a: -1 + } + j ?AliasStruct + k ?AliasStruct2 + l ?AliasStruct3 + m ?SumType + n SumType +} + +fn test_simple_compare() { + mut a := Test{} + mut b := Test{} + assert a == b +} + +fn test_anon_compare() { + z := fn () {} + d := fn () {} + mut a := Test{} + mut b := Test{} + + assert a == b + + a.g = z + b.g = d + assert a != b +} + +fn test_alias_compare() { + z := 1 + d := 2 + mut a := Test{} + mut b := Test{} + + assert a == b + + a.h = z + b.h = d + assert a != b +} + +fn test_iface_compare() { + z := Test2{ + a: -1 + } + d := Test2{ + a: 0 + } + mut a := Test{ + a: 0 + } + mut b := Test{ + a: 0 + } + + assert a == b + + a.i = z + b.i = d + assert a != b +}