cgen: auto eq method for sumtype (#9408)

pull/9415/head
zakuro 2021-03-22 14:39:07 +09:00 committed by GitHub
parent e5a698395a
commit 0258482caf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 362 additions and 167 deletions

View File

@ -741,6 +741,7 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
else {} else {}
} }
} }
eq_ne := infix_expr.op in [.eq, .ne]
// Single side check // Single side check
// Place these branches according to ops' usage frequency to accelerate. // 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. // 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 { match infix_expr.op {
// .eq, .ne, .gt, .lt, .ge, .le, .and, .logical_or, .dot, .key_as, .right_shift {} // .eq, .ne, .gt, .lt, .ge, .le, .and, .logical_or, .dot, .key_as, .right_shift {}
.eq, .ne { .eq, .ne {
is_mismatch := (left.kind == .alias && right.kind in [.struct_, .array]) is_mismatch := (left.kind == .alias && right.kind in [.struct_, .array, .sum_type])
|| (right.kind == .alias && left.kind in [.struct_, .array]) || (right.kind == .alias && left.kind in [.struct_, .array, .sum_type])
if is_mismatch { if is_mismatch {
c.error('possible type mismatch of compared values of `$infix_expr.op` operation', c.error('possible type mismatch of compared values of `$infix_expr.op` operation',
left_right_pos) left_right_pos)
@ -996,7 +997,7 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
// TODO broken !in // TODO broken !in
c.error('string types only have the following operators defined: `==`, `!=`, `<`, `>`, `<=`, `>=`, and `+`', c.error('string types only have the following operators defined: `==`, `!=`, `<`, `>`, `<=`, `>=`, and `+`',
infix_expr.pos) 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 left_enum := left.info as table.Enum
right_enum := right.info as table.Enum right_enum := right.info as table.Enum
if left_enum.is_flag && right_enum.is_flag { 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) infix_expr.pos)
} }
} }
// sum types can't have any infix operation except of "is", is is checked before and doesn't reach this // sum types can't have any infix operation except of `is`, `eq`, `ne`.
if c.table.type_kind(left_type) == .sum_type { // `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) 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) 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 ` // TODO move this to symmetric_check? Right now it would break `return 0` for `fn()?int `

View File

@ -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` 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) 10 |
11 | println(10 == Aaa{}) 11 | fn main() {
12 | println(Aaa{} != 10) 12 | println(Aaa{} == 10)
| ~~~~~~~~~~~ | ~~~~~~~~~~~
13 | println(10 != Aaa{}) 13 | println(10 == Aaa{})
14 | 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` 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{}) 11 | fn main() {
12 | println(Aaa{} != 10) 12 | println(Aaa{} == 10)
13 | println(10 != Aaa{}) 13 | println(10 == Aaa{})
| ~~~~~~~~~~~ | ~~~~~~~~~~~
14 | 14 | println(Aaa{} != 10)
15 | println(Aaa{0} == AAaa{0}) 15 | println(10 != Aaa{})
vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:15:10: error: possible type mismatch of compared values of `==` operation vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:14:10: error: infix expr: cannot use `int literal` (right expression) as `Aaa`
13 | println(10 != Aaa{}) 12 | println(Aaa{} == 10)
14 | 13 | println(10 == Aaa{})
15 | println(Aaa{0} == AAaa{0}) 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}) 18 | println(AAaa{0} == Aaa{0})
17 | println(AAaa{1} != Aaa{1}) 19 | 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 vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:18:10: error: possible type mismatch of compared values of `==` operation
14 | 16 |
15 | println(Aaa{0} == AAaa{0}) 17 | println(Aaa{0} == AAaa{0})
16 | println(AAaa{0} == Aaa{0}) 18 | println(AAaa{0} == Aaa{0})
| ~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~
17 | println(AAaa{1} != Aaa{1}) 19 | println(AAaa{1} != Aaa{1})
18 | println(Aaa{1} != AAaa{1}) 20 | 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 vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:19:10: error: possible type mismatch of compared values of `!=` operation
15 | println(Aaa{0} == AAaa{0}) 17 | println(Aaa{0} == AAaa{0})
16 | println(AAaa{0} == Aaa{0}) 18 | println(AAaa{0} == Aaa{0})
17 | println(AAaa{1} != Aaa{1}) 19 | println(AAaa{1} != Aaa{1})
| ~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~
18 | println(Aaa{1} != AAaa{1}) 20 | println(Aaa{1} != AAaa{1})
19 | 21 |
vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:18:10: error: possible type mismatch of compared values of `!=` operation vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:20:10: error: possible type mismatch of compared values of `!=` operation
16 | println(AAaa{0} == Aaa{0}) 18 | println(AAaa{0} == Aaa{0})
17 | println(AAaa{1} != Aaa{1}) 19 | println(AAaa{1} != Aaa{1})
18 | println(Aaa{1} != AAaa{1}) 20 | println(Aaa{1} != AAaa{1})
| ~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~
19 | 21 |
20 | arr := Arr([0]) 22 | 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 vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:23:10: error: possible type mismatch of compared values of `==` operation
19 | 21 |
20 | arr := Arr([0]) 22 | arr := Arr([0])
21 | println(arr == [0]) 23 | println(arr == [0])
| ~~~~~~~~~~ | ~~~~~~~~~~
22 | println([1] == arr) 24 | println([1] == arr)
23 | println(arr != [0]) 25 | 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 vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:24:10: error: possible type mismatch of compared values of `==` operation
20 | arr := Arr([0]) 22 | arr := Arr([0])
21 | println(arr == [0]) 23 | println(arr == [0])
22 | println([1] == arr) 24 | println([1] == arr)
| ~~~~~~~~~~ | ~~~~~~~~~~
23 | println(arr != [0]) 25 | println(arr != [0])
24 | println([1] != arr) 26 | 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 vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:25:10: error: possible type mismatch of compared values of `!=` operation
21 | println(arr == [0]) 23 | println(arr == [0])
22 | println([1] == arr) 24 | println([1] == arr)
23 | println(arr != [0]) 25 | println(arr != [0])
| ~~~~~~~~~~ | ~~~~~~~~~~
24 | println([1] != arr) 26 | println([1] != arr)
25 | } 27 |
vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:24:10: error: possible type mismatch of compared values of `!=` operation vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:26:10: error: possible type mismatch of compared values of `!=` operation
22 | println([1] == arr) 24 | println([1] == arr)
23 | println(arr != [0]) 25 | println(arr != [0])
24 | println([1] != arr) 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 | }

View File

@ -6,6 +6,8 @@ type AAaa = Aaa
type Arr = []int type Arr = []int
type ArrAaa = Aaa | Arr
fn main() { fn main() {
println(Aaa{} == 10) println(Aaa{} == 10)
println(10 == Aaa{}) println(10 == Aaa{})
@ -22,4 +24,15 @@ fn main() {
println([1] == arr) println([1] == arr)
println(arr != [0]) println(arr != [0])
println([1] != arr) 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)
} }

View File

@ -5,6 +5,59 @@ module c
import strings import strings
import v.table 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 { fn (mut g Gen) gen_struct_equality_fn(left table.Type) string {
ptr_typ := g.typ(left).trim('*') ptr_typ := g.typ(left).trim('*')
if ptr_typ in g.struct_fn_definitions { 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) sym := g.table.get_type_symbol(field.typ)
if sym.kind == .string { if sym.kind == .string {
fn_builder.writeln('\tif (string_ne(a.$field.name, b.$field.name)) {') 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() { } else if sym.kind == .struct_ && !field.typ.is_ptr() {
eq_fn := g.gen_struct_equality_fn(field.typ) eq_fn := g.gen_struct_equality_fn(field.typ)
fn_builder.writeln('\tif (!${eq_fn}_struct_eq(a.$field.name, b.$field.name)) {') 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) sym := g.table.get_type_symbol(info.parent_type)
if sym.kind == .string { if sym.kind == .string {
fn_builder.writeln('\tif (string_ne(a, b)) {') 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() { } else if sym.kind == .struct_ && !left.is_ptr() {
eq_fn := g.gen_struct_equality_fn(info.parent_type) eq_fn := g.gen_struct_equality_fn(info.parent_type)
fn_builder.writeln('\tif (!${eq_fn}_struct_eq(a, b)) {') 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 // compare every pair of elements of the two arrays
if elem_sym.kind == .string { 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))))) {') 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() { } else if elem_sym.kind == .struct_ && !elem_typ.is_ptr() {
eq_fn := g.gen_struct_equality_fn(elem_typ) 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])) {') 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 // compare every pair of elements of the two fixed arrays
if elem_sym.kind == .string { if elem_sym.kind == .string {
fn_builder.writeln('\t\tif (string_ne(a[i], b[i])) {') 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() { } else if elem_sym.kind == .struct_ && !elem_typ.is_ptr() {
eq_fn := g.gen_struct_equality_fn(elem_typ) eq_fn := g.gen_struct_equality_fn(elem_typ)
fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(a[i], b[i])) {') 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 { .string {
fn_builder.writeln('\t\tif (!fast_string_eq(*(string*)map_get_1(&b, k, &(string[]){_SLIT("")}), v)) {') 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_ { .struct_ {
eq_fn := g.gen_struct_equality_fn(value_typ) 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)) {') 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) {') fn_builder.writeln('\t\tif (*(voidptr*)map_get_1(&b, k, &(voidptr[]){ 0 }) != v) {')
} }
else { else {
println(kind)
fn_builder.writeln('\t\tif (*($ptr_value_typ*)map_get_1(&b, k, &($ptr_value_typ[]){ 0 }) != v) {') fn_builder.writeln('\t\tif (*($ptr_value_typ*)map_get_1(&b, k, &($ptr_value_typ[]){ 0 }) != v) {')
} }
} }

View File

@ -32,95 +32,96 @@ struct Gen {
pref &pref.Preferences pref &pref.Preferences
module_built string module_built string
mut: mut:
table &table.Table table &table.Table
out strings.Builder out strings.Builder
cheaders strings.Builder cheaders strings.Builder
includes strings.Builder // all C #includes required by V modules includes strings.Builder // all C #includes required by V modules
typedefs strings.Builder typedefs strings.Builder
typedefs2 strings.Builder typedefs2 strings.Builder
type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) 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) 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{}` inits map[string]strings.Builder // contents of `void _vinit/2{}`
cleanups map[string]strings.Builder // contents of `void _vcleanup(){}` cleanups map[string]strings.Builder // contents of `void _vcleanup(){}`
gowrappers strings.Builder // all go callsite wrappers gowrappers strings.Builder // all go callsite wrappers
stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined 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 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 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 pcs_declarations strings.Builder // -prof profile counter declarations for each function
hotcode_definitions strings.Builder // -live declarations & functions hotcode_definitions strings.Builder // -live declarations & functions
embedded_data strings.Builder // data to embed in the executable/binary embedded_data strings.Builder // data to embed in the executable/binary
shared_types strings.Builder // shared/lock types shared_types strings.Builder // shared/lock types
shared_functions strings.Builder // shared constructors shared_functions strings.Builder // shared constructors
channel_definitions strings.Builder // channel related code channel_definitions strings.Builder // channel related code
options_typedefs strings.Builder // Option typedefs options_typedefs strings.Builder // Option typedefs
options strings.Builder // `Option_xxxx` types options strings.Builder // `Option_xxxx` types
json_forward_decls strings.Builder // json type forward decls json_forward_decls strings.Builder // json type forward decls
enum_typedefs strings.Builder // enum types enum_typedefs strings.Builder // enum types
sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
file ast.File file ast.File
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
last_fn_c_name string last_fn_c_name string
tmp_count int // counter for unique tmp vars (_tmp1, tmp2 etc) tmp_count int // counter for unique tmp vars (_tmp1, tmp2 etc)
tmp_count2 int // a separate tmp var counter for autofree fn calls tmp_count2 int // a separate tmp var counter for autofree fn calls
is_c_call bool // e.g. `C.printf("v")` is_c_call bool // e.g. `C.printf("v")`
is_assign_lhs bool // inside left part of assign expr (for array_set(), etc) 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 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_void_expr_stmt bool // ExprStmt whos result is discarded
is_arraymap_set bool // map or array set value state 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_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_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_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 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 arraymap_set_pos int // map or array set value position
vlines_path string // set to the proper path for generating #line directives vlines_path string // set to the proper path for generating #line directives
optionals []string // to avoid duplicates TODO perf, use map optionals []string // to avoid duplicates TODO perf, use map
chan_pop_optionals []string // types for `x := <-ch or {...}` chan_pop_optionals []string // types for `x := <-ch or {...}`
chan_push_optionals []string // types for `ch <- x or {...}` chan_push_optionals []string // types for `ch <- x or {...}`
shareds []int // types with hidden mutex for which decl has been emitted shareds []int // types with hidden mutex for which decl has been emitted
inside_ternary int // ?: comma separated statements on a single line inside_ternary int // ?: comma separated statements on a single line
inside_map_postfix bool // inside map++/-- postfix expr inside_map_postfix bool // inside map++/-- postfix expr
inside_map_infix bool // inside map<</+=/-= infix expr inside_map_infix bool // inside map<</+=/-= infix expr
inside_map_index bool inside_map_index bool
inside_opt_data bool inside_opt_data bool
inside_if_optional bool inside_if_optional bool
ternary_names map[string]string ternary_names map[string]string
ternary_level_names map[string][]string ternary_level_names map[string][]string
stmt_path_pos []int // positions of each statement start, for inserting C statements before the current statement stmt_path_pos []int // positions of each statement start, for inserting C statements before the current statement
skip_stmt_pos bool // for handling if expressions + autofree (since both prepend C statements) skip_stmt_pos bool // for handling if expressions + autofree (since both prepend C statements)
right_is_opt bool right_is_opt bool
is_autofree bool // false, inside the bodies of fns marked with [manualfree], otherwise === g.pref.autofree is_autofree bool // false, inside the bodies of fns marked with [manualfree], otherwise === g.pref.autofree
indent int indent int
empty_line bool empty_line bool
is_test bool is_test bool
assign_op token.Kind // *=, =, etc (for array_set) assign_op token.Kind // *=, =, etc (for array_set)
defer_stmts []ast.DeferStmt defer_stmts []ast.DeferStmt
defer_ifdef string defer_ifdef string
defer_profile_code string defer_profile_code string
str_types []string // types that need automatic str() generation str_types []string // types that need automatic str() generation
threaded_fns []string // for generating unique wrapper types and fns for `go xxx()` threaded_fns []string // for generating unique wrapper types and fns for `go xxx()`
waiter_fns []string // functions that wait for `go xxx()` to finish waiter_fns []string // functions that wait for `go xxx()` to finish
array_fn_definitions []string // array equality functions that have been defined array_fn_definitions []string // array equality functions that have been defined
map_fn_definitions []string // map equality functions that have been defined map_fn_definitions []string // map equality functions that have been defined
struct_fn_definitions []string // struct equality functions that have been defined struct_fn_definitions []string // struct equality functions that have been defined
alias_fn_definitions []string // alias equality functions that have been defined sumtype_fn_definitions []string // sumtype equality functions that have been defined
auto_fn_definitions []string // auto generated functions defination list alias_fn_definitions []string // alias equality functions that have been defined
anon_fn_definitions []string // anon generated functions defination list auto_fn_definitions []string // auto generated functions defination list
sumtype_definitions map[int]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated anon_fn_definitions []string // anon generated functions defination list
is_json_fn bool // inside json.encode() sumtype_definitions map[int]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated
json_types []string // to avoid json gen duplicates is_json_fn bool // inside json.encode()
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name json_types []string // to avoid json gen duplicates
is_builtin_mod bool pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
hotcode_fn_names []string is_builtin_mod bool
embedded_files []ast.EmbeddedFile hotcode_fn_names []string
cur_fn ast.FnDecl embedded_files []ast.EmbeddedFile
cur_generic_types []table.Type // `int`, `string`, etc in `foo<T>()` cur_fn ast.FnDecl
sql_i int cur_generic_types []table.Type // `int`, `string`, etc in `foo<T>()`
sql_stmt_name string sql_i int
sql_side SqlExprSide // left or right, to distinguish idents in `name == name` sql_stmt_name string
inside_vweb_tmpl bool sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
inside_return bool inside_vweb_tmpl bool
inside_or_block bool inside_return bool
strs_to_free0 []string // strings.Builder inside_or_block bool
strs_to_free0 []string // strings.Builder
// strs_to_free []string // strings.Builder // strs_to_free []string // strings.Builder
inside_call bool inside_call bool
has_main bool has_main bool
@ -3518,6 +3519,23 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
} }
g.expr(node.right) g.expr(node.right)
g.write(')') 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 { } else if op_is_key_in_or_not_in {
if node.op == .not_in { if node.op == .not_in {
g.write('!') g.write('!')

View File

@ -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 { pub fn (t TypeSymbol) str() string {
return t.name return t.name

View File

@ -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
}