checker: support Flags.bit ^ Flags.bit1 and ~Flags.bit (flagged enums) (fix #20925) (#20929)

This commit is contained in:
Swastik Baranwal 2024-03-01 18:00:55 +05:30 committed by GitHub
parent a2d23306c2
commit cc443e50bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 70 additions and 32 deletions

View file

@ -4317,8 +4317,14 @@ fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) ast.Type {
}
}
}
if node.op == .bit_not && !right_sym.is_int() && !c.pref.translated && !c.file.is_translated {
c.type_error_for_operator('~', 'integer', right_sym.name, node.pos)
if node.op == .bit_not && !c.pref.translated && !c.file.is_translated {
if right_sym.info is ast.Enum && !right_sym.info.is_flag {
c.error('operator `~` can only be used with `@[flag]` tagged enums', node.pos)
}
// Only check for int not enum as it is done above
if !right_sym.is_int() && right_sym.info !is ast.Enum {
c.type_error_for_operator('~', 'integer', right_sym.name, node.pos)
}
}
if node.op == .not && right_sym.kind != .bool && !c.pref.translated && !c.file.is_translated {
c.type_error_for_operator('!', 'bool', right_sym.name, node.pos)

View file

@ -750,9 +750,9 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
left_enum := left_sym.info as ast.Enum
right_enum := right_sym.info as ast.Enum
if left_enum.is_flag && right_enum.is_flag {
// `[flag]` tagged enums are a special case that allow also `|` and `&` binary operators
if node.op !in [.pipe, .amp] {
c.error('only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed',
// `@[flag]` tagged enums are a special case that allow also `|` and `&` binary operators
if node.op !in [.pipe, .amp, .xor, .bit_not] {
c.error('only `==`, `!=`, `|`, `&`, `^` and `~` are defined on `@[flag]` tagged `enum`, use an explicit cast to `int` if needed',
node.pos)
}
} else if !c.pref.translated && !c.file.is_translated && !left_type.has_flag(.generic)

View file

@ -1,32 +1,48 @@
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)
vlib/v/checker/tests/enum_op_flag_err.vv:16:24: error: only `==`, `!=`, `|`, `&`, `^` and `~` are defined on `@[flag]` tagged `enum`, use an explicit cast to `int` if needed
14 |
15 | fn main() {
16 | 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)
17 | println(FilePerm.write + FilePerm.exec)
18 | println(FilePerm.write && FilePerm.exec)
vlib/v/checker/tests/enum_op_flag_err.vv:17:25: error: only `==`, `!=`, `|`, `&`, `^` and `~` are defined on `@[flag]` tagged `enum`, use an explicit cast to `int` if needed
15 | fn main() {
16 | println(FilePerm.read > FilePerm.write)
17 | println(FilePerm.write + FilePerm.exec)
| ^
11 | println(FilePerm.write && FilePerm.exec)
12 | }
vlib/v/checker/tests/enum_op_flag_err.vv:11:10: 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)
18 | println(FilePerm.write && FilePerm.exec)
19 |
vlib/v/checker/tests/enum_op_flag_err.vv:18:10: error: left operand for `&&` is not a boolean
16 | println(FilePerm.read > FilePerm.write)
17 | println(FilePerm.write + FilePerm.exec)
18 | println(FilePerm.write && FilePerm.exec)
| ~~~~~~~~~~~~~~
12 | }
vlib/v/checker/tests/enum_op_flag_err.vv:11:28: error: right operand for `&&` is not a boolean
9 | println(FilePerm.read > FilePerm.write)
10 | println(FilePerm.write + FilePerm.exec)
11 | println(FilePerm.write && FilePerm.exec)
19 |
20 | f := Flags.bit0 | Flags.bit1
vlib/v/checker/tests/enum_op_flag_err.vv:18:28: error: right operand for `&&` is not a boolean
16 | println(FilePerm.read > FilePerm.write)
17 | println(FilePerm.write + FilePerm.exec)
18 | println(FilePerm.write && FilePerm.exec)
| ~~~~~~~~~~~~~
12 | }
vlib/v/checker/tests/enum_op_flag_err.vv:11:25: error: only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed
9 | println(FilePerm.read > FilePerm.write)
10 | println(FilePerm.write + FilePerm.exec)
11 | println(FilePerm.write && FilePerm.exec)
19 |
20 | f := Flags.bit0 | Flags.bit1
vlib/v/checker/tests/enum_op_flag_err.vv:18:25: error: only `==`, `!=`, `|`, `&`, `^` and `~` are defined on `@[flag]` tagged `enum`, use an explicit cast to `int` if needed
16 | println(FilePerm.read > FilePerm.write)
17 | println(FilePerm.write + FilePerm.exec)
18 | println(FilePerm.write && FilePerm.exec)
| ~~
12 | }
19 |
20 | f := Flags.bit0 | Flags.bit1
vlib/v/checker/tests/enum_op_flag_err.vv:20:18: error: only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed
18 | println(FilePerm.write && FilePerm.exec)
19 |
20 | f := Flags.bit0 | Flags.bit1
| ^
21 | println(~f)
22 | }
vlib/v/checker/tests/enum_op_flag_err.vv:21:10: error: operator `~` can only be used with `@[flag]` tagged enums
19 |
20 | f := Flags.bit0 | Flags.bit1
21 | println(~f)
| ^
22 | }

View file

@ -5,8 +5,18 @@ enum FilePerm {
exec
}
enum Flags {
bit0
bit1
bit2
bit3
}
fn main() {
println(FilePerm.read > FilePerm.write)
println(FilePerm.write + FilePerm.exec)
println(FilePerm.write && FilePerm.exec)
f := Flags.bit0 | Flags.bit1
println(~f)
}

View file

@ -15,10 +15,16 @@ const abc = Foo.a | Foo.b | Foo.c
const abc2 = Foo.a | .b | .c
const abc3 = Foo.a ^ .b
const abc4 = ~Foo.a
fn test_main() {
assert dump(a) == Foo.a
assert dump(ab) == Foo.a | Foo.b
assert dump(ab2) == Foo.a | .b
assert dump(abc) == Foo.a | Foo.b | Foo.c
assert dump(abc2) == Foo.a | .b | .c
assert dump(abc3) == Foo.a ^ .b
assert dump(abc4) == ~Foo.a
}