From 0258482caf9a73b733d428fa683c054f6cc27510 Mon Sep 17 00:00:00 2001 From: zakuro Date: Mon, 22 Mar 2021 14:39:07 +0900 Subject: [PATCH] cgen: auto eq method for sumtype (#9408) --- vlib/v/checker/checker.v | 14 +- .../checker/tests/eq_ne_op_wrong_type_err.out | 199 +++++++++++------- .../checker/tests/eq_ne_op_wrong_type_err.vv | 13 ++ vlib/v/gen/c/auto_eq_methods.v | 70 ++++++ vlib/v/gen/c/cgen.v | 196 +++++++++-------- vlib/v/table/types.v | 8 + vlib/v/tests/sumtype_equality_test.v | 29 +++ 7 files changed, 362 insertions(+), 167 deletions(-) create mode 100644 vlib/v/tests/sumtype_equality_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 67fcbf4d3f..3948bef58f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -741,6 +741,7 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { else {} } } + eq_ne := infix_expr.op in [.eq, .ne] // Single side check // Place these branches according to ops' usage frequency to accelerate. // TODO: First branch includes ops where single side check is not needed, or needed but hasn't been implemented. @@ -748,8 +749,8 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { match infix_expr.op { // .eq, .ne, .gt, .lt, .ge, .le, .and, .logical_or, .dot, .key_as, .right_shift {} .eq, .ne { - is_mismatch := (left.kind == .alias && right.kind in [.struct_, .array]) - || (right.kind == .alias && left.kind in [.struct_, .array]) + is_mismatch := (left.kind == .alias && right.kind in [.struct_, .array, .sum_type]) + || (right.kind == .alias && left.kind in [.struct_, .array, .sum_type]) if is_mismatch { c.error('possible type mismatch of compared values of `$infix_expr.op` operation', left_right_pos) @@ -996,7 +997,7 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { // TODO broken !in c.error('string types only have the following operators defined: `==`, `!=`, `<`, `>`, `<=`, `>=`, and `+`', infix_expr.pos) - } else if left.kind == .enum_ && right.kind == .enum_ && infix_expr.op !in [.ne, .eq] { + } else if left.kind == .enum_ && right.kind == .enum_ && !eq_ne { left_enum := left.info as table.Enum right_enum := right.info as table.Enum if left_enum.is_flag && right_enum.is_flag { @@ -1011,10 +1012,11 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { infix_expr.pos) } } - // sum types can't have any infix operation except of "is", is is checked before and doesn't reach this - if c.table.type_kind(left_type) == .sum_type { + // sum types can't have any infix operation except of `is`, `eq`, `ne`. + // `is` is checked before and doesn't reach this. + if c.table.type_kind(left_type) == .sum_type && !eq_ne { c.error('cannot use operator `$infix_expr.op` with `$left.name`', infix_expr.pos) - } else if c.table.type_kind(right_type) == .sum_type { + } else if c.table.type_kind(right_type) == .sum_type && !eq_ne { c.error('cannot use operator `$infix_expr.op` with `$right.name`', infix_expr.pos) } // TODO move this to symmetric_check? Right now it would break `return 0` for `fn()?int ` diff --git a/vlib/v/checker/tests/eq_ne_op_wrong_type_err.out b/vlib/v/checker/tests/eq_ne_op_wrong_type_err.out index ad5375f0e6..d5f43cf749 100644 --- a/vlib/v/checker/tests/eq_ne_op_wrong_type_err.out +++ b/vlib/v/checker/tests/eq_ne_op_wrong_type_err.out @@ -1,84 +1,139 @@ -vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:10:10: error: infix expr: cannot use `int literal` (right expression) as `Aaa` - 8 | - 9 | fn main() { - 10 | println(Aaa{} == 10) - | ~~~~~~~~~~~ - 11 | println(10 == Aaa{}) - 12 | println(Aaa{} != 10) -vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:11:10: error: infix expr: cannot use `Aaa` (right expression) as `int literal` - 9 | fn main() { - 10 | println(Aaa{} == 10) - 11 | println(10 == Aaa{}) - | ~~~~~~~~~~~ - 12 | println(Aaa{} != 10) - 13 | println(10 != Aaa{}) vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:12:10: error: infix expr: cannot use `int literal` (right expression) as `Aaa` - 10 | println(Aaa{} == 10) - 11 | println(10 == Aaa{}) - 12 | println(Aaa{} != 10) + 10 | + 11 | fn main() { + 12 | println(Aaa{} == 10) | ~~~~~~~~~~~ - 13 | println(10 != Aaa{}) - 14 | + 13 | println(10 == Aaa{}) + 14 | println(Aaa{} != 10) vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:13:10: error: infix expr: cannot use `Aaa` (right expression) as `int literal` - 11 | println(10 == Aaa{}) - 12 | println(Aaa{} != 10) - 13 | println(10 != Aaa{}) + 11 | fn main() { + 12 | println(Aaa{} == 10) + 13 | println(10 == Aaa{}) | ~~~~~~~~~~~ - 14 | - 15 | println(Aaa{0} == AAaa{0}) -vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:15:10: error: possible type mismatch of compared values of `==` operation - 13 | println(10 != Aaa{}) - 14 | - 15 | println(Aaa{0} == AAaa{0}) + 14 | println(Aaa{} != 10) + 15 | println(10 != Aaa{}) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:14:10: error: infix expr: cannot use `int literal` (right expression) as `Aaa` + 12 | println(Aaa{} == 10) + 13 | println(10 == Aaa{}) + 14 | println(Aaa{} != 10) + | ~~~~~~~~~~~ + 15 | println(10 != Aaa{}) + 16 | +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:15:10: error: infix expr: cannot use `Aaa` (right expression) as `int literal` + 13 | println(10 == Aaa{}) + 14 | println(Aaa{} != 10) + 15 | println(10 != Aaa{}) + | ~~~~~~~~~~~ + 16 | + 17 | println(Aaa{0} == AAaa{0}) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:17:10: error: possible type mismatch of compared values of `==` operation + 15 | println(10 != Aaa{}) + 16 | + 17 | println(Aaa{0} == AAaa{0}) | ~~~~~~~~~~~~~~~~~ - 16 | println(AAaa{0} == Aaa{0}) - 17 | println(AAaa{1} != Aaa{1}) -vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:16:10: error: possible type mismatch of compared values of `==` operation - 14 | - 15 | println(Aaa{0} == AAaa{0}) - 16 | println(AAaa{0} == Aaa{0}) + 18 | println(AAaa{0} == Aaa{0}) + 19 | println(AAaa{1} != Aaa{1}) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:18:10: error: possible type mismatch of compared values of `==` operation + 16 | + 17 | println(Aaa{0} == AAaa{0}) + 18 | println(AAaa{0} == Aaa{0}) | ~~~~~~~~~~~~~~~~~ - 17 | println(AAaa{1} != Aaa{1}) - 18 | println(Aaa{1} != AAaa{1}) -vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:17:10: error: possible type mismatch of compared values of `!=` operation - 15 | println(Aaa{0} == AAaa{0}) - 16 | println(AAaa{0} == Aaa{0}) - 17 | println(AAaa{1} != Aaa{1}) + 19 | println(AAaa{1} != Aaa{1}) + 20 | println(Aaa{1} != AAaa{1}) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:19:10: error: possible type mismatch of compared values of `!=` operation + 17 | println(Aaa{0} == AAaa{0}) + 18 | println(AAaa{0} == Aaa{0}) + 19 | println(AAaa{1} != Aaa{1}) | ~~~~~~~~~~~~~~~~~ - 18 | println(Aaa{1} != AAaa{1}) - 19 | -vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:18:10: error: possible type mismatch of compared values of `!=` operation - 16 | println(AAaa{0} == Aaa{0}) - 17 | println(AAaa{1} != Aaa{1}) - 18 | println(Aaa{1} != AAaa{1}) + 20 | println(Aaa{1} != AAaa{1}) + 21 | +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:20:10: error: possible type mismatch of compared values of `!=` operation + 18 | println(AAaa{0} == Aaa{0}) + 19 | println(AAaa{1} != Aaa{1}) + 20 | println(Aaa{1} != AAaa{1}) | ~~~~~~~~~~~~~~~~~ - 19 | - 20 | arr := Arr([0]) -vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:21:10: error: possible type mismatch of compared values of `==` operation - 19 | - 20 | arr := Arr([0]) - 21 | println(arr == [0]) + 21 | + 22 | arr := Arr([0]) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:23:10: error: possible type mismatch of compared values of `==` operation + 21 | + 22 | arr := Arr([0]) + 23 | println(arr == [0]) | ~~~~~~~~~~ - 22 | println([1] == arr) - 23 | println(arr != [0]) -vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:22:10: error: possible type mismatch of compared values of `==` operation - 20 | arr := Arr([0]) - 21 | println(arr == [0]) - 22 | println([1] == arr) + 24 | println([1] == arr) + 25 | println(arr != [0]) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:24:10: error: possible type mismatch of compared values of `==` operation + 22 | arr := Arr([0]) + 23 | println(arr == [0]) + 24 | println([1] == arr) | ~~~~~~~~~~ - 23 | println(arr != [0]) - 24 | println([1] != arr) -vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:23:10: error: possible type mismatch of compared values of `!=` operation - 21 | println(arr == [0]) - 22 | println([1] == arr) - 23 | println(arr != [0]) + 25 | println(arr != [0]) + 26 | println([1] != arr) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:25:10: error: possible type mismatch of compared values of `!=` operation + 23 | println(arr == [0]) + 24 | println([1] == arr) + 25 | println(arr != [0]) | ~~~~~~~~~~ - 24 | println([1] != arr) - 25 | } -vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:24:10: error: possible type mismatch of compared values of `!=` operation - 22 | println([1] == arr) - 23 | println(arr != [0]) - 24 | println([1] != arr) + 26 | println([1] != arr) + 27 | +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:26:10: error: possible type mismatch of compared values of `!=` operation + 24 | println([1] == arr) + 25 | println(arr != [0]) + 26 | println([1] != arr) | ~~~~~~~~~~ - 25 | } - + 27 | + 28 | arr_aaa := ArrAaa(arr) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:29:10: error: possible type mismatch of compared values of `==` operation + 27 | + 28 | arr_aaa := ArrAaa(arr) + 29 | println(arr_aaa == arr) + | ~~~~~~~~~~~~~~ + 30 | println(arr == arr_aaa) + 31 | println(arr_aaa != arr) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:30:10: error: possible type mismatch of compared values of `==` operation + 28 | arr_aaa := ArrAaa(arr) + 29 | println(arr_aaa == arr) + 30 | println(arr == arr_aaa) + | ~~~~~~~~~~~~~~ + 31 | println(arr_aaa != arr) + 32 | println(arr != arr_aaa) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:31:10: error: possible type mismatch of compared values of `!=` operation + 29 | println(arr_aaa == arr) + 30 | println(arr == arr_aaa) + 31 | println(arr_aaa != arr) + | ~~~~~~~~~~~~~~ + 32 | println(arr != arr_aaa) + 33 | +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:32:10: error: possible type mismatch of compared values of `!=` operation + 30 | println(arr == arr_aaa) + 31 | println(arr_aaa != arr) + 32 | println(arr != arr_aaa) + | ~~~~~~~~~~~~~~ + 33 | + 34 | println(arr_aaa == [0]) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:34:10: error: infix expr: cannot use `[]int` (right expression) as `ArrAaa` + 32 | println(arr != arr_aaa) + 33 | + 34 | println(arr_aaa == [0]) + | ~~~~~~~~~~~~~~ + 35 | println([1] == arr_aaa) + 36 | println(arr_aaa != [0]) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:35:10: error: infix expr: cannot use `ArrAaa` (right expression) as `[]int` + 33 | + 34 | println(arr_aaa == [0]) + 35 | println([1] == arr_aaa) + | ~~~~~~~~~~~~~~ + 36 | println(arr_aaa != [0]) + 37 | println([1] != arr_aaa) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:36:10: error: infix expr: cannot use `[]int` (right expression) as `ArrAaa` + 34 | println(arr_aaa == [0]) + 35 | println([1] == arr_aaa) + 36 | println(arr_aaa != [0]) + | ~~~~~~~~~~~~~~ + 37 | println([1] != arr_aaa) + 38 | } +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:37:10: error: infix expr: cannot use `ArrAaa` (right expression) as `[]int` + 35 | println([1] == arr_aaa) + 36 | println(arr_aaa != [0]) + 37 | println([1] != arr_aaa) + | ~~~~~~~~~~~~~~ + 38 | } diff --git a/vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv b/vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv index a32f38df56..36e7d178e3 100644 --- a/vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv +++ b/vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv @@ -6,6 +6,8 @@ type AAaa = Aaa type Arr = []int +type ArrAaa = Aaa | Arr + fn main() { println(Aaa{} == 10) println(10 == Aaa{}) @@ -22,4 +24,15 @@ fn main() { println([1] == arr) println(arr != [0]) println([1] != arr) + + arr_aaa := ArrAaa(arr) + println(arr_aaa == arr) + println(arr == arr_aaa) + println(arr_aaa != arr) + println(arr != arr_aaa) + + println(arr_aaa == [0]) + println([1] == arr_aaa) + println(arr_aaa != [0]) + println([1] != arr_aaa) } diff --git a/vlib/v/gen/c/auto_eq_methods.v b/vlib/v/gen/c/auto_eq_methods.v index 0d263da967..9f5276cc77 100644 --- a/vlib/v/gen/c/auto_eq_methods.v +++ b/vlib/v/gen/c/auto_eq_methods.v @@ -5,6 +5,59 @@ module c import strings import v.table +fn (mut g Gen) gen_sumtype_equality_fn(left table.Type) string { + ptr_typ := g.typ(left).trim('*') + if ptr_typ in g.sumtype_fn_definitions { + return ptr_typ + } + g.sumtype_fn_definitions << ptr_typ + left_sym := g.table.get_type_symbol(left) + info := left_sym.sumtype_info() + g.type_definitions.writeln('static bool ${ptr_typ}_sumtype_eq($ptr_typ a, $ptr_typ b); // auto') + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static bool ${ptr_typ}_sumtype_eq($ptr_typ a, $ptr_typ b) {') + + fn_builder.writeln('\tif (a._typ != b._typ) { return false; } ') + for typ in info.variants { + sym := g.table.get_type_symbol(typ) + fn_builder.writeln('\tif (a._typ == $typ) {') + name := '_$sym.cname' + if sym.kind == .string { + fn_builder.writeln('\t\tif (string_ne(*a.$name, *b.$name)) {') + } else if sym.kind == .sum_type && !typ.is_ptr() { + eq_fn := g.gen_sumtype_equality_fn(typ) + fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq(*a.$name, *b.$name)) {') + } else if sym.kind == .struct_ && !typ.is_ptr() { + eq_fn := g.gen_struct_equality_fn(typ) + fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(*a.$name, *b.$name)) {') + } else if sym.kind == .array && !typ.is_ptr() { + eq_fn := g.gen_array_equality_fn(typ) + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(*a.$name, *b.$name)) {') + } else if sym.kind == .array_fixed && !typ.is_ptr() { + eq_fn := g.gen_fixed_array_equality_fn(typ) + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(*a.$name, *b.$name)) {') + } else if sym.kind == .map && !typ.is_ptr() { + eq_fn := g.gen_map_equality_fn(typ) + fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(*a.$name, *b.$name)) {') + } else if sym.kind == .alias && !typ.is_ptr() { + eq_fn := g.gen_alias_equality_fn(typ) + fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(*a.$name, *b.$name)) {') + } else if sym.kind == .function { + fn_builder.writeln('\t\tif (*((voidptr*)(*a.$name)) != *((voidptr*)(*b.$name))) {') + } else { + fn_builder.writeln('\t\tif (*a.$name != *b.$name) {') + } + fn_builder.writeln('\t\t\treturn false;') + fn_builder.writeln('\t\t}') + fn_builder.writeln('\t\treturn true;') + fn_builder.writeln('\t}') + } + fn_builder.writeln('\treturn false;') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() + return ptr_typ +} + fn (mut g Gen) gen_struct_equality_fn(left table.Type) string { ptr_typ := g.typ(left).trim('*') if ptr_typ in g.struct_fn_definitions { @@ -20,6 +73,9 @@ fn (mut g Gen) gen_struct_equality_fn(left table.Type) string { sym := g.table.get_type_symbol(field.typ) if sym.kind == .string { fn_builder.writeln('\tif (string_ne(a.$field.name, b.$field.name)) {') + } else if sym.kind == .sum_type && !field.typ.is_ptr() { + eq_fn := g.gen_sumtype_equality_fn(field.typ) + fn_builder.writeln('\tif (!${eq_fn}_sumtype_eq(a.$field.name, b.$field.name)) {') } else if sym.kind == .struct_ && !field.typ.is_ptr() { eq_fn := g.gen_struct_equality_fn(field.typ) fn_builder.writeln('\tif (!${eq_fn}_struct_eq(a.$field.name, b.$field.name)) {') @@ -63,6 +119,9 @@ fn (mut g Gen) gen_alias_equality_fn(left table.Type) string { sym := g.table.get_type_symbol(info.parent_type) if sym.kind == .string { fn_builder.writeln('\tif (string_ne(a, b)) {') + } else if sym.kind == .sum_type && !left.is_ptr() { + eq_fn := g.gen_sumtype_equality_fn(info.parent_type) + fn_builder.writeln('\tif (!${eq_fn}_sumtype_eq(a, b)) {') } else if sym.kind == .struct_ && !left.is_ptr() { eq_fn := g.gen_struct_equality_fn(info.parent_type) fn_builder.writeln('\tif (!${eq_fn}_struct_eq(a, b)) {') @@ -108,6 +167,9 @@ fn (mut g Gen) gen_array_equality_fn(left table.Type) string { // compare every pair of elements of the two arrays if elem_sym.kind == .string { fn_builder.writeln('\t\tif (string_ne(*(($ptr_elem_typ*)((byte*)a.data+(i*a.element_size))), *(($ptr_elem_typ*)((byte*)b.data+(i*b.element_size))))) {') + } 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_typ*)a.data)[i], (($ptr_elem_typ*)b.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_typ*)a.data)[i], (($ptr_elem_typ*)b.data)[i])) {') @@ -155,6 +217,9 @@ fn (mut g Gen) gen_fixed_array_equality_fn(left table.Type) string { // compare every pair of elements of the two fixed arrays if elem_sym.kind == .string { fn_builder.writeln('\t\tif (string_ne(a[i], b[i])) {') + } 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(a[i], b[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(a[i], b[i])) {') @@ -225,6 +290,10 @@ fn (mut g Gen) gen_map_equality_fn(left table.Type) string { .string { fn_builder.writeln('\t\tif (!fast_string_eq(*(string*)map_get_1(&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_typ*)map_get_1(&b, k, &($ptr_value_typ[]){ 0 }), v)) {') + } .struct_ { eq_fn := g.gen_struct_equality_fn(value_typ) fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(*($ptr_value_typ*)map_get_1(&b, k, &($ptr_value_typ[]){ 0 }), v)) {') @@ -249,6 +318,7 @@ fn (mut g Gen) gen_map_equality_fn(left table.Type) string { fn_builder.writeln('\t\tif (*(voidptr*)map_get_1(&b, k, &(voidptr[]){ 0 }) != v) {') } else { + println(kind) fn_builder.writeln('\t\tif (*($ptr_value_typ*)map_get_1(&b, k, &($ptr_value_typ[]){ 0 }) != v) {') } } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 9c5db0b11d..8922399636 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -32,95 +32,96 @@ struct Gen { pref &pref.Preferences module_built string mut: - table &table.Table - out strings.Builder - cheaders strings.Builder - includes strings.Builder // all C #includes required by V modules - typedefs strings.Builder - typedefs2 strings.Builder - type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) - definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) - inits map[string]strings.Builder // contents of `void _vinit/2{}` - cleanups map[string]strings.Builder // contents of `void _vcleanup(){}` - gowrappers strings.Builder // all go callsite wrappers - stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined - auto_str_funcs strings.Builder // function bodies of all auto generated _str funcs - comptime_defines strings.Builder // custom defines, given by -d/-define flags on the CLI - pcs_declarations strings.Builder // -prof profile counter declarations for each function - hotcode_definitions strings.Builder // -live declarations & functions - embedded_data strings.Builder // data to embed in the executable/binary - shared_types strings.Builder // shared/lock types - shared_functions strings.Builder // shared constructors - channel_definitions strings.Builder // channel related code - options_typedefs strings.Builder // Option typedefs - options strings.Builder // `Option_xxxx` types - json_forward_decls strings.Builder // json type forward decls - enum_typedefs strings.Builder // enum types - sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc - file ast.File - fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 - last_fn_c_name string - tmp_count int // counter for unique tmp vars (_tmp1, tmp2 etc) - tmp_count2 int // a separate tmp var counter for autofree fn calls - is_c_call bool // e.g. `C.printf("v")` - is_assign_lhs bool // inside left part of assign expr (for array_set(), etc) - discard_or_result bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage - is_void_expr_stmt bool // ExprStmt whos result is discarded - is_arraymap_set bool // map or array set value state - is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc - is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc) - is_shared bool // for initialization of hidden mutex in `[rw]shared` literals - is_vlines_enabled bool // is it safe to generate #line directives when -g is passed - arraymap_set_pos int // map or array set value position - vlines_path string // set to the proper path for generating #line directives - optionals []string // to avoid duplicates TODO perf, use map - chan_pop_optionals []string // types for `x := <-ch or {...}` - chan_push_optionals []string // types for `ch <- x or {...}` - shareds []int // types with hidden mutex for which decl has been emitted - inside_ternary int // ?: comma separated statements on a single line - inside_map_postfix bool // inside map++/-- postfix expr - inside_map_infix bool // inside map< fn counter name - is_builtin_mod bool - hotcode_fn_names []string - embedded_files []ast.EmbeddedFile - cur_fn ast.FnDecl - cur_generic_types []table.Type // `int`, `string`, etc in `foo()` - sql_i int - sql_stmt_name string - sql_side SqlExprSide // left or right, to distinguish idents in `name == name` - inside_vweb_tmpl bool - inside_return bool - inside_or_block bool - strs_to_free0 []string // strings.Builder + table &table.Table + out strings.Builder + cheaders strings.Builder + includes strings.Builder // all C #includes required by V modules + typedefs strings.Builder + typedefs2 strings.Builder + type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) + definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) + inits map[string]strings.Builder // contents of `void _vinit/2{}` + cleanups map[string]strings.Builder // contents of `void _vcleanup(){}` + gowrappers strings.Builder // all go callsite wrappers + stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined + auto_str_funcs strings.Builder // function bodies of all auto generated _str funcs + comptime_defines strings.Builder // custom defines, given by -d/-define flags on the CLI + pcs_declarations strings.Builder // -prof profile counter declarations for each function + hotcode_definitions strings.Builder // -live declarations & functions + embedded_data strings.Builder // data to embed in the executable/binary + shared_types strings.Builder // shared/lock types + shared_functions strings.Builder // shared constructors + channel_definitions strings.Builder // channel related code + options_typedefs strings.Builder // Option typedefs + options strings.Builder // `Option_xxxx` types + json_forward_decls strings.Builder // json type forward decls + enum_typedefs strings.Builder // enum types + sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc + file ast.File + fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 + last_fn_c_name string + tmp_count int // counter for unique tmp vars (_tmp1, tmp2 etc) + tmp_count2 int // a separate tmp var counter for autofree fn calls + is_c_call bool // e.g. `C.printf("v")` + is_assign_lhs bool // inside left part of assign expr (for array_set(), etc) + discard_or_result bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage + is_void_expr_stmt bool // ExprStmt whos result is discarded + is_arraymap_set bool // map or array set value state + is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc + is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc) + is_shared bool // for initialization of hidden mutex in `[rw]shared` literals + is_vlines_enabled bool // is it safe to generate #line directives when -g is passed + arraymap_set_pos int // map or array set value position + vlines_path string // set to the proper path for generating #line directives + optionals []string // to avoid duplicates TODO perf, use map + chan_pop_optionals []string // types for `x := <-ch or {...}` + chan_push_optionals []string // types for `ch <- x or {...}` + shareds []int // types with hidden mutex for which decl has been emitted + inside_ternary int // ?: comma separated statements on a single line + inside_map_postfix bool // inside map++/-- postfix expr + inside_map_infix bool // inside map< fn counter name + is_builtin_mod bool + hotcode_fn_names []string + embedded_files []ast.EmbeddedFile + cur_fn ast.FnDecl + cur_generic_types []table.Type // `int`, `string`, etc in `foo()` + sql_i int + sql_stmt_name string + sql_side SqlExprSide // left or right, to distinguish idents in `name == name` + inside_vweb_tmpl bool + inside_return bool + inside_or_block bool + strs_to_free0 []string // strings.Builder // strs_to_free []string // strings.Builder inside_call bool has_main bool @@ -3518,6 +3519,23 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { } g.expr(node.right) g.write(')') + } else if op_is_eq_or_ne && left_sym.kind == .sum_type && right_sym.kind == .sum_type { + ptr_typ := g.gen_sumtype_equality_fn(left_type) + if node.op == .eq { + g.write('${ptr_typ}_sumtype_eq(') + } else if node.op == .ne { + g.write('!${ptr_typ}_sumtype_eq(') + } + if node.left_type.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(', ') + if node.right_type.is_ptr() { + g.write('*') + } + g.expr(node.right) + g.write(')') } else if op_is_key_in_or_not_in { if node.op == .not_in { g.write('!') diff --git a/vlib/v/table/types.v b/vlib/v/table/types.v index c3200fb001..093d08c621 100644 --- a/vlib/v/table/types.v +++ b/vlib/v/table/types.v @@ -525,6 +525,14 @@ pub fn (t &TypeSymbol) struct_info() Struct { } } +[inline] +pub fn (t &TypeSymbol) sumtype_info() SumType { + match mut t.info { + SumType { return t.info } + else { panic('TypeSymbol.sumtype_info(): no sumtype info for type: $t.name') } + } +} + /* pub fn (t TypeSymbol) str() string { return t.name diff --git a/vlib/v/tests/sumtype_equality_test.v b/vlib/v/tests/sumtype_equality_test.v new file mode 100644 index 0000000000..057159d1b0 --- /dev/null +++ b/vlib/v/tests/sumtype_equality_test.v @@ -0,0 +1,29 @@ +type Str = string | ustring + +struct Foo { + v int +} + +struct Bar { + v int +} + +type FooBar = Foo | Bar + +fn test_sumtype_equality() { + s1 := Str('s') + s2 := Str('s2') + u1 := Str('s1'.ustring()) + u2 := Str('s2'.ustring()) + assert s1 == s1 + assert u1 == u1 + assert s1 != s2 + assert u1 != u2 + assert u1 != s1 + + // Same value, defferent type + foo := FooBar(Foo{v: 0}) + bar := FooBar(Bar{v: 0}) + assert foo.v == bar.v + assert foo != bar +}