checker, cgen: allow | between bitfield enum values, autogenerate a more specific .str method for them too (#8856)
parent
cc565b22a9
commit
f67a4c3ee0
|
@ -960,8 +960,19 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
|
||||||
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_ && infix_expr.op !in [.ne, .eq] {
|
||||||
c.error('only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed',
|
left_enum := left.info as table.Enum
|
||||||
infix_expr.pos)
|
right_enum := right.info as table.Enum
|
||||||
|
if left_enum.is_flag && right_enum.is_flag {
|
||||||
|
// `[flag]` tagged enums are a special case that allow also `|` and `&` binary operators
|
||||||
|
if infix_expr.op !in [.pipe, .amp] {
|
||||||
|
c.error('only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed',
|
||||||
|
infix_expr.pos)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Regular enums
|
||||||
|
c.error('only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed',
|
||||||
|
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", is is checked before and doesn't reach this
|
||||||
if c.table.type_kind(left_type) == .sum_type {
|
if c.table.type_kind(left_type) == .sum_type {
|
||||||
|
|
|
@ -1,13 +1,34 @@
|
||||||
vlib/v/checker/tests/enum_op_err.vv:8:20: error: only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed
|
vlib/v/checker/tests/enum_op_err.vv:8:20: error: only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed
|
||||||
6 |
|
6 |
|
||||||
7 | fn main() {
|
7 | fn main() {
|
||||||
8 | println(Color.red > Color.green)
|
8 | println(Color.red > Color.green)
|
||||||
| ^
|
| ^
|
||||||
9 | println(Color.red + Color.green)
|
9 | println(Color.red + Color.green)
|
||||||
10 | }
|
10 | println(Color.red && Color.green)
|
||||||
vlib/v/checker/tests/enum_op_err.vv:9:20: error: only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed
|
vlib/v/checker/tests/enum_op_err.vv:9:20: error: only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed
|
||||||
7 | fn main() {
|
7 | fn main() {
|
||||||
8 | println(Color.red > Color.green)
|
8 | println(Color.red > Color.green)
|
||||||
9 | println(Color.red + Color.green)
|
9 | println(Color.red + Color.green)
|
||||||
| ^
|
| ^
|
||||||
10 | }
|
10 | println(Color.red && Color.green)
|
||||||
|
11 | println(Color.red | Color.green)
|
||||||
|
vlib/v/checker/tests/enum_op_err.vv:10:20: error: left operand for `&&` is not a boolean
|
||||||
|
8 | println(Color.red > Color.green)
|
||||||
|
9 | println(Color.red + Color.green)
|
||||||
|
10 | println(Color.red && Color.green)
|
||||||
|
| ~~
|
||||||
|
11 | println(Color.red | Color.green)
|
||||||
|
12 | println(Color.red & Color.green)
|
||||||
|
vlib/v/checker/tests/enum_op_err.vv:11:20: error: only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed
|
||||||
|
9 | println(Color.red + Color.green)
|
||||||
|
10 | println(Color.red && Color.green)
|
||||||
|
11 | println(Color.red | Color.green)
|
||||||
|
| ^
|
||||||
|
12 | println(Color.red & Color.green)
|
||||||
|
13 | }
|
||||||
|
vlib/v/checker/tests/enum_op_err.vv:12:20: error: only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed
|
||||||
|
10 | println(Color.red && Color.green)
|
||||||
|
11 | println(Color.red | Color.green)
|
||||||
|
12 | println(Color.red & Color.green)
|
||||||
|
| ^
|
||||||
|
13 | }
|
||||||
|
|
|
@ -7,4 +7,7 @@ enum Color {
|
||||||
fn main() {
|
fn main() {
|
||||||
println(Color.red > Color.green)
|
println(Color.red > Color.green)
|
||||||
println(Color.red + Color.green)
|
println(Color.red + Color.green)
|
||||||
|
println(Color.red && Color.green)
|
||||||
|
println(Color.red | Color.green)
|
||||||
|
println(Color.red & Color.green)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
vlib/v/checker/tests/enum_op_flag_err.vv:9:24: error: only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed
|
||||||
|
7 |
|
||||||
|
8 | fn main() {
|
||||||
|
9 | println(FilePerm.read > FilePerm.write)
|
||||||
|
| ^
|
||||||
|
10 | println(FilePerm.write + FilePerm.exec)
|
||||||
|
11 | println(FilePerm.write && FilePerm.exec)
|
||||||
|
vlib/v/checker/tests/enum_op_flag_err.vv:10:25: error: only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed
|
||||||
|
8 | fn main() {
|
||||||
|
9 | println(FilePerm.read > FilePerm.write)
|
||||||
|
10 | println(FilePerm.write + FilePerm.exec)
|
||||||
|
| ^
|
||||||
|
11 | println(FilePerm.write && FilePerm.exec)
|
||||||
|
12 | }
|
||||||
|
vlib/v/checker/tests/enum_op_flag_err.vv:11:25: error: left operand for `&&` is not a boolean
|
||||||
|
9 | println(FilePerm.read > FilePerm.write)
|
||||||
|
10 | println(FilePerm.write + FilePerm.exec)
|
||||||
|
11 | println(FilePerm.write && FilePerm.exec)
|
||||||
|
| ~~
|
||||||
|
12 | }
|
|
@ -0,0 +1,12 @@
|
||||||
|
[flag]
|
||||||
|
enum FilePerm {
|
||||||
|
read
|
||||||
|
write
|
||||||
|
exec
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println(FilePerm.read > FilePerm.write)
|
||||||
|
println(FilePerm.write + FilePerm.exec)
|
||||||
|
println(FilePerm.write && FilePerm.exec)
|
||||||
|
}
|
|
@ -573,19 +573,31 @@ fn (mut g Gen) gen_str_for_enum(info table.Enum, styp string, str_fn_name string
|
||||||
s := util.no_dots(styp)
|
s := util.no_dots(styp)
|
||||||
g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto')
|
g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto')
|
||||||
g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { /* gen_str_for_enum */')
|
g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { /* gen_str_for_enum */')
|
||||||
g.auto_str_funcs.writeln('\tswitch(it) {')
|
// Enums tagged with `[flag]` are special in that they can be a combination of enum values
|
||||||
// Only use the first multi value on the lookup
|
if info.is_flag {
|
||||||
mut seen := []string{len: info.vals.len}
|
clean_name := util.strip_main_name(styp.replace('__', '.'))
|
||||||
for val in info.vals {
|
g.auto_str_funcs.writeln('\tstring ret = _SLIT("$clean_name{");')
|
||||||
if info.is_multi_allowed && val in seen {
|
g.auto_str_funcs.writeln('\tint first = 1;')
|
||||||
continue
|
for i, val in info.vals {
|
||||||
} else if info.is_multi_allowed {
|
g.auto_str_funcs.writeln('\tif (it & (1 << $i)) {if (!first) {ret = string_add(ret, _SLIT(" | "));} ret = string_add(ret, _SLIT(".$val")); first = 0;}')
|
||||||
seen << val
|
|
||||||
}
|
}
|
||||||
g.auto_str_funcs.writeln('\t\tcase ${s}_$val: return _SLIT("$val");')
|
g.auto_str_funcs.writeln('\tret = string_add(ret, _SLIT("}"));')
|
||||||
|
g.auto_str_funcs.writeln('\treturn ret;')
|
||||||
|
} else {
|
||||||
|
g.auto_str_funcs.writeln('\tswitch(it) {')
|
||||||
|
// Only use the first multi value on the lookup
|
||||||
|
mut seen := []string{len: info.vals.len}
|
||||||
|
for val in info.vals {
|
||||||
|
if info.is_multi_allowed && val in seen {
|
||||||
|
continue
|
||||||
|
} else if info.is_multi_allowed {
|
||||||
|
seen << val
|
||||||
|
}
|
||||||
|
g.auto_str_funcs.writeln('\t\tcase ${s}_$val: return _SLIT("$val");')
|
||||||
|
}
|
||||||
|
g.auto_str_funcs.writeln('\t\tdefault: return _SLIT("unknown enum value");')
|
||||||
|
g.auto_str_funcs.writeln('\t}')
|
||||||
}
|
}
|
||||||
g.auto_str_funcs.writeln('\t\tdefault: return _SLIT("unknown enum value");')
|
|
||||||
g.auto_str_funcs.writeln('\t}')
|
|
||||||
g.auto_str_funcs.writeln('}')
|
g.auto_str_funcs.writeln('}')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ fn test_enum_bitfield() {
|
||||||
assert a.perm.has(.execute)
|
assert a.perm.has(.execute)
|
||||||
assert !a.perm.has(.write)
|
assert !a.perm.has(.write)
|
||||||
assert !a.perm.has(.other)
|
assert !a.perm.has(.other)
|
||||||
mut b := BfPermission(int(BfPermission.read) | int(BfPermission.execute))
|
mut b := BfPermission.read | BfPermission.execute
|
||||||
assert int(b) == 1 + 0 + 4 + 0
|
assert int(b) == 1 + 0 + 4 + 0
|
||||||
assert b.has(.read)
|
assert b.has(.read)
|
||||||
assert b.has(.execute)
|
assert b.has(.execute)
|
||||||
|
@ -42,4 +42,11 @@ fn test_enum_bitfield() {
|
||||||
assert int(b) == 0 + 2 + 0 + 8
|
assert int(b) == 0 + 2 + 0 + 8
|
||||||
assert !b.has(.read)
|
assert !b.has(.read)
|
||||||
assert !b.has(.execute)
|
assert !b.has(.execute)
|
||||||
|
|
||||||
|
mut c := BfPermission.read
|
||||||
|
c.set(.write | .execute)
|
||||||
|
assert c.has(.read | .write | .execute)
|
||||||
|
assert !c.has(.other)
|
||||||
|
c.toggle(.write | .other)
|
||||||
|
assert '$c' == 'BfPermission{.read | .execute | .other}'
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue