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 `+`',
|
||||
infix_expr.pos)
|
||||
} 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',
|
||||
infix_expr.pos)
|
||||
left_enum := left.info as table.Enum
|
||||
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
|
||||
if c.table.type_kind(left_type) == .sum_type {
|
||||
|
|
|
@ -4,10 +4,31 @@ vlib/v/checker/tests/enum_op_err.vv:8:20: error: only `==` and `!=` are defined
|
|||
8 | 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
|
||||
7 | fn main() {
|
||||
8 | 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() {
|
||||
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)
|
||||
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('\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
|
||||
// Enums tagged with `[flag]` are special in that they can be a combination of enum values
|
||||
if info.is_flag {
|
||||
clean_name := util.strip_main_name(styp.replace('__', '.'))
|
||||
g.auto_str_funcs.writeln('\tstring ret = _SLIT("$clean_name{");')
|
||||
g.auto_str_funcs.writeln('\tint first = 1;')
|
||||
for i, val in info.vals {
|
||||
g.auto_str_funcs.writeln('\tif (it & (1 << $i)) {if (!first) {ret = string_add(ret, _SLIT(" | "));} ret = string_add(ret, _SLIT(".$val")); first = 0;}')
|
||||
}
|
||||
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('}')
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ fn test_enum_bitfield() {
|
|||
assert a.perm.has(.execute)
|
||||
assert !a.perm.has(.write)
|
||||
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 b.has(.read)
|
||||
assert b.has(.execute)
|
||||
|
@ -42,4 +42,11 @@ fn test_enum_bitfield() {
|
|||
assert int(b) == 0 + 2 + 0 + 8
|
||||
assert !b.has(.read)
|
||||
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